4 ms·
I've programmed a lot in various dialects of Lisp, and I like car and cdr. Using names like first and rest might make a language more attractive to new users, b
by montrose 9y ago
I've programmed a lot in various dialects of Lisp, and I like car and cdr. Using names like first and rest might make a language more attractive to new users, but there is something to be said for designing at least some languages for experienced users rather than new ones, and it doesn't take much experience (a week?) for the words car and cdr to mean the left and right halves of a cons cell to you. And once they do, they're better than first and rest, because
1. They're shorter.
2. They're the same length, which means in practice that a lot of code lines up in a way that's easier to read.
3. They look similar to one another, which telegraphs that they're part of the same set of list access tools. Code that munges lists looks like it munges lists.
4. Their meaning is precise: car just takes the first element of a list. Whereas something called first might reasonably be expected to also give you the first element of a vector, or a string.
5. When you're operating on lists as trees, the names first and rest are actively misleading. The car and cdr are the left and right subtrees, not the first and rest of something.
- jackmott 9y agofst rst
- kazinator 9y agoWhen you see car and cdr in code, it is usually a loud and clear signal that this module is doing something very concrete and explicit, with conses as a structuring material.
- dang 9y agoI agree. The argument against car/cdr reminds me of the adage 'in theory theory and practice are the same and in practice they're different'. car and cdr may have begun as a historical accident but they stuck around because they hold up in practice. As other commenters have pointed out, we could add to your list the classical point that car and cadr are composable, so cadr, cdar, etc. This creates a simple DSL for list manipulation which sometimes is just what's needed.
- kqr 9y ago> When you're operating on lists as trees, the names first and rest are actively misleading. The car and cdr are the left and right subtrees, not the first and rest of something. If by trees you mean 2-ary trees, sure. For any other n, #'first and #'rest are probably the correct generalisation.
- pcwalton 9y ago> Using names like first and rest might make a language more attractive to new users, but there is something to be said for designing at least some languages for experienced users rather than new ones, and it doesn't take much experience (a week?) for the words car and cdr to mean the left and right halves of a cons cell to you. When designing Rust, we quickly learned that the idea of picking short names to satisfy experienced users at the cost of new ones doesn't work in practice. Too many users complain that the language is too hard to learn and ugly; it took years for "Rust is so ugly, it's the resurrection of Perl" to finally stop being a meme. If we had stuck to our guns with short keywords, Rust might have been dead by now. Choosing unfamiliar syntax such as car and cdr worked for Lisp because, during the '70s, Lisp as a whole was novel enough to gain a sizable following. It doesn't work today. (And note that, even then, Lisp lost a lot of potential users due to the S-expression based syntax.) I'm firmly in the "first" and "rest" camp, because history has shown that readable languages much more frequently go on to be successful than languages with idiosyncratic syntactic choices.
- kbp 9y ago> car and cdr worked for Lisp because, during the '70s, Lisp as a whole was novel enough to gain a sizable following. It doesn't work today. (And note that, even then, Lisp lost a lot of potential users due to the S-expression based syntax.) I'm firmly in the "first" and "rest" camp first and rest are bad names for what car and cdr do, in the general case. If you have "Jenny" mapped to "867-5309" in a dictionary, then getting the "Jenny" part of that binding with first and the "867-5309" part with rest doesn't really make any sense. Similarly, if you have a node in a binary search tree, getting the subtree with the lesser elements with first and the subtree with the greater elements with rest also doesn't make any sense. Lisp, even in the 70s, uses first and rest as synonyms for car and cdr for the case where you are operating on a list. first and rest do not make sense as replacements for car and cdr in the general case, however, and if all you want is to have them as synonyms for when they do make sense, then Lisp has been providing that since the 70s.
- pcwalton 9y agoIf you want precise names to offer as aliases to "first" and "rest", then I think ML-style "head" and "tail" are better names than "car" and "cdr".
- vthriller 9y ago> They look similar to one another, which telegraphs that they're part of the same set of list access tools. …and probably allows for more typos and misreads during code review or something else. Luckily 'a' is distinct enough from 'd' visually so the difference between them is more noticable; imagine functions ending up with unfortunate names like cbr/cdr or car/cer…
- lvh 9y ago> 4. Their meaning is precise: car just takes the first element of a list. Whereas something called first might reasonably be expected to also give you the first element of a vector, or a string. That may be true in LISP, but it's not true in Clojure (which I assume is a fair paragon for the first/rest camp), where first works just fine on strings and vectors: => (first "abc") \a => (first [1 2 3]) 1 > 5. When you're operating on lists as trees, the names first and rest are actively misleading. The car and cdr are the left and right subtrees, not the first and rest of something. Firstly, trees can clearly branch out more than n=2. Then, nth becomes a much cleaner term than "left" and "right" which immediately limit you to 2 cases. But, let's say we're talking about n=2 trees. Consider: headmarc.core=> (second [1 2 3]) 2 And, of course, if that still seems unreasonable: (def left first) (def right second) ... but maybe what you really want is a zipper, which has everything you're asking for and more: https://clojure.github.io/clojure/clojure.zip-api.html#clojure.zip/left https://clojure.github.io/clojure/clojure.zip-api.html#cloju...
- kazinator 9y agoTXR Lisp, a dialect with true conses: 1> (car "abc") #\a 2> (cdr "abc") "bc" 3> (cddr "abc") "c" 4> (cdddr "abc") nil 5> (caddr "abc") #\c
- lvh 9y agoI'm not sure if that's what you meant, but the point I was addressing was that "first" is bad because you could reasonably expect it to work on anything sequentialish not just on conses (agreed), and car/cdr is explicitly about conses (agreed). But first, in some Lisps, works just fine on everything sequentialish, including vectors and strings, so I don't consider that a particularly strong argument. cl, cr I could live with, maybe. (consleft, consright). Other suggestions, head and tail, fst and snd, which have some of the other touted advantages. (I don't necessarily agree with them, but they also don't hurt, so...)
- kazinator 9y agocar and cdr is not necessarily about conses. I made them the basis for iterating over any list-like sequences in TXR Lisp. To participate in the protocol, an object can implement methods called cdr, car and nullify. This latter nullify should return nil if the object considers itself as representing "empty". Otherwise it can return the object itself, or possibly something else, like an ordinary list.