1

I do not understand the following code :

(defun my-test ()
  (let ((temp '(())))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

;; Execute this call twice in a row.
(my-test)

Output :

temp: (NIL)
temp: ((5))

How can temp save the value ? I know there is the following warning, but I do not understand the logic behind this behavior.

; in: DEFUN MY-TEST
;     (PUSH 5 (FIRST TEMP))
; --> LET* 
; ==>
;   (SB-KERNEL:%RPLACA #:TEMP0 (CONS 5 (FIRST #:TEMP0)))
; 
; caught WARNING:
;   Destructive function SB-KERNEL:%RPLACA called on constant data: (NIL)
;   See also:
;     The ANSI Standard, Special Operator QUOTE
;     The ANSI Standard, Section 3.2.2.3
; 
; compilation unit finished
;   caught 1 WARNING condition

The following codes output the same result :

(flet ((my-fct ()
     (let ((temp '(())))
       (format t "temp: ~a~%" temp)
       ((lambda ()
          (push 5 (first temp)))))))
  (my-fct)
  (my-fct))

(let ((fct (lambda ()
         (let ((temp '(())))
           (format t "temp: ~a~%" temp)
           ((lambda ()
          (push 5 (first temp))))))))
  (funcall fct)
  (funcall fct))

But this one works :

;; Execute this call twice in a row.
((lambda ()
   (let ((temp '(())))
     (format t "temp: ~a~%" temp)
     ((lambda ()
    (push 5 (first temp)))))))

This one works too :

(defun my-test ()
  (let ((temp (list ())))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

(my-test)

And this one too :

(defun my-test ()
  (let ((temp (list (list))))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

(my-test)

But not this one :

(defun my-test ()
  (let ((temp `(,(list))))
    (format t "temp: ~a~%" temp)
    ((lambda ()
       (push 5 (first temp))))))

(my-test)

  • Why does it work for some codes, and not for some others ?
  • How can the lexical value can be conserved across multiple calls ?
Fnifni
  • 319
  • 1
  • 10
  • 1
    Dammit. I made an answer looking the special cases like `\`(,(list))` which I found quite interesting. I think he's using SBCL and it actually constant folds `(list)` to `()` which makes the whole thing `'(())`. Also the first OP says works doesn't in SBCL if you save it as a file and load a compiled version. Then `'(())` in all places within the file is replaced by one instance and mutating the first mutates all. – Sylwester Feb 13 '19 at 22:10
  • @Sylwester : I indeed use SBCL, and did not compile the file. Common Lisp is so cool. :D – Fnifni Feb 13 '19 at 22:28

1 Answers1

5

The Common Lisp spec does not specify what happens when you try to mutate constant data. Constant data is:

  1. Data produced by the quote operator
  2. Any object which evaluates to itself and is a part of the source code

The intent of this is that implementations are allowed to use read only memory (which needn’t be scanned by the gc) for constants, and to reuse the storage for equal constants.

So the code:

(defun foo ()
  ... '(()) ...)

Can be converted to:

(defconstant +foo1+ '(()))
(defun foo ()
  ... +foo1+ ...)

Without deviating from the letter or spirit of the standard.

Dan Robertson
  • 4,315
  • 12
  • 17