Bizarre quoted list example from On Lisp
Asked Answered
Z

1

13

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?

Zucker answered 11/4, 2014 at 2:7 Comment(2)
I see you've got an answer, but take a look at this answer for some additional discussion and resourcesKaylil
Also, this is probably a duplicate of Strange Lisp Quoting scenario - Graham's On Lisp, page 37.Kaylil
R
14

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.

Recollect answered 11/4, 2014 at 2:27 Comment(4)
so you are saying that the '(oh my) lives on after the function returns, lives on as an actual piece of persistent data, and that the nconc is mutating that data and making it '(oh my goodness) so that when exclaim runs again, it is using a new value in that data?Zucker
Yes. exclaim itself does not mutate anything, that is done by nconc. exclaim merely makes the new contents of the altered quoted list visible.Commandeer
@Rainer: Is (a) true for all lists or just literal lists?Percolator
@Baggers: only literal lists. I changed the wording to make it clear - without the context. Thanks!Recollect

© 2022 - 2024 — McMap. All rights reserved.