1. 23
  1. 6

    I truly hate the fact that nil stands for “the empty list”, “false”, “undefined result” and “error” in Common Lisp. Its the worst.

    1. 5

      It really works out well in my experience. Also, as alexandria points out, errors in Lisp are typically represented with conditions, not NILs.

      FWIW, I truly hate that Scheme separates #f, NIL and ’().

      1. 4

        I truly hate that Scheme separates #f, NIL and ’()

        I don’t like it either. But:

        1. Scheme has no NIL. (I mean, you could have a symbol named ‘nil’, but it doesn’t mean anything in particular.)

        2. The bigger wart (imo) is that () is not self-evaluating.

        1. 1

          In my scheme programs I always denote the empty as (list).

        2. 2

          If you look at this from a non-Lisp perspective, it makes a lot of sense that an empty list is not the same thing as a boolean false value, just like an empty string is not the same thing as zero.

          nil being the empty list and the universal falsy value is entirely a Lisp thing.

          1. 2

            Not even a Lisp thing but a MacLisp thing (or maybe even a Lisp 1.5 thing?) which got inherited by MacLisp descendants Common Lisp, InterLisp, Emacs Lisp, etc.

          2. 1

            (cadr nil) is an error by almost any conceivable standard1 but it results in nil.

            I admit that in practice it typically doesn’t cause trouble, but it drives me crazy that this is somehow an allowed thing you can say in common lisp:

            (caddr (= 3 10))


            1 Ok I’m being a little salty here.

            I know its a silly thing to get caught up on but it just feels wrong to me.

            1. 2

              Ok but False is NIL, because (= 3 10) is NIL. The empty list is also NIL. Taking the CAR of the CDR of the CDR of an empty list, returns an empty list. Since CDR returns an empty list (NIL) when given an empty list (Because the CDR of the list is empty) and since CAR gives NIL when given an empty list (Because the value of the CAR is NIL), it’s just a byproduct of the chain of evaluation. None of these are error conditions in and of their own right, so the result is not an error condition either.

              I know you understand this, and still feel that this should be an error (which I can understand except for the fact that it would make the LISP interpreter more complex to implement), I wrote this for the people who are reading this discussion.

              1. 3

                it would make the LISP interpreter more complex to implement

                (disassemble (lambda (x) (car x)))
                ; disassembly for (LAMBDA (X))
                ; Size: 31 bytes. Origin: #x53571660                          ; (LAMBDA (X))
                ; 60:       498B5D10         MOV RBX, [R13+16]                ; thread.binding-stack-pointer
                ; 64:       48895DF8         MOV [RBP-8], RBX
                ; 68:       8D50F9           LEA EDX, [RAX-7]
                ; 6B:       F6C20F           TEST DL, 15
                ; 6E:       7403             JEQ L0
                ; 70:       CC49             INT3 73                          ; OBJECT-NOT-LIST-ERROR
                ; 72:       00               BYTE #X00                        ; RAX
                ; 73: L0:   488B50F9         MOV RDX, [RAX-7]
                ; 77:       488BE5           MOV RSP, RBP
                ; 7A:       F8               CLC
                ; 7B:       5D               POP RBP
                ; 7C:       C3               RET
                ; 7D:       CC10             INT3 16                          ; Invalid argument count trap
                
                1. Compiler, not interpreter

                2. It already requires a type check; it would complicate nothing to make (car nil) an error

            1. 1

              Yes, I programmed in CL for a few years at a startup. Quite familiar with the language. See my response to rau.

              1. 2

                I did, and I disagree that that represents an error. It’s correct behaviour of the system. If False was represented by a separate value, or Lisp was more strict with types, then sure it would be an error. But since any false expression returns an empty list, and since the interpreter is not literally doing pointer lookups, it is not in any way an error.

                1. 2

                  The issue with me is that there are things which you can express as valid programs which run and even produce values which don’t really make sense at a type level. I know its impossible for mathematical reasons that a programming language prevent you from denoting nonsense statically (and I’m not even really sold on the idea that such static guarantees are that useful or ergonomic) but I do sort of like when a language at least stops when something silly has happened.

                  If one sees the expression (cadr x) its pretty reasonable to expect that x denotes a list. It also seems reasonable that x is a non-empty list. But CL doesn’t guarantee either of those things are true. It doesn’t necessarily bother me too much that cadr returns a value instead of throwing an error when it receives a list which doesn’t have a cadr, but nil seems to me to be the wrong value. Why?

                  Because:

                  (cadr nil)
                  (cadr (list 'a nil))
                  

                  Return the same value. In other words, if you have a list with empty sublists (which doesn’t even seem particularly unusual a situation) then you can’t count on cadr to tell you about the cadr of the list! It would be better of cadr was called maybe-cadr and it returned a user provided sentinel value (default nil, perhaps) in the event of a list without a cadr.

                  All this goes back to the idea that weird circumstances should halt programs as quickly as possible. Given that many of these list destructuring functions fold nil, its possible, and I have even encountered situations where, an unusual condition doesn’t raise a real error until long after the offending list is off the stack and beyond the debugger.

            2. 1

              nil punning ftw… until it is null pointer error.