5

GNU Emacs Lisp Reference Manual, section 2.9 Mutability:

A mutable object stops being mutable if it is part of an expression that is evaluated. For example:

(let* ((x (list 0.5))
       (y (eval (list 'quote x))))
  (setcar x 1.5) ;; The program should not do this.
  y)

Although the list (0.5) was mutable when it was created, it should not have been changed via setcar because it was given to eval. The reverse does not occur: an object that should not be changed never becomes mutable afterwards.

Then I try this example in *ielm* to see what will happen:

ELISP> (let* ((x (list 0.5))
              (y (eval (list 'quote x))))
         (setcar x 1.5)
         y)
(1.5)

ELISP>

It worked fine. So, what is the reason that the manual says that?
(Actually, I want to ask why the eval here in the example will change x's mutability.)

shynur
  • 4,065
  • 1
  • 3
  • 23

2 Answers2

5

The key to understanding this sentence in the manual, is the next sentence in the manual, and its footnote:

If a program attempts to change objects that should not be changed, the resulting behavior is undefined: the Lisp interpreter might signal an error, or it might crash or behave unpredictably in other ways. [Footnote: This is the behavior specified for languages like Common Lisp and C for constants...]

What this means is, your test code isn't guaranteed to do the thing you observed it to do. Plausible other behaviors include:

  • the overall expression evaluates to (0.5), because (eval (list 'quote x)) evaluated to a copy of the list in x (same as (list x) would have)
  • the setcar throws an error
  • the overall expression evaluates to nonsense (I find it plausible that you would get nil, for instance)
  • Emacs crashes

And, what happens could change without your having changed anything about your code. The actual behavior could depend on things like the exact version of Emacs in use, apparently irrelevant details of code nearby in the same file, whether or not the construct has been byte-compiled, whether the garbage collector happens to be invoked in the middle of the evaluation and how aggressive a collection it does, ...

That's why the manual says not to do this.

Drew
  • 75,699
  • 9
  • 109
  • 225
zwol
  • 272
  • 1
  • 8
  • 1
    thanks. i know that i should not change objects that should not be changed (ie, immutable object). but i can't understand **why the *manual* says `eval` will change an object's mutability** because it also says "we set the value of a symbol with setq. Then we evaluate the symbol, and get back the value that setq stored." ([10.2.2](https://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Forms.html)) – shynur Feb 13 '23 at 17:00
  • 1
    @Shynur: It's because evaluating `(list 'quote x)` with `x` being the list `(0.5)` evaluates to the list `(quote (0.5))`, and evaluating that list gives the *"constant"* list `(0.5)`. That is, that list could be a "constant", i.e., considered "immutable". Those terms really mean that the list structure is likely something you don't *want* to modify, because the result is, as zwol said, undefined. This is just a roundabout case of creating a list by quoting it, e.g., `'(0.5)`, and Elisp likes to be able to consider such a result as constant. – Drew Feb 14 '23 at 02:33
  • 1
    @Drew: I originally thought `(eval (list 'quote x))` would evaluate to `(0.5)` which is identical to the list stored by `x`, instead of the result simply returned by evaluating `'(0.5)`. What you said ("This is just a roundabout case of creating a list by quoting it, e.g., `'(0.5)`, and Elisp likes to be able to consider such a result as constant.") solved my confusion, you might add your comment to your answer, then I will accept it. – shynur Feb 14 '23 at 08:14
  • @Shynur I would like to revise my answer but I don't understand what the connection is between "the manual says eval will change an object's mutability" and "we set the value of a symbol with setq. Then we evaluate the symbol, and get back the value that setq stored.". And I also don't understand what about Drew's comment clarified anything for you. – zwol Feb 14 '23 at 16:01
  • Sorry, I accidentally updated @zwol's answer instead of mine. I've removed what I'd added here. – Drew Feb 14 '23 at 17:07
  • @zwol: It's my fault; I apologize for my misquoting and unclear statements. As you said, the behavior of changing an immutable object (eg, `'(0.5)`) is undefined, and that's what I already knew and I also knew why, anyway, thank you for your list of plausible behaviors. I said "why `eval` will change the mutability" because I originally thought `(eval (list 'quote x))` would evaluate to the same object to which is referenced by `x` (not undefined behavior; not copy): `(quote the_object_referenced_by_x)`⇒ the_object_referenced_by_x. Drew said it is simply like `(quote (0.5)`, nothing special. – shynur Feb 14 '23 at 21:11
0

You say it "worked fine" no doubt because you understand what's happening and the behavior makes sense to you. The manual added this "warning" to make users aware of the behavior; that's all. If you understand the behaver, so that you expect what in fact happens, then all is well.

I don't agree with the manual's wording, FWIW.

Instead of saying that you "shouldn't" do this, it should instead just point out what happens, and then advise you against doing that in general, i.e., to do that only if (1) you understand what it does and (2) for some reason you want it to do that.

There should be almost no uses of the word "should" in the manuals. Instead, there should be explanation of what's going on - the consequences of doing XYZ.


As for why the manual says this? I think it's just trying to point out, besides the general advice that you likely don't want to modify something that can be considered by Emacs as a constant, that sometimes it might not be so obvious that something might be considered by Emacs as a constant.

The blanket statement about eval is not correct or sufficient, IMHO. But the intent of this doc is to save you trouble by inadvertently modifying a "constant". In particular, the doc rightly wants to make you aware that '(some list) is not handled the same by the interpreter and byte compiler as, say (list 'some 'list). And that even cases like the example shown can get you into trouble.

Now, whether you want such behavior/trouble is up to you.


Evaluating (list 'quote x) with x being the list (0.5) evaluates to the list (quote (0.5)), and evaluating that list gives the "constant" list (0.5).

That is, that list could be a "constant", i.e., considered "immutable". Those terms really mean that the list structure is likely something you don't want to modify, because the result is, as @zwol said, undefined.

This is just a roundabout case of creating a list by quoting it, e.g., '(0.5), and Elisp likes to be able to consider such a result as constant.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • 1
    AIUI you have missed the point of the warning in the manual -- the program "should not do this" because modifying a list that was passed to `eval` is not a well-defined operation -- you could get `(1.5)` or `(0.5)` or `()` or you could even make the interpreter crash, and _which behavior you get_ depends on which version of Emacs you're using, whether the expression has been byte-compiled, and other such influences. – zwol Feb 12 '23 at 18:37
  • @zwol: Maybe. That shouldn't be the case IMO. But yeah, it might well be. You might want to post that as an answer - likely it's the right answer. – Drew Feb 12 '23 at 21:53
  • @Shynur: I've added that explanation to my answer. Hope it helps. – Drew Feb 14 '23 at 17:05