Optionality in Common Lisp: Generalized Booleans
Common Lisp has one “falsy” value: nil. Every non-nil value is
considered “truthy”, but there is also a “true” value, t. This union
of two-valued “Boolean” logic and a more generic “nothing” vs “something”
logic is called the “generialized boolean”.
There’s one little wrinkle to this particular to Common Lisp, which is
that nil and the empty list () are considered the same
value. Mostly, this is a great convenience when dealing with
lists. Let’s explore some other consequences.
One consequence is that there are two different functions which all, basically do the same thing, and the main reason for chosing one over the other has to do with the semantics of one’s code.
not : generalized-boolean -> boolean- used for logical inversion
null : object -> generalized-boolean- used for detecting
nilvalues
There’s also endp which is strict about its input and does a little
extra work so it works with circular lists.
endp : list -> generalized-boolean- used for detecting the end of a list
It’s interesting to think about atom in this context too, which
returns t for all non cons types. And if we’re thinking of atom
we should probably consider consp and listp too. They all have the
same signature:
atom : object -> generalized-booleanconsp : object -> generalized-booleanlistp : object -> generalized-boolean
Here’s a table showing the mapping of inputs and outputs:
| function | nil |
'(foo bar) |
t |
'foo |
|---|---|---|---|---|
not |
t |
nil |
nil |
nil |
null |
t |
nil |
nil |
nil |
endp |
t |
nil |
error | error |
atom |
t |
nil |
t |
t |
consp |
nil |
t |
nil |
nil |
listp |
t |
t |
nil |
nil |
There’s system classes which these predicate functions
null, consp, atom, and listp are coorespondents:
| equiv | |
|---|---|
null |
|
cons |
|
atom |
(not cons) |
list |
(or null cons) |
This might seem like a snarl of logic, which is unfortunate, I don’t
know if I can convey quite what’s awesome about it. The True and False
of classical Boolean logic are represented by t and nil, but these
two values are members of the types t and null. The null type
only has one member, nil, but the t type has t and everything
else. This is the basis of the generalized logic of “nothing” vs
“something”.
Now I feel like I have run out of space to discuss “conses” and the
construction of lists and trees, and nil’s double life as an empty
list. But if you’ve read the other articles in this series the concept
of optionality being represented by the identity element should have
been introduced, and that concept is, of course true in Common Lisp,
as well as everything else. If you’re dealing with cons objects any
function of cons works with nil too.
There’s much more to say, of course, but it’ll have to wait for another post.