Live data from Hacker News

The empty list

tfeb.org

31–40 of 109 posts

Re: The empty list

#31
post #11
post #8

Earlier quoted context omitted.

I tend to agree but would be happy to be proven wrong by more knowledgeable folks in these comments. However I assume here we are talking about dotted lists and not ‘proper’ ones?

No. Dotted lists are just a notational convention. All lists can be written in dotted notation. You can define NIL as follows: (setf NIL (cons 0 0))) (setf (car NIL) NIL) (setf (cdr NIL) NIL) Then: (defun null (thing) (eq thing NIL)) You also have to add a bunch of special cases to other functions: (defun cl-style-consp (thing) (and (consp thing) (not (null thing))) (defun cl-style-symbolp (thing) (or (symbolp thing)…

You are conflating implementation tricks with language semantics. In Lisp, NIL is always an atom, never a cons.

Also, a dotted list is a nonempty list where the cdr of the last cons is not NIL. It is not a notational convention. A list (a b c) is not a dotted list, even if you write it as (a . (b . (c . nil))).

Re: The empty list

#32
post #30
post #5

TLDR (at the end): "Certainly the CL approach carries more historical baggage." > Combining the empty list and the special false object can lead to particularly good implementations perhaps. I suppose they wanted the null pointer to represent them both in RAM, because every CPU has an instruction to test whether a value is zero, or even a flag that is set for free when you encounter the value.

That's one reason. Another is that it allows you to use the idiom (if X ...) to test for non-null rather than (if (not (null X)) ...)

Indeed, although some languages achieve the same by defining multiple falsy values, e.g. JS has 9 such values: https://developer.mozilla.org/en-US/docs/Glossary/Falsy

Re: The empty list

#33
post #4

> It is necessary that there be a special empty-list object which is a list but not a cons. That is not true. NIL could be a CONS. In fact, it acts just like a CONS whose CAR and CDR are itself. The only way in which NIL does not behave like a CONS is that it does not answer true to CONSP. But that doesn't matter because it answers true to NULL, so you can build a CL-style CONSP if you want it by checking for (AND (C…

If NIL were a CONS, how would you distinguish between an empty list and a list of length one containing NIL?

Re: The empty list

#34

It's very difficult to deserialize in serialize things like yaml and json in CL because it doesn't have a distinct false object. When you do deserialize `false` it turns into nil. Then when you serialize it back again it turns into `null`. From a practical perspective, this is annoying. I don't hate using it as a language, it's fine, but because it's different enough than other languages it confuses people.

> it's different enough than other languages it confuses people.

And javascript integers (or lack thereof) don't match CL integers. So if you want to interoperate between distinct languages you do have to consider the differences anyway.

Re: The empty list

#35

It's very difficult to deserialize in serialize things like yaml and json in CL because it doesn't have a distinct false object. When you do deserialize `false` it turns into nil. Then when you serialize it back again it turns into `null`. From a practical perspective, this is annoying. I don't hate using it as a language, it's fine, but because it's different enough than other languages it confuses people.

I agree that it’s annoying. It is possible to work around without too much trouble by defining some other constant that evaluates to nil to distinguish.

Re: The empty list

#36
The Null Object[1] is a useful thing. It can represent the behavior when there's no correct behavior. It exists in languages like SmallTalk as well as LISP and LISP-derived languages.

In Go, you can call methods on nil objects. [2]

1 https://wiki.c2.com/?NullObject

2 https://go.dev/play/p/mgayNTjdacL

Re: The empty list

#37
post #33
post #4

> It is necessary that there be a special empty-list object which is a list but not a cons. That is not true. NIL could be a CONS. In fact, it acts just like a CONS whose CAR and CDR are itself. The only way in which NIL does not behave like a CONS is that it does not answer true to CONSP. But that doesn't matter because it answers true to NULL, so you can build a CL-style CONSP if you want it by checking for (AND (C…

If NIL were a CONS, how would you distinguish between an empty list and a list of length one containing NIL?

The same way you do now: (NIL) would not answer true to NULL. And it would not be EQ to NIL.

Re: The empty list

#38

It's very difficult to deserialize in serialize things like yaml and json in CL because it doesn't have a distinct false object. When you do deserialize `false` it turns into nil. Then when you serialize it back again it turns into `null`. From a practical perspective, this is annoying. I don't hate using it as a language, it's fine, but because it's different enough than other languages it confuses people.

Serialization formats not designed specifically for the language should not be treated as serializers for objects in that language.

That is, JSON is not a representation of objects in your language. It's your application that holds a representation of JSON data. If you're not paying attention to the translation layer between your native objects and the JSON ones, you'll hit problems sooner or later.

Re: The empty list

#39
post #11

Earlier quoted context omitted.

No. Dotted lists are just a notational convention. All lists can be written in dotted notation. You can define NIL as follows: (setf NIL (cons 0 0))) (setf (car NIL) NIL) (setf (cdr NIL) NIL) Then: (defun null (thing) (eq thing NIL)) You also have to add a bunch of special cases to other functions: (defun cl-style-consp (thing) (and (consp thing) (not (null thing))) (defun cl-style-symbolp (thing) (or (symbolp thing)…

You are conflating implementation tricks with language semantics. In Lisp, NIL is always an atom, never a cons. Also, a dotted list is a nonempty list where the cdr of the last cons is not NIL. It is not a notational convention. A list (a b c) is not a dotted list, even if you write it as (a . (b . (c . nil))).

> You are conflating implementation tricks with language semantics.

No, I'm not.

> In Lisp, NIL is always an atom, never a cons.

That depends on what you mean by "atom". If by "atom" you mean something that answers true to the ATOM predicate then yes, NIL is an atom. But if by "atom" you mean something that produces an error if you try to call CAR or CDR on it then no, NIL is not an atom, it is equal to (CONS NIL NIL) because (CAR NIL) and (CDR NIL) are both NIL.

So the result of (ATOM NIL) is an arbitrary choice. And CL arguably got it wrong because it fails to preserve the invariant that if (ATOM X) is true then (CAR X) and (CDR X) will produce errors.

[UPDATE]

> a dotted list is a nonempty list where the cdr of the last cons is not NIL

But this is just terminology. In CL, NIL behaves exactly the same as (NIL . NIL) with respect to CAR and CDR.

Indeed, it is exactly this confusion that is the basis for a lot of criticism of CL's design. In Scheme, the empty list is unambiguously atomic: trying to take the CAR or CDR of the empty list in Scheme is an error, as with any other atom.

Re: The empty list

#40
An annoying thing about `nil` is when it stomps a namespace in which you're using symbols, such as in a minilanguage, by being read as the nil value rather than as the symbol `nil`.

Besides it being obvious, I actually ran into this in a real-world situation, with some super-rapid work on a dataset of only about 100 entries, where it turned out one user's real name had the initials N.I.L.

Post reply on HN