The code below has z
as a local variable, yet it behaves as if it is a global:
(defun foo (m)
(let ((z '(stuff nil)))
(push m (getf z 'stuff))
(print z)))
(foo 1)
(foo 2)
(foo 3)
I would expect the output to be
(STUFF (1))
(STUFF (2))
(STUFF (3))
T
but when running it with SBCL I see
(STUFF (1))
(STUFF (2 1))
(STUFF (3 2 1))
T
Why is this the case? Is this behaviour peculiar to property lists?
In foo
, z
is bound to the literal expression '(stuff nil)
. The function destructively alters z
, thus destructively changing the value of the literal. How LISP behaves in circumstances like this is implementation-dependent. Some implementations will obediently alter the literal value (as in your case). Other implementations place literals in read-only memory locations and will fail if you attempt to modify those literals.
To get the desired behaviour, use COPY-LIST
to make a copy of the literal that can be safely modified:
(defun foo (m)
(let ((z (copy-list '(stuff nil))))
(push m (getf z 'stuff))
(print z)))