views:

103

answers:

3

I'm working my way through Graham's book "On Lisp" and can't understand the following example at page 37:

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

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

>  (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)))

Does anyone understand what's going on here? This is seriously screwing with my mental model of what quoting does.

+1  A: 

nconc is a destructive operation that alters its first argument by changing its tail. In this case, it means that the constant list '(oh my) gets a new tail.

To hopefully make this clearer. It's a bit like this:

; Hidden variable inside exclaim
oh_my = oh → my → nil

(exclaim '(lions and tigers and bears)) =
    lions → and → tigers → and → bears → oh_my

(nconc * '(goodness)) destructively appends goodness to the last result:
    lions → and → tigers → and → bears → oh → my → goodness → nil
so now, oh_my = oh → my → goodness → nil

Replacing '(oh my) with (list 'oh 'my) fixes this because there is no longer a constant being shared by all and sundry. Each call to exclaim generates a new list (the list function's purpose in life is to create brand new lists).

Marcelo Cantos
So, then (defun exclaim (expression)(append expression (list ’oh ’(oh my))))would theoretically have the same problems, right?
mazemaster225
@mazemaster225: No, see the latest version of my answer.
Marcelo Cantos
Would it be fair to say that the action of (list 'oh '(oh my)) "flattens" the symbol oh_my into it's individual components 'oh and 'my?
mazemaster225
Sorry, I didn't notice that you wrote a slightly different list. Your version will still have no problem, since the `'(oh my)` is treated as the last element within the list, not as its tail. It's like this: `oh → (oh → my → nil) → nil`. No flattening would occur. Of course, if you accessed this last item via, e.g., `cadr` and passed it to `nconc`, you would see the same issue.
Marcelo Cantos
Ok, thanks for the explanation.
mazemaster225
+2  A: 

In Common Lisp.

Remember:

'(1 2 3 4)

Above is a literal list. Constant data.

(list 1 2 3 4)

LIST is a function that when call returns a fresh new list with its arguments as list elements.

Avoid modifying literal lists. The effects are not standardized. Imagine a Lisp that compiles all constant data into a write only memory area. Imagine a Lisp that takes constant lists and shares them across functions.

(defun a () '(1 2 3)

(defun b () '(1 2 3))

A Lisp compiler may create one list that is shared by both functions.

If you modify the list returned by function a

  • it might not be changed
  • it might be changed
  • it might be an error
  • it might also change the list returned by function b

Implementations have the freedom to do what they like. This leaves room for optimizations.

Rainer Joswig
In what sort of situations would it be preferable to use '(1 2 3 4) instead of (list 1 2 3 4)?
mazemaster225
+1  A: 
Matthias Benkard