What @phils said in a comment:
"Remember that the value of x is a cons cell."
The cons cell x
is passed to list
, and then it is reversed when list
's second arg is evaluated. Just as you expected.
The result of list
is a list of the cons cell x
(which has been changed) and the result of (nreverse x)
.
Try this, and look at the message:
(let ((x '(1 2 3)))
(list (progn
(message "X: %S" x) (sleep-for 1)
x)
(nreverse x)))
And BTW (for @Tobias, for example), your example has nothing to do with the result of modifying a quoted list being "undefined".
And it's not really undefined, though Common Lisp, which is a spec for any number of implementations, says it is undefined in the spec, because you cannot depend on any particular behavior about it from all implementations. And even Emacs Lisp would not want to tie its implementation hands by specifying any particular behavior for this that the designers would then have to try to maintain (for interpreter, byte-compiler, reader, etc.).
The point about not modifying a "constant" (and it's not a constant), that is, a quoted cons, is this: Do not expect that the sexp '(some list)
gets re-read or re-evaluated each time, if you use it in code that is evaluated more than once. Do not expect that a new list (some list)
gets created each time.
An implementation is free to create a new list (new conses) each time, or some times, or to just reuse the same list that was created during the first read/evaluation.
This is a typical gotcha. Someone writes (let ((x '(1 2 3)))...) in some context, then modifies that list structure, but expects that each time the context is used she gets a new list (new conses) bound to local variable x
. Not necessarily.
The confusion comes from thinking of the program as the program text. The executing program works with things like list structure (conses), that are live. The program can modify some list structure whose apparent representation in the textual program seems to be constant/static.
x
is a cons cell.(nreverse x)
destroys the list referenced byx
. You actually have a more subtile error in your code fragments. With(let ((x '(1 2 3))) ...)
you givex
the constant value(1 2 3)
but afterwards you manipulate that value. That leads to undefined behavior which is only by accident what you expect. The correct way is(let ((x (list 1 2 3))) ...)
. I had to learn it the hard way. Therefore I am telling you that.nreverse
says(setq x '(a b c)) (nreverse x)
, should it be changed to(list 'a 'b 'c)
too?