lispcommon-lispliteralson-lisp

Bizarre quoted list example from On Lisp


This passage from On Lisp is genuinely confusing -- it is not clear how returning a quoted list such as '(oh my) can actually alter how the function behaves in the future: won't the returned list be generated again in the function from scratch, the next time it is called?

If we define exclaim so that its return value incorporates a quoted list,

(defun exclaim (expression) 
  (append expression ’(oh my)))

Then any later destructive modification of the return value

(exclaim ’(lions and tigers and bears)) 
->  (LIONS AND TIGERS AND BEARS OH MY)
(nconc * ’(goodness))
->  (LIONS AND TIGERS AND BEARS OH MY GOODNESS)

could alter the list within the function:

(exclaim ’(fixnums and bignums and floats)) 
->  (FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS)

To make exclaim proof against such problems, it should be written:

(defun exclaim (expression)
  (append expression (list ’oh ’my)))

How exactly is that last call to exclaim adding the word goodness to the result? The function is not referencing any outside variable so how did the separate call to nconc actually alter how the exclaim function works?


Solution

  • a) the effects of modifying literal lists is undefined in the Common Lisp standard. What you here see as an example is one possible behavior.

    (1 2 3 4) is a literal list. But a call to LIST like in (list 1 2 3 4) returns a freshly consed list at runtime.

    b) the list is literal data in the code of the function. Every call will return exactly this data object. If you want to provide a fresh list on each call, then you need to use something like LIST or COPY-LIST.

    c) Since the returned list is always the same literal data object, modifying it CAN have this effect as described. One could imagine also that an error happens if the code and its objects are allocated in a read-only memory. Modifying the list then would try to write to read-only memory.

    d) One thing to keep in mind when working with literal list data in source code is this: the Lisp compiler is free to optimize the storage. If a list happens to be multiple times in the source code, a compiler is allowed to detect this and to only create ONE list. All the various places would then point to this one list. Thus modifying the list would have the effect, that these changes could be visible in several places.

    This may also happen with other literal data objects like arrays/vectors.

    If your data structure is a part of the code, you return this internal data structure, you modify this data structure - then you try to modify your code.

    Note also that Lisp can be executed by an Interpreter. The interpreter typically works on the Lisp source structure - the code is not machine code, but interpreted Lisp code as Lisp data. Here you might be able to modify the source code at runtime, not only the data embedded in the source code.