Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

I never realized the funny difference between nil and () before.

148 views
Skip to first unread message

jimka

unread,
Nov 1, 2011, 10:44:30 AM11/1/11
to
I got this excerpt from Common Lisp the Language, 2nd Edition
http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node9.html#SECTION00522000000000000000

An empty list may ... be written (); this normally denotes the same
object as nil.
(It is possible, by extremely perverse manipulation of the package
system, to cause the sequence of letters nil to be recognized not as
the symbol that represents the empty list but as another symbol with
the same name.)

Madhu

unread,
Nov 1, 2011, 11:50:58 AM11/1/11
to

There is only one, One, ONE!!, One, one symbol which is CL:NIL

* jimka <d6bccc83-d83e-4f44...@s35g2000pra.googlegroups.com> :
Wrote on Tue, 1 Nov 2011 07:44:30 -0700 (PDT):

[cltl]

| An empty list may ... be written (); this normally denotes the same
| object as nil. (It is possible, by extremely perverse manipulation of
| the package system, to cause the sequence of letters nil to be
| recognized not as the symbol that represents the empty list but as
| another symbol with the same name.)

There is no difference between CL:NIL and ().

This is probably GLS' idea of "extremely perverse manipulation of the
package system"

(defpackage "FOO"
(:use "CL")
(:shadow "NIL")
(:export "NIL"))

(setq foo:nil t)
(in-package "FOO")

(eq nil ()) ;; => CL:NIL

But, There is no difference between CL:NIL and ().

There is only one, One, ONE!!, One, one symbol which is CL:NIL

--- Madhu

Pascal Costanza

unread,
Nov 1, 2011, 12:04:52 PM11/1/11
to
That is not perverse. Here is something perverse:

CL-USER 1 > (rename-package :cl "COMMON-LISP-OLD")
#<The COMMON-LISP-OLD package, 3/4 internal, 978/1024 external>

CL-USER 2 > (defpackage "COMMON-LISP"
(:use :common-lisp-old)
(:shadow "NIL")
(:export "NIL"))

Error: Defining system package COMMON-LISP.
1 (continue) Define it anyway.
2 (abort) Return to level 0.
3 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other
options.

CL-USER 3 : 1 > :c
#<The COMMON-LISP package, 0/16 internal, 1/16 external>

CL-USER 4 > (in-package :common-lisp)
#<The COMMON-LISP package, 0/16 internal, 1/16 external>

COMMON-LISP 5 > (defparameter nil 42)

Error: Defining variable NIL visible from package COMMON-LISP.
1 (continue) Define it anyway.
2 (abort) Return to level 0.
3 Return to top loop level 0.

Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other
options.

COMMON-LISP 6 : 1 > :c
NIL

COMMON-LISP 7 > nil
42


;-)

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
The views expressed are my own, and not those of my employer.

Pascal J. Bourguignon

unread,
Nov 1, 2011, 12:46:25 PM11/1/11
to
That's not perverse. Here is something perverse:

;; Add to your new common lisp package:

