Equality of booleans in Emacs
Exploring the quirky world of booleans in Emacs Lisp—or rather, the lack of them! Since everything is either nil (the empty list) or some form of "true", comparing boolean values gets interesting. Learn different approaches using if/when, not, and xor to handle boolean comparisons properly.
There's a fun story about booleans in Emacs Lisp - there are no booleans in Emacs Lisp. Well, sort of. We have a symbol nil, which represents an empty list. You can write it as nil or () - they both stand for the same object, the symbol nil.
(eq nil ()) ; => t
Since LISP is all about list processing, an empty list is something very false. So false that we don't have a special symbol for false values - the empty list serves this purpose well.
Everything that isn't an empty list has the meaning of true. However, there's a symbol t, which is the preferred way to represent the truth value true.
So nil and t are considered canonical boolean values. There's a function booleanp that returns t if the argument is a canonical boolean value and nil otherwise.
The fun begins when you need to check if two boolean values are equal. Since non-nil (or "not an empty list") can mean many different things (like "Emacs is the only true editor"), you can't just do a regular equality check.
(equal t "Emacs is the only true editor") ; => nil
There are, however, several tricks to get it working. The most obvious solution is to convert the value to a canonical boolean value.
#1if/when
We can directly use if function.
(if "Some truth" t nil) ; => t (if 42 t nil) ; => t (if t t nil) ; => t (if nil t nil) ; => nil (let ((a t) (b "Emacs is the only true editor")) (equal (if a t nil) (if b t nil))) ; => t (defun boolean-eq (a b) (equal (if a t nil) (if b t nil))) (let ((a t) (b "Emacs is the only true editor")) (boolean-eq a b)) ; => t
Directly using if is a little bit cumbersome, but when we hide it inside a helper function, it's not that bad, actually.
The same result can be achieved by using when.
(when "Some truth" t) ; => t (when 42 t) ; => t (when t t) ; => t (when nil t) ; => nil (let ((a t) (b "Emacs is the only true editor")) (equal (when a t) (when b t))) ; => t (defun boolean-eq (a b) (equal (when a t) (when b t))) (let ((a t) (b "Emacs is the only true editor")) (boolean-eq a b)) ; => t
#1not
There's another function we can use - not, which returns t if the argument is nil, and returns nil otherwise. Yes, it negates the value, but the result is one of the canonical booleans, so we're good.
Since is equivalent to , we can just compare negated values.
| 0 | 0 | 1 | 1 | 1 | 1 |
| 0 | 1 | 1 | 0 | 0 | 0 |
| 1 | 0 | 0 | 1 | 0 | 0 |
| 1 | 1 | 0 | 0 | 1 | 1 |
(not "Some truth") ; => nil (not 42) ; => nil (not t) ; => nil (not nil) ; => t (let ((a t) (b "Emacs is the only true editor")) (equal (not a) (not b))) ; => t (defun boolean-eq (a b) (equal (not a) (not b))) (let ((a t) (b "Emacs is the only true editor")) (boolean-eq a b)) ; => t
This one looks a little bit better when used without a helper function, at least in my opinion.
#1xor/or/and
Sometimes you want to do something when two 'boolean' values are not equal.
(let ((a nil) (b "Emacs is the only true editor")) (unless (equal (not a) (not b)) (message "Some real work begins"))) ; => Some real work begins
For such situations, there's a xor function, which returns nil when both arguments are equal in canonical boolean form and t otherwise.
(xor nil nil) ; => nil (xor nil t) ; => t (xor t nil) ; => t (xor t t) ; => nil (xor "Some truth" nil) ; => t (xor "Some truth" t) ; => nil (xor 42 nil) ; => t (xor 42 t) ; => nil (let ((a nil) (b "Emacs is the only true editor")) (when (xor a b) (message "Some real work begins"))) ; => Some real work begins
Other functions (like or, and) also convert values to canonical boolean values. So you can keep it in mind.
#1Epilogue
The sole purpose of this post is fun. If you didn't get your portion of fun, then it's not funny at all. Please fix it somehow!