To better understand the semantics of conses and lists, a graphical representation of this kind of structures is often used. A cons cell is usually represented with two boxes in contact, that contain either two arrows that point to the car
and cdr
values, or directly the values. For instance, the result of:
(cons 1 2)
;; -> (1 . 2)
can be represented with one of these drawings:
Note that these representations are purely conceptual, and do not denote the fact that the values are contained into the cell, or are pointed from the cell: in general this depends on the implementation, the type of the values, the level of optimization, etc. In the rest of the example we will use the first kind of drawing, which is the one more commonly used.
So, for instance:
(cons 1 (cons 2 (cons 3 4))) ; improper “dotted” list
;; -> (1 2 3 . 4)
is represented as:
while:
(cons 1 (cons 2 (cons 3 (cons 4 nil)))) ;; proper list, equivalent to: (list 1 2 3 4)
;; -> (1 2 3 4)
is represented as:
Here is a tree-like structure:
(cons (cons 1 2) (cons 3 4))
;; -> ((1 . 2) 3 . 4) ; note the printing as an improper list
The final example shows how this notation can help us to understand important semantics aspects of the language. First, we write an expression similar to the previous one:
(cons (cons 1 2) (cons 1 2))
;; -> ((1 . 2) 1 . 2)
that can be represented in the usual way as:
Then, we write a different expression, which is apparently equivalent to the previous one, and this seems confirmed by printed representation of the result:
(let ((cell-a (cons 1 2)))
(cons cell-a cell-a))
;; -> ((1 . 2) 1 . 2)
But, if we draw the diagram, we can see that the semantics of the expression is different, since the same cell is the value both of the car
part and the cdr
part of the outer cons
(this is, cell-a
is shared):
and the fact that the semantics of the two results is actually different at the language level can be verified by the following tests:
(let ((c1 (cons (cons 1 2) (cons 1 2)))
(c2 (let ((cell-a (cons 1 2)))
(cons cell-a cell-a))))
(list (eq (car c1) (cdr c1))
(eq (car c2) (cdr c2)))
;; -> (NIL T)
The first eq
is false since the car
and cdr
of c1
are structurally equal (that is true by equal
), but are not “identical” (i.e. “the same shared structure”), while in the second test the result is true since the car
and cdr
of c2
are identical, that is they are the same structure.