1

The following is a very simple function called my-function which begins by setting the global variable L to be the list containing nil. The next step is to modify (car L), a.k.a. nil, by pushing foo into it. The value of (car L) is then returned.

Since (car L) started out being nil, one expects it to become the list (foo) and in fact it does, but only upon the first call. The second time one calls my-function a very unexpected behavior occurs in the sense that the return value is (foo foo).

(defun my-function ()
  (setq L '(nil))      ;; L is set to be  a list containing a single element, namely the empty list
  (push 'foo (car L))  ;; Push 'foo into (car L), which so far used to be nil. (car L) should therefore become the list (foo)
  (car L))             ;; Return (car L)

(my-function)          ;; Call my-function once

        ⇒ (foo)        ;; As expected

(my-function)          ;; Call my-function twice

        ⇒ (foo foo)    ;; WHAT ???????

Why on earth do we get TWO foo's? my-function started out destroying whatever previous value L might have had, so the second call to my-function should return the same thing as the first one!!!

Should one call my-function successively, we get longer and longer lists of foo's.

This is a minimalist version of a problem I encountered elsewhere and I'd really like to understand the reason for the behavior described above. Among other things I'd like to increase my understanding of the rules of scope in elisp, which I suspect is what is presently eluding me.

In particular I am not interested in workarounds, which I can easily provide, such as replacing the line (setq L '(nil)) by (setq L (list nil)) (which actually also puzzles me a lot!).

EDIT: Some users suggested the related question Why does a constant in `let` change with repeated calls? which is indeed pretty similar but the reliance there on the let construct perhaps obscures the real reason behind the phenomenon which, as pointed out by @phils and @shynur, is an attempt to change the value of a constant.

5
  • 3
    Does this answer your question? Why does a constant in `let` change with repeated calls?
    – phils
    Nov 25, 2023 at 2:11
  • "Quoting should be reserved for constants that will never be modified by side-effects, unless you like self-modifying code."
    – shynur
    Nov 25, 2023 at 5:06
  • Please delete your question, which is a duplicate. Thx.
    – Drew
    Nov 25, 2023 at 16:09
  • @shynur, thanks for weighing in! Your point is the most important thing I learned asking this question. As I found out, the same applies to constant vectors defined by [...]. I wonder if there is any reason why lisp should not prohibit modification of such constants once and for all, the same way (setq nil 1) is prohibited.
    – Ruy
    Nov 27, 2023 at 18:16
  • @Ruy Possibly some function of effort involved and maintaining backward-compatibility. One large discussion on this took place in bugs.gnu.org/40671 (e.g. bugs.gnu.org/40671#135 (or 204, 234, 357, ...)).
    – Basil
    Nov 27, 2023 at 19:21

1 Answer 1

1

It's very important to understand that '... which means (quote ...) is not a shorthand for making lists. It's a form which causes lisp to return, unevaluated, the object that was created by the lisp reader (at read time).

If you quote a list and then modify the quoted list, it stays modified. If you want to create a list and then modify it, create it (at eval time) with (list ...).

Be sure to go over the distinct read and eval phases of lisp execution. Once you understand the distinction, quote will make much more sense.

(defun foo () (list 'a 'b 'c)) => foo
(defun bar () '(a b c)) => bar

(foo) => (a b c)
(bar) => (a b c)

(delq 'b (foo)) => (a c)
(delq 'b (bar)) => (a c)

(foo) => (a b c)
(bar) => (a c)

(symbol-function 'foo) => (closure (t) nil (list 'a 'b 'c))
(symbol-function 'bar) => (closure (t) nil '(a c))
7
  • Thanks a lot. However I should say that this is indeed very unnatural. What you are saying amounts to the fact that changing the OUTPUT of a function (delq 'b (bar)) changes the function itself! This is sure to raise eyebrows of any mathematician such as myself.
    – Ruy
    Nov 25, 2023 at 2:25
  • Correct, it's self-modifying code. This is one example of the (in)famous "code is data" property of lisp. Be careful with quote. (See also the warning in C-h f quote.)
    – phils
    Nov 25, 2023 at 2:26
  • 1
    And to reiterate, I highly recommend that you research the way that lisp goes through distinct read and eval phases, so that you understand when those things happen and what they are doing. (A lot of things fall into place once you understand this process.)
    – phils
    Nov 25, 2023 at 2:30
  • Should I assume that this weird behavior also applies to something like (setq L [nil nil])? Here no quote is used but I understand that, whithin a constant vector, the elements are not evaluated either, right?
    – Ruy
    Nov 25, 2023 at 2:31
  • 1
    And like (list ...) you can call (vector ...) to create a new vector.
    – phils
    Nov 25, 2023 at 2:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.