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 Lisp, every symbol has a property list (plist). When a symbol is created initially its property list is empty. A property list consists of entries where every entry consists of a key called an indicator and a value called a property . There are no duplicates among the indicators.
Defining a global variable: New global variables can be defined using DEFVAR and DEFPARAMETER construct. The difference between both of them is that DEFPARAMETER always assigns initial value and DEFVAR form can be used to create a global variable without giving it a value.
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)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With