(defun null (x) (or (eql x 'nil) (eql x nil)))

(set-macro-character #\(
(lambda (stream ch)
(if (char= (peek-char t stream) #\))
'nil
(read-delimited-list #\) stream t))))


so that (and (eql '() 'nil)
(eql () nil)
(not (eql '() nil))
(not (eql () 'nil))
(null ())
(null '())
(null nil)
(null 'nil))



--
__Pascal Bourguignon__ http://www.informatimago.com/
A bad day in () is better than a good day in {}.

Kaz Kylheku

unread,
Nov 2, 2011, 1:16:12 PM11/2/11
to
Forgetting to bring in symbols from the :cl package is hardly perverse,
let alone extremely perverse:

[1]> (defpackage :my-package (:use))
#<PACKAGE MY-PACKAGE>
[2]> (in-package :my-package)
#<PACKAGE MY-PACKAGE>
MY-PACKAGE[3]> nil

*** - SYSTEM::READ-EVAL-PRINT: variable NIL has no value
The following restarts are available:
USE-VALUE :R1 Input a value to be used instead of NIL.
STORE-VALUE :R2 Input a new value for NIL.
ABORT :R3 Abort main loop

fortunatus

unread,
Nov 2, 2011, 8:00:32 PM11/2/11
to
I find the basic concept a little odd in itself:

CL-USER> (symbolp 'nil)
T <-- no sweat
CL-USER> (symbolp nil)
T <-- kind of freaky...
CL-USER> (symbolp '( ))
T <-- this freaks me out!!
CL-USER> (symbolp ( ))
T
CL-USER> (symbolp '( nil ))
NIL <-- finally back to normalcy - whew

A list is not a symbol but the empty list is a symbol?!! The nothing-
value is a symbol? That freaks me out!

Ankur

unread,
Nov 3, 2011, 1:12:21 AM11/3/11
to
* fortunatus <daniel....@excite.com>
> I find the basic concept a little odd in itself:
>
> CL-USER> (symbolp 'nil)
> T <-- no sweat
> CL-USER> (symbolp nil)
> T <-- kind of freaky...

[1]> (symbolp ':hi)
T
[2]> (symbolp :hi)
T

> CL-USER> (symbolp '( ))
> T <-- this freaks me out!!
> CL-USER> (symbolp ( ))
> T

Why? () is the same as NIL, isn't it?

> CL-USER> (symbolp '( nil ))
> NIL <-- finally back to normalcy - whew

(listp '(nil)) => T

It's a list with one element.

> A list is not a symbol but the empty list is a symbol?!! The nothing-
> value is a symbol? That freaks me out!

It's defined that way, isn't it? What's freaky about it?

--
Ankur

Kaz Kylheku

unread,
Nov 3, 2011, 1:46:37 AM11/3/11
to
On 2011-11-03, fortunatus <daniel....@excite.com> wrote:
> I find the basic concept a little odd in itself:
>
> CL-USER> (symbolp 'nil)
> T <-- no sweat
> CL-USER> (symbolp nil)
> T <-- kind of freaky...

CL-USER> nil
NIL
CL-USER> 'nil
NIL

The object nil, when treated as program syntax to be evaluated,
evaluates to itself, nil. So nil and (quote nil) is the same.

(QUOTE X) and X is the same thing for any X which evaluates to itself!

Thus this is the case for most atoms, and some kinds of symbols. The symbol T,
and keyword symbols:

CL-USER> 5
5
CL-USER> '5
5

CL-USER> 'T
T
CL-USER> T
T

CL-USER> ':keyword-x
:KEYWORD-X
CL-USER> :keyword-x
:KEYWORD-X

> CL-USER> (symbolp '( ))
> T <-- this freaks me out!!

Note how the unquoted case freaks you out when you use nil,
and the quoted case freaks you out when you use (). Haha!

Since empty list is represented by the symbol NIL, symbolp has to return
T for an empty list.

> A list is not a symbol but the empty list is a symbol?!! The nothing-

A list is not an encapsulated container of stuff like in some languages. It is
recursively defined as one of two things:

1. The symbol nil, denoting an empty list
2. A cons cell, with the first element of the list in CAR, and
a list of the remaining elements in CDR.

(A cons cell which has something other than a list in CDR is called
an "improper list".)

So you cannot have an operation like

(list-append list new_element)

unless list-append is an operator rather than a function. You cannot turn a
list stored in a location L from empty to non-empty without overwriting the
location L with a new object:

(setf L (cons 1 L)) ;; prepend 1 to list L.

The above can be done using a standard macro:

(push 1 L) ;; expands to something similar to (setf L (cons 1 L)).

The container's identity changes!

This is an important feature in Lisp because of this:

(let* ((list1 '(1 2 3))
(list2 (cons 0 list1))
(list3 (cdr list1)))
(values list1 list2 list3))

We can prepend to a list, or remove the first item from a list,
without modifying it in any way. (In fact the literal list '(1 2 3)
cannot be modified without invoking undefined behavior!)

The expressions which initialize list2 and list3 just perform some
pointer manipulations: allocate a cons cell, and stuff the existing
list into the CDR, thereby obtaining a longer list; or retrieve
the CDR of the original list to obtain a suffix of it shortened
by one item.

In Lisp, a lot of computation is done using lists, whereby old lists are turned
into parts of new lists, without a lot of unnecessary copying going on.

Recursion on list structures takes place without copying cells
thereby creating garbage.

> value is a symbol?

It's not a nothing value. It's an empty list value which also serves
as a boolean false.

To obtain a nothing value, call the function VALUES with no arguments:

CL-USER> (values)

But, alas, even that is a symbol:

CL-USER> (symbolp (values))
T

:)

This is because if the expression (values) is used in a context
that requires a value, like a function argument, a default value
of NIL is supplied.

fortunatus

unread,
Nov 3, 2011, 11:01:00 AM11/3/11
to
On Nov 3, 1:46 am, Kaz Kylheku <k...@kylheku.com> wrote:
> To obtain a nothing value, call the function VALUES with no arguments:
>
>  CL-USER> (values)
>
> But, alas, even that is a symbol:
>
>  CL-USER> (symbolp (values))
>  T
>
> :)


AAAAAAHHHHH!!!!

fortunatus

unread,
Nov 3, 2011, 11:21:42 AM11/3/11
to
On Nov 3, 1:46 am, Kaz Kylheku <k...@kylheku.com> wrote:

> A list is not an encapsulated container of stuff like in some languages. It is
> recursively defined as one of two things:
>
> 1. The symbol nil, denoting an empty list
> 2. A cons cell, with the first element of the list in CAR, and
>    a list of the remaining elements in CDR.

A very good point!

Barry Fishman

unread,
Nov 3, 2011, 11:40:36 AM11/3/11
to

On 2011-11-03 01:46:37 EDT, Kaz Kylheku wrote:
> (QUOTE X) and X is the same thing for any X which evaluates to itself!

I don't think so. They evaluate to the same thing. This is an
important distinction when writing macros:

(non-eval-eq (a b)
`(eq ',a ',b))

(non-eval-eq (quote nil) nil)
==> nil
(non-eval-eq () nil)
==> t

--
Barry Fishman

Don Geddis

unread,
Nov 3, 2011, 11:29:19 AM11/3/11
to
Pascal Costanza <p...@p-cos.net> wrote on Tue, 01 Nov 2011:
>> * jimka<d6bccc83-d83e-4f44...@s35g2000pra.googlegroups.com> :
>> Wrote on Tue, 1 Nov 2011 07:44:30 -0700 (PDT):
>> [cltl]
>> | An empty list may ... be written (); this normally denotes the same
>> | object as nil. (It is possible, by extremely perverse manipulation of
>> | the package system, to cause the sequence of letters nil to be
>> | recognized not as the symbol that represents the empty list but as
>> | another symbol with the same name.)
>>
> That is not perverse. Here is something perverse:
[...]
> COMMON-LISP 5 > (defparameter nil 42)
> Error: Defining variable NIL visible from package COMMON-LISP.
> 1 (continue) Define it anyway.
> 2 (abort) Return to level 0.
> 3 Return to top loop level 0.

Sure, but redefining symbols in the COMMON-LISP package already violates
the standard, right? (Hence the continuable error you got.) It's cute,
but CLTL hardly needed a parenthetical warning against people who
deliberately violate other parts of the standard. Surely Steele had
some other example in mind...

-- Don
_______________________________________________________________________________
Don Geddis http://don.geddis.org/ d...@geddis.org
There has been an alarming increase in the number of things you know nothing
about.

Kaz Kylheku

unread,
Nov 3, 2011, 12:12:33 PM11/3/11
to
On 2011-11-03, Barry Fishman <barry_...@acm.org> wrote:
>
> On 2011-11-03 01:46:37 EDT, Kaz Kylheku wrote:
>> (QUOTE X) and X is the same thing for any X which evaluates to itself!
>
> I don't think so.

I see, this is what Tim Bradshaw was talking about a couple of days ago.

Tim Bradshaw

unread,
Nov 3, 2011, 3:58:27 PM11/3/11
to
Kaz Kylheku <k...@kylheku.com> wrote:
>
> A list is not an encapsulated container of stuff like in some languages. It is
> recursively defined as one of two things:
>
> 1. The symbol nil, denoting an empty list
> 2. A cons cell, with the first element of the list in CAR, and
> a list of the remaining elements in CDR.

I think that NIL has been implementationally interesting sometimes though:
since (car nil) is nil &c, I think there have been (are?) implementations
which do some clever trick to make code not have to special-case NIL.
Someone who knows more about implementations than me might have a better
comment.

Kaz Kylheku

unread,
Nov 3, 2011, 4:22:02 PM11/3/11
to
By "not special case it" I think you mean that it actually looks like a symbol,
or cons cell, etc.

In unsafe compiled code you could get some boost if (car x) compiles to
something that doesn't check the type of x, if x is declared to be a list. If
nil is actually a pointer to something that looks like a cons, you get smaller
code. One or two less words in the instruction cache, but a hit to the data
cache.

In a lot of code you do compare against nil. If this nil-cons could be located
a constant address in every image, then you can still do efficient tests for
the presence of nil.

It would just be annoying for someone debugging at the machine level, if nil
does not have a clear bit pattern, that's all. :)

Tim Bradshaw

unread,
Nov 3, 2011, 5:10:44 PM11/3/11
to
Kaz Kylheku <k...@kylheku.com> wrote:
.
>
> By "not special case it" I think you mean that it actually looks like a symbol,
> or cons cell, etc.
>
Yes. The HOPL paper describes this, in fact, and it seems to be MACLISP
which I was remembering (a description of - I never used it). Interestingly
tha (car nil) = nil &c thing seems to have come from InterLisp, and wasn't
always true in other Lisps: I didn't know that.

Kaz Kylheku

unread,
Nov 3, 2011, 5:53:39 PM11/3/11
to
On 2011-11-03, Tim Bradshaw <t...@tfeb.org> wrote:
Really. Let's make a quick peek at the Lisp 1.5 manual.

Ah, here we go. Page 2:

"car of an atomic symbol is undefined"

Not every good idea in Lisp was discovered early, like allowing (car nil) and
(cdr nil).

This has me googling for "A Short Ballad Dedicated to The Growth Of Programs"
by Ashwin Ram. :)

Alex Mizrahi

unread,
Nov 4, 2011, 3:23:59 PM11/4/11
to
> A list is not a symbol but the empty list is a symbol?!! The nothing-
> value is a symbol? That freaks me out!

It is in line with mathematical set theory notation where empty set is
represented with crossed O and other sets with a list of members, e.g.
0
{0}
{{0}}
{{0},0}


Rob Warnock

unread,
Nov 5, 2011, 12:04:02 AM11/5/11
to
Tim Bradshaw <t...@tfeb.org> wrote:
+---------------
+---------------

CMUCL implements a variant of this overly-"clever" hack:

1. The value of NIL (0x28f0000b) is low-tagged with 0x3, which says LIST
(well, CONS, really). CONS cells are two words only, and have no heap
header word.

2. The process-wide single instance of the internal NIL *symbol structure*
is located in a fixed place (0x28f00004), and is deliberately mis-aligned
compared to "normal" symbols, which are aligned on 8-byte boundaries
[as are all heap objects except NIL!].

3. The first two slots of the symbol NIL (Value & Hash) contain the
value NIL (0x28f0000b) so that *WHEN TREATED AS A CONS POINTER*
the value of NIL is of the correct format [including low-tag]
to be a (CONS NIL NIL) cell pointer. So NIL == (CAR NIL) == (CDR NIL).

4. But *if* you can sneak the value of NIL past the usual low-tag checks
in symbol-processing functions [the low-tag for a symbol is 0x7, the
same as generic "other" heap objects], then the value of NIL when
a *symbol's* low-tag (0x7) is subtracted from it -- I say again,
*subtracted* from it, *not* masked off! -- is (0x28f0000b - 7) =
0x28f00004, which is indeed the address of the *symbol* NIL's
heap header.

Now somebody surely thought this was a performance advantage way back
when CMUCL was first designed, but these days with instruction execution
being *much* faster than memory references, there's a better way:

Simply allocate a small (8-bit) otherwise-unused "immediate" constant
to NIL [the value 0x2, "type_OtherImmediate0" is available & convenient],
and go ahead and put the special-case checks everywhere you need them
in the code. It's actually *faster* to test for an 8-bit immediate than
to compare against a full memory address. And there are really only a
few places that need to do the special-case test for NIL as a symbol.

I haven't yet made that change to CMUCL, but I did make it to my own
toy Lisp, QDL ("Quick & Dirty Lisp"), which uses the same low-tag scheme
as CMUCL... and originally used exactly the same NIL hack as well!!
Changing NIL from being a fixed-location oddly-aligned symbol to being
an "immediate" constant resulted in only a tiny speedup in my benchmarks,
but at least it *didn't* cause a slowdown! ;-}


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <http://rpw3.org/>
San Mateo, CA 94403

0 new messages