Note: this is cannibalized from my answer to Recursive range in Lisp adds a period? The question is different (so not a duplicate), but the background explanation of how lists are constructed from cons cells, and how they're printed is the same. The end is a bit different.
A list in Scheme is either the empty list ()
(also known as nil
in some Lisps), or a cons cell whose car
(also known as first
) is an element of the list and whose cdr
(also known as rest
) is either the rest of the list (i.e., another list), or an atom that terminates the list. The conventional terminator is the empty list ()
; lists terminated by ()
are said to be "proper lists". Lists terminated by any other atom are called "improper lists". The list (1 2 3 4 5)
contains the elements 1, 2, 3, 4, and 5, and is terminated by ()
. You could construct it by
(cons 1 (cons 2 (cons 3 (cons 4 (cons 5 ())))))
Now, when the system prints a cons cell, the general case is to print it by
(car . cdr)
For instance, the result of (cons 1 2)
is printed as
(1 . 2)
Since lists are built of cons cells, you can use this notation for lists too:
'(1 2 3 4 5) ; ==
'(1 . (2 . (3 . (4 . (5 . ())))))
That's rather clunky, though, so most lisps (all that I know of) have a special case for printing cons cells: if the cdr
is a list (either another cons cell, or ()
), then don't print the .
, and don't print the surrounding parenthesis of the cdr
(which it would otherwise have, since it's a list).
With this understanding of lists and cons cells, we can consider the specific cases ((1 2) 3)
and ((1 2) . 3)
. Let's break each of these down into the cons cells.
((1 2) 3) ; ==
((1 . (2 . ()) . (3 . ())) ; ==
(cons (cons 1 (cons 2 ()) (cons 3 ()))
((1 2) . 3) ; ==
((1 . (2 . ())) . 3) ; ==
(cons (cons 1 (cons 2 ())) 3)
The last lines in each case are different, so ((1 2) 3)
and ((1 2) . 3)
are not the same.