Comment by brudgers
8 years ago
A sometimes overlooked feature of car and cdr is composibility, e.g. caadr, cddr, etc. Though I don't think it is useful to go ten deep as the spec requires if people are reading the code, deep composition may make some sense for machine written code.
Car and cdr also reflect a sometimes misunderstood aspect of Lisp: lists are an abstraction for sequential memory addresses in the Von Neumann architecture. The sense in which Lisp was designed for functional programming only goes about as far as one can pragmatically throw the lambda calculus. Practically and historically speaking Lisp programming was only slightly less about mutation than any other language. Design for current state of the art (in the 70's, 80's and 90's) is why Common Lisp has destructive versions of everything useful. Heck, Lisp even mutates its source code.
At the end of the work week, the major language design choice is not so much between car/cdr and first/rest it's between the language having a concept of first/rest and not having one: e.g. Python, Javscript, Go[?], C, and so on.
Finally, car/cdr is not that much worse than first/rest for all those programmers who are not fluent in English. Naming things is hard mostly because names are arbitrary. Both car/cdr and first/rest require the harder concept of fixed order data...and next makes more sense than first if one applies the stream abstraction.
> A sometimes overlooked feature of car and cdr is composibility, e.g. caadr, cddr, etc. Though I don't think it is useful to go ten deep as the spec requires if people are reading the code, deep composition may make some sense for machine written code.
The 90% case here is "second", "third", etc. For more exotic cases, surely there are other naming conventions that would be more readable. You could use "h" and "t" for head and tail, or "l" and "r" for left and right...
> At the end of the work week, the major language design choice is not so much between car/cdr and first/rest it's between the language having a concept of first/rest and not having one: e.g. Python, Javscript, Go[?], C, and so on.
The "concept of first/rest" is just the concept of pairs, which are a special case of tuples, which Python and JS certainly have.
Erlang uses a {H|T} pattern matching convention in lieu of language keywords. For what it is worth (limit x | x -> 0), I think Erlang is an exceptionally elegant piece of engineering as engineering. But it's not the 90% use case. Then again, neither is Lisp, ML, or Rust. The 90% use case these days is Javascript. Which I would hesitate to associate with the phrase "elegant piece of engineering" without an "in" prefix.
Python and Javascript may have pairs. They just live at the bottom of a Turing Tarpit.
I rarely used Python before the last few months, but I've already stumbled upon a great tragedy in the 3.x migration.
Although few know it, and fewer use it, Python 2.x allowed tuples to be unpacked in function heads, like Erlang. Since my last job was 100% Erlang, I stumbled into using it without knowing any better.
I decided a couple of months later that since it was a new project, I really should convert to Python 3, whereupon I discovered that feature had been removed due to parsing complexity and "because no one uses it."
Sigh.
2 replies →
> The 90% case here is "second", "third", etc.
In addition to first and rest, Lisp has provided second, third, fourth, etc since the 70s, as well as the more general nth/elt which just takes the index as an argument.
> there are other naming conventions that would be more readable. You could use "h" and "t" for head and tail, or "l" and "r" for left and right
Why would you bother replacing names with 60 years of precedent with h and t so that instead of cdaddr you could write thtt? I think cdaddr nonsense is illegible and very bad style, but I don't see how someone could seriously suggest writing thtt as a good solution to that readability problem. The real solution is to use named accessors instead of ad-hoc pointer chains.
> The 90% case here is "second", "third", etc.
Unfortunately, those cases exhibit poor style if they are mixed with cddr. For instance, I would never write this:
If we checked that cddr is a cons, we then want to access caddr (its car) or cdddr (its cdr).
Those first, second, rest and whatnot are really geared toward when the structure is a (proper!) list. If it really is just a proper list and we just want to be sure it has a third element, it would be more consistent to just do
Basically stick to one way or the other.