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.