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 only one, One, ONE!!, One, one symbol which is CL:NIL
* jimka <d6bccc83-d83e-4f44-aeb6-106fb1817...@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"
> There is only one, One, ONE!!, One, one symbol which is CL:NIL
> * jimka<d6bccc83-d83e-4f44-aeb6-106fb1817...@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"
> 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.)
Forgetting to bring in symbols from the :cl package is hardly perverse,
let alone extremely perverse:
*** - 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
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!
> 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:
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.
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.
>> * jimka<d6bccc83-d83e-4f44-aeb6-106fb1817...@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.
> 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.
>> 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.
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. :)
> 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.
>> 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.
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. :)
> 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}
Tim Bradshaw <t...@tfeb.org> wrote:
+---------------
| 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.
+---------------
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 <r...@rpw3.org>
627 26th Avenue <http://rpw3.org/>
San Mateo, CA 94403