In the SBCL REPL, why does entering (nil . nil) evaluate to (nil) and not just nil?
If an empty list is one where both "elements" of the cons cell are nil, why are these not the same?
My assumption for this is that SBCL makes the following evaluations:
(car '()) => nil
(cdr '()) => nil
(car '(nil . nil)) => nil
(cdr '(nil . nil)) => nil
And yet:
'() => nil
'(nil . nil) => (nil)
>Solution :
car and cdr return nil if given nil as an argument. Let’s predispose of the word nil for a minute and see what values you’re actually looking at.
(car '()) == '()
(cdr '()) => nil
car and cdr got an empty list, so we return nil, or ().
(car '(nil . nil)) => nil
(cdr '(nil . nil)) => nil
Now, (nil . nil) is (() . ()). That is, it’s a cons cell whose car and cdr are both nil.
When we have a . () at the end of a cons cell, we can shorten it notationally by omitting the trailing nil. This is just a notational convenience, so by our notation (() . ()) can be written as (()), or (nil). Note that that does not change the value. The most explicit way to write it is still (() . ()), but we can also write it shorter for readability.
If an empty list is one where both "elements" of the cons cell are nil, why are these not the same?
That is not correct. An empty list is not a cons cell at all. An empty list is the atom nil. It’s a symbol, just like 'foo or 'pizza or 'common-lisp. It’s just a symbol that we chose to use for this purpose. But nil doesn’t have a car or cdr cell. It just so happens that it’s often convenient to let (car nil) and (cdr nil) be nil as corner cases of algorithms, so the functions car and cdr have special behavior on nil. But nil is not a cons cell.
> (consp nil)
nil
> (consp '())
nil
> (consp '(1 . 2))
T
Per the system class LIST, the list type can be described as
The types
consandnullform an exhaustive partition of the type list.
So a list in Common Lisp is defined to be "either a cons cell or the special value nil". Notably, nil is not itself a cons cell.