2

While evaluating a function call, the arguments is from left to right. For example,

(let ((x '(1 2 3)))
  (list (nreverse x) x))
;; => ((3 2 1) (1))

The first argument (nreverse x) evaluates to (3 2 1), as a side effect, x is changed to (1), then the second argument x evaluates to (1). So the result is expected. However,

(let ((x '(1 2 3)))
  (list x (nreverse x)))
;; => ((1) (3 2 1))

The result is more or less surprising for me. Because the first arguments x evaluates (1 2 3), then the seconds arguments (nreverse x) evaluates to (3 2 1). The Edebug also follows the flow, thus one might get wrong assumption.

Drew
  • 75,699
  • 9
  • 109
  • 225
xuchunyang
  • 14,302
  • 1
  • 18
  • 39
  • Remember that the value of `x` is a cons cell. – phils Nov 07 '18 at 10:40
  • The function arguments are evaluated before the function and the list value of the symbol is more like a pointer in other languages. Therefore `(nreverse x)` destroys the list referenced by `x`. You actually have a more subtile error in your code fragments. With `(let ((x '(1 2 3))) ...)` you give `x` 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. – Tobias Nov 07 '18 at 10:41
  • @Tobias Unfortunately, I don't have much experience with other language which has pointer. About *constant value*, the Elisp [manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequence-Functions.html) of `nreverse ` says `(setq x '(a b c)) (nreverse x)`, should it be changed to `(list 'a 'b 'c)` too? – xuchunyang Nov 07 '18 at 12:00
  • Good catch. I think that is really an error in the manual. See ["The consequences are undefined if literal objects (including quoted objects) are destructively modified." at the Common Lisp Hyper Spec](http://clhs.lisp.se/Body/s_quote.htm) and [that answer](https://stackoverflow.com/a/578365/2708138) on SO. I believe lisp behaves the same as CL in that aspect. There was already a discussion about that here on SE. [Constant lists are also special in Elisp.](ftp://ftp.gnu.org/pub/old-gnu/Manuals/elisp-manual-20-2.5/html_chapter/elisp_6.html#SEC89) They modify many constant lists in the manual! – Tobias Nov 07 '18 at 13:07
  • ... Since the Hyper Spec only says something about CL, there is not really a Spec for Elisp, and I am not an Emacs programmer but only a user of Elisp. I don't really know. It's a good question for SO. – Tobias Nov 07 '18 at 13:12
  • I have posted [a question about modifying quoted lists](https://emacs.stackexchange.com/questions/45820/when-to-use-quote-for-lists) here on SE. – Tobias Nov 07 '18 at 13:38

2 Answers2

2

That's because (list x) doesn't create a copy of x, so if you change x later, the change is reflected in the list:

(let* ((x '(1 2 3))
       (y (list x)))
  (list x y (nreverse x)))
=> ((1) ((1)) (3 2 1))
choroba
  • 1,925
  • 10
  • 16
2

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.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • The user [xanthir](https://stackoverflow.com/users/58347/xanthir) states [in his answer at stackoverflow](https://stackoverflow.com/questions/134887/when-to-use-or-quote-in-lisp/578365#578365) that quoted lists may even be used in the sense of [common subexpressions](https://en.wikipedia.org/wiki/Common_subexpression_elimination) by the byte compiler. That way completely separate functions may reference the same constant list. If the developers of Emacs Lisp leave quoted lists unspecified anything may happen and one should use quoted lists only if one really intents to keep them constant. – Tobias Nov 07 '18 at 15:24
  • @tobias: Agreed. Don't count on what looks like a quoted, literal list in source code being a constant. And don't count on a new list being created and returned by such source code each time it is invoked. There are a Lisp reader, an interpreter, and a byte-compiler, each of which can create (or not) new list structure (conses). – Drew Nov 07 '18 at 15:52