Peter Seibel wrote in Practical Common Lisp that "All values in Common Lisp are, conceptually at least, references to objects."
I tried that concept with the following code:
(setf x 5)
(setf y x)
(print x) % output: x is 5
(print y) % output: y is 5
(setf x 6)
(print x) % output: x is 6
(print y) % output: y is 5
If Lisp was pass-by-object-reference, y should point to x and so changing x to 6 should also change y to 6. But that's not what happened. It looks instead like Lisp is pass-by-object-value. Can someone help explain what happened?
Small integers are not usually references in Lisp implementations. It is not correct to say that all values are references in Common Lisp. Programming under the suspicion that values are references is often the safer assumption that is more aligned with writing correct code.
However, your example is consistent with small integers being implemented as references. It doesn't prove that they are not.
If a variable x holds an integer like 5, and then we assign to x with (setf x 4), we are not mutating the object 5 into 4. We are mutating the variable binding x: we have overwritten the 5 value that was previously in x with the new value 4.
This will work even if we use objects that are positively references into the heap, like cons cells:
(setf x '(1 . 2))
(setf y x)
y -> (1 . 2)
(setf x '(4 . 5))
y -> (1 . 2)
x and y are independent variables, and independently hold references to cons cells. x initially holds a reference to the (1 . 2) object. We assign x to y, and so now y also holds a reference to (1 . 2). It has its own copy of the reference. So then when we assign (4 . 5), x's reference is overwritten with a reference to (4 . 5), but y is unaffected. Why should it be?
How we can demonstrate that conses use reference semantics is by mutating the cells themselves:
(setf x (cons 1 2)) ;; we better not use a literal any more!
x -> (1 . 2)
(setf y x)
y -> (1 . 2)
;; now mutate
(rplaca x 10)
(rplacd x 20)
x -> (10 . 20)
y -> (10 . 20)
Since mutating the cell stored in x makes the mutation appear on the cell stored in y, we know that x and y must hold a reference to the same object.
Now here is the kicker: we cannot perform this test with integers, because integers are not mutable! There is no function similar to rplaca that will clobber the actual bits representing 1, and turn them into 10.
The eq function doesn't help, because all it can do is to confirm that the two values are the same object:
(setf x 5)
(setf y x)
(eq x y) -> ?
If this eq call returns T, then x and y are the same object. I say if because ANSI Common Lisp leaves this implementation-specific. It is permitted for an implementation to yield NIL!
However, it yields T in implementations in which small integers (fixnums) are packed directly into a value, and are not pointer into boxed heap object (the prevalent implementation approach). So that is to say, a value like 4 is considered to be a one object everywhere it appears, even though it is propagated by the copying of a value that holds the the bit pattern representing 4 directly.
So it more or less boils down to that you just have to know how your implementation works to be sure about which kinds of objects are references.
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