←back to thread

Hofstadter on Lisp (1983)

(gist.github.com)
372 points Eric_WVGG | 2 comments | | HN request time: 0.389s | source
Show context
susam ◴[] No.41861244[source]
> Attempting to take the car or cdr of nil causes (or should cause) the Lisp genie to cough out an error message, just as attempting to divide by zero should evoke an error message.

Interestingly, this is no longer the case. Modern Lisps now evaluate (car nil) and (cdr nil) to nil. In the original Lisp defined by John McCarthy, indeed CAR and CDR were undefined for NIL. Quoting from <https://dl.acm.org/doi/pdf/10.1145/367177.367199>:

> Here NIL is an atomic symbol used to terminate lists.

> car [x] is defined if and only if x is not atomic.

> cdr [x] is also defined when x is not atomic.

However, both Common Lisp and Emacs Lisp define (car nil) and (cdr nil) to be nil. Quoting from <https://www.lispworks.com/documentation/HyperSpec/Body/f_car...>:

> If x is a cons, car returns the car of that cons. If x is nil, car returns nil.

> If x is a cons, cdr returns the cdr of that cons. If x is nil, cdr returns nil.

Also, quoting from <https://www.gnu.org/software/emacs/manual/html_node/elisp/Li...>:

> Function: car cons-cell ... As a special case, if cons-cell is nil, this function returns nil. Therefore, any list is a valid argument. An error is signaled if the argument is not a cons cell or nil.

> Function: cdr cons-cell ... As a special case, if cons-cell is nil, this function returns nil; therefore, any list is a valid argument. An error is signaled if the argument is not a cons cell or nil.

replies(6): >>41861327 #>>41861751 #>>41862379 #>>41862873 #>>41862933 #>>41868929 #
sph ◴[] No.41861751[source]
Sadly this is not the case with Scheme and it makes for very unergonomic code, especially for a newbie like me.

Which is a shame, because I prefer (Guile) Scheme to Common Lisp.

replies(2): >>41862075 #>>41863892 #
pfdietz ◴[] No.41862075[source]
I'm very tied to Common Lisp, but I'm perfectly fine with the idea of a lisp in which car and cdr would be undefined on nil. Also, I'd be fine with a lisp in which () is not a symbol. I don't think these features of Common Lisp are essential or all that valuable.
replies(1): >>41862909 #
sph ◴[] No.41862909[source]
They are not essential, but they make code that operates in lisp more compact and pleasant to write.

In Scheme my code is littered with

  (if (null? lst)
      ;; handle empty case here
      ...)
Simply because otherwise car throws an error. This whole section is often unnecessary in CL.
replies(2): >>41863303 #>>41867459 #
tmtvl ◴[] No.41863303[source]
But you need to handle the empty case anyway otherwise you process nils ad infinitum.
replies(1): >>41865061 #
erik_seaberg ◴[] No.41865061[source]
You can say

  (if lst
    ...)
if the empty list is falsy, but Scheme eventually chose to add #t and #f. Oddly #f is the only false value but #t is not the only true value.
replies(1): >>41867448 #
1. tmtvl ◴[] No.41867448[source]
I do prefer nil being the false value as well as the empty list, even if it makes it more awkward to distinguish between 'there is a result, but the result is an empty list' and 'there are no results'. But that has nothing to do with car and cdr in Common Lisp treating nil as though it were `(cons nil nil)'. The only value in that I can see is would be if rplaca and rplacd can do some useful things with that (so `(setf (car symbol-that-currently-points-at-nil) foo)' and `(setf (cdr stcpat) bar)' do those useful things).
replies(1): >>41870450 #
2. waterhouse ◴[] No.41870450[source]
An oldie: https://ashwinram.org/1986/01/28/a-short-ballad-dedicated-to...

Describes the evolution from:

  (cdr (assq key a-list))
to:

  (let ((val (assq key a-list)))
     (cond ((not (null? val)) (cdr val))
           (else nil)))