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
nil
values
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-boolean
consp : object -> generalized-boolean
listp : 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.