0

In "The Little Schemer", null is used to refer to the empty list (). But I also see the empty list referred to as nil in Scheme's Error message, e.g. doing:

(car ())

causes:

Error: Attempt to apply car on nil [car] [1]

Which sounds like it's referring to () as nil.


[1] using replit.com's BiwaScheme Interpreter version 0.6.4


This question is similar to What is the exact difference between null and null in common lisp. It is different because as the answers here point out there are differences between Common Lisp and Scheme in their treatment of nil and null.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
joseville
  • 685
  • 3
  • 16

2 Answers2

3

I think that what you're seeing is an artifact of history in the error messages.

In traditional Lisps, nil was both the empty list and boolean false, and was rather special in various other ways (for instance, it was also a symbol, which is very weird).

So in particular () and nil are the same object in such a Lisp, and (not nil) is true, and (not '()) is also true (and in fact (not ()) is both legal and also true). The predicate for the empty list object is null (perhaps should be nullp but history says it is what it is) and (null nil) is true as is (null '()) and (null ()).

There are also questions about what (car nil) aka (car '()) aka (car ()) should do. Traditionally the answer was this was fine and (car nil) is nil. This gives you a very neat implementation of nil if you're clever.

Scheme now does away with all of this punning:

  • there is a unique false object which is #f / #false, which is not the empty list;
  • all other objects, are true, so (not x) is true only for x being #f;
  • there is a unique empty list object, which is (), which is not #f and which is therefore true;
  • the predicate for this object is null?, so (null? '()) is true, but (null? #f) is false;
  • car and cdr of () are errors;
  • nil is just a symbol with no special status in the language at all.

I don't think that having a name for () is standard, but obviously it might be useful to give it one, and null is fine: Racket does that, for instance.

So in Scheme, nil is just gone, and the pun between empty lists and boolean false with it.

But very many people who implement Schemes know that nil is what people call the empty list. So when they write error messages, they write nil when they mean (), because that's what everyone has called () for a very long time.

And that, I think, is what you are seeing here.

ignis volens
  • 7,040
  • 2
  • 12
  • 1
    May be worth adding: in Common Lisp `nil` and `()` are self-evaluating symbols, so `(null nil)`, `(null 'nil)`, `(null ())`, and `(null '())` are all ok. In Scheme `()` is _not_ self-evaluating, and `(null? ())` is a syntax error; the empty list must be quoted in Scheme: `(null? '())`. WRT `null` vs `nullp` in CL: I sometimes wish it was `nullp`, and often I accidentally type `nullp`, especially after writing a bunch of Scheme code before writing some Common Lisp ;) – ad absurdum Dec 13 '21 at 14:20
  • 1
    @adabsurdum: I agree with that, but I wanted not to get too far into the gory details of it (I had an earlier draft which had a lot more stuff in it but I think it just got too far from the original question). – ignis volens Dec 13 '21 at 14:57
  • 1
    In addition `()` is not a valid expression in Scheme. Some implementations allows it, but the standard is clear that you need to quote it to get the empty list. eg. `(null? ())` would not be a valid Scheme expression while `(null? '())` is. OPs expression `(car ())` has the same problem and thus might fail before `car` application. – Sylwester Dec 18 '21 at 00:46
  • @Sylwester: yes. I think I've always quoted `()` in the answer except where explicitly pointing out that it was allowed unquoted in traditional lisps). In fact I now always quote `()` in CL as well, in fact, because, well, because it makes more sense to me. Once I used not to, but once I had no taste... – ignis volens Dec 20 '21 at 10:20
1

The difference between different Lisps is how they handle true, false and nil.

In Common Lisp, we have t for true, and nil for false, although everything except nil is in fact true. Where the list is empty, or a find found nothing, we have nil and we can test directly. The technical term is nil punning.

'() ; => NIL
(= 1 2) ; => NIL
(if '() "full" "empty") ; => "empty"

(find 5 '(1 2 3 4)) ; => NIL
(if (find 5 '(1 2 3 4)) "found" "not found" ; => "not found"

Clojure does it differently. There is one official true value true, and there are two false values, false and nil - all values other than false and nil are also true. The empty list is no longer nil, and you need to use a separate test for an empty list, with empty?. The value nil is more like void in C. Better or worse? - you decide.

'() ; => ()
(= 1 1) ; => true
(= 1 2) ; => false
(if '() "full" "empty") ; => "full"
(if (empty? '()) "empty" "full") ; => "empty"
(if nil "full" "empty") ; => "empty"
(if (println "Hello world") "full" "empty") ; => empty

Scheme is much closer to Clojure in intent. Here there is one false value, #f. The empty list is no longer nil, and you need to use a separate test for an empty list, with null?.

(= 1 1) ; => #t
(= 1 2) ; => #f
(null? '()) ; => #t
(null? 'nil) ; => #f

So, null is another name for the empty list, but nil or 'nil is not.

null ; => ()
nil  ; =>  cannot reference an identifier before its definition
'nil ; => nil

How about (car ())? Your Scheme returns nil from evaluating (), and that is what car is complaining about. My Scheme, Dr Racket, refuses to play at all, saying that the expression is missing from the empty list.

Francis King
  • 1,652
  • 1
  • 7
  • 14