nil
in ELisp is an amalgam of Scheme’s #f
and '()
.
It is false, and it is the end-of-list; thus it is a boolean, and a list
as well.
Guile has chosen to support nil
as a separate value, distinct
from #f
and '()
. This allows existing Scheme and Elisp
code to maintain their current semantics. nil
, which in Elisp
would just be written and read as nil
, in Scheme has the external
representation #nil
.
In Elisp code, #nil
, #f
, and '()
behave like
nil
, in the sense that they are all interpreted as nil
by
Elisp if
, cond
, when
, not
, null
, etc.
To test whether Elisp would interpret an object as nil
from
within Scheme code, use nil?
:
Return #t
if obj would be interpreted as nil
by
Emacs Lisp code, else return #f
.
(nil? #nil) ⇒ #t (nil? #f) ⇒ #t (nil? '()) ⇒ #t (nil? 3) ⇒ #f
This decision to have nil
as a low-level distinct value
facilitates interoperability between the two languages. Guile has chosen
to have Scheme deal with nil
as follows:
(boolean? #nil) ⇒ #t (not #nil) ⇒ #t (null? #nil) ⇒ #t
And in C, one has:
scm_is_bool (SCM_ELISP_NIL) ⇒ 1 scm_is_false (SCM_ELISP_NIL) ⇒ 1 scm_is_null (SCM_ELISP_NIL) ⇒ 1
In this way, a version of fold
written in Scheme can correctly
fold a function written in Elisp (or in fact any other language) over a
nil-terminated list, as Elisp makes. The converse holds as well; a
version of fold
written in Elisp can fold over a
'()
-terminated list, as made by Scheme.
On a low level, the bit representations for #f
, #t
,
nil
, and '()
are made in such a way that they differ by
only one bit, and so a test for, for example, #f
-or-nil
may be made very efficiently. See libguile/boolean.h
, for more
information.
Since Scheme’s equal?
must be transitive, and '()
is not equal?
to #f
, to Scheme nil
is not
equal?
to #f
or '()
.
(eq? #f '()) ⇒ #f (eq? #nil '()) ⇒ #f (eq? #nil #f) ⇒ #f (eqv? #f '()) ⇒ #f (eqv? #nil '()) ⇒ #f (eqv? #nil #f) ⇒ #f (equal? #f '()) ⇒ #f (equal? #nil '()) ⇒ #f (equal? #nil #f) ⇒ #f
However, in Elisp, '()
, #f
, and nil
are all
equal
(though not eq
).
(defvar f (make-scheme-false)) (defvar eol (make-scheme-null)) (eq f eol) ⇒ nil (eq nil eol) ⇒ nil (eq nil f) ⇒ nil (equal f eol) ⇒ t (equal nil eol) ⇒ t (equal nil f) ⇒ t
These choices facilitate interoperability between Elisp and Scheme code, but they are not perfect. Some code that is correct standard Scheme is not correct in the presence of a second false and null value. For example:
(define (truthiness x) (if (eq? x #f) #f #t))
This code seems to be meant to test a value for truth, but now that
there are two false values, #f
and nil
, it is no longer
correct.
Similarly, there is the loop:
(define (my-length l) (let lp ((l l) (len 0)) (if (eq? l '()) len (lp (cdr l) (1+ len)))))
Here, my-length
will raise an error if l is a
nil
-terminated list.
Both of these examples are correct standard Scheme, but, depending on
what they really want to do, they are not correct Guile Scheme.
Correctly written, they would test the properties of falsehood or
nullity, not the individual members of that set. That is to say, they
should use not
or null?
to test for falsehood or nullity,
not eq?
or memv
or the like.
Fortunately, using not
and null?
is in good style, so all
well-written standard Scheme programs are correct, in Guile Scheme.
Here are correct versions of the above examples:
(define (truthiness* x) (if (not x) #f #t)) ;; or: (define (t* x) (not (not x))) ;; or: (define (t** x) x) (define (my-length* l) (let lp ((l l) (len 0)) (if (null? l) len (lp (cdr l) (1+ len)))))
This problem has a mirror-image case in Elisp:
(defun my-falsep (x) (if (eq x nil) t nil))
Guile can warn when compiling code that has equality comparisons with
#f
, '()
, or nil
. See Compiling Scheme Code, for details.