3 ms·
> I dont think it's obfuscated at all. Not deliberately obfuscated, true. But any code that contains tokens like "CAR", "caar", "CDR", "cdar", "CONS", "caddr",
by p-e-w 4y ago
> I dont think it's obfuscated at all.
Not deliberately obfuscated, true. But any code that contains tokens like "CAR", "caar", "CDR", "cdar", "CONS", "caddr", etc doesn't belong in a modern production environment where auditability and maintainability are paramount. What's the goal here, shortest possible text? Why not just replace each of those with a Unicode Greek letter and save two bytes and three characters each?
If that means Lisp itself is the problem, so be it. The assumption that "software engineers" in the late 1950s somehow designed the holy grail of programming languages is laughable anyway.
- ctrlmeta 4y ago> Not deliberately obfuscated, true. But any code that contains tokens like "CAR", "caar", "CDR", "cdar", "CONS", "caddr", etc Do you code in Lisp? I do and I don't find it obfuscated at all. On the other hand I find it absolutely clear and beautiful. car, caar, cdr, etc. are no more obfuscated than chmod, chown, cp, ls, etc. in shell. If you are unfamiliar with Lisp, sure they would seem obfuscated but so would those commands in shell to someone who has never done shell. But after using the shell for a while those commands become clear, concise and convenient. So do car, cdr, caar, etc. "cons" just constructs a cell. (cons "foo" "bar") constructs a cell with "foo" as its car (first element) and "bar" as its cdr (second element). It can't get simpler than that!
- duped 4y agoLISP is beautiful with terrible naming conventions, and I don't think that's a hot take. "caddr means car of the cdr of the cdr" is almost intentionally obtuse for describing the third element of a list. > It can't get simpler than that! It can, because in your description you introduce multiple levels of indirection.
- kazinator 4y agoIf we have obj which holds ((x (y . z))), cdadar is a nicer way to get the z than (rest (first (rest (first obj)))). What would improve things would be to have the mirror image functions: rac rdc raac rdac radc rddc and so on. We might as well drop the final c, too. These would read out the traversal from left to right. (radad obj) would take the car, then cdr, then car, then cdr, left to right. This would be better in a left-to-right threading macro: (--> (whatever) radad (+ 3) ...) because it would be equivalent to (--> (whatever) car cdr car cdr (+ 3) ...) the expansion to car/cdr is in the same order.
- duped 4y agoHow about obj[2]?
- ctrlmeta 4y agoWhat abomination of a syntax is that! What does [ and ] do? Why does the 2 go between the brackets? What does a[2] do? Is it making a function call? It is a statement? An expression? Or some other magic? Why deliberately create such cryptic and confusing syntax? Why not simply (nth 2 obj) which tells clearly that this is a function call that picks the 2nd element in obj and returns it? Seriously though, arguing about syntax to criticize a language is such a trivial and banal activity that it detracts from any useful discussion about the true merits of a language, about its expressiveness, flexibility and paradigms it supports. Syntax is superficial (and Lisp has very little of it anyway). What syntax someone likes depends on what syntax they are familiar with. I am familiar with Lisp syntax (the very little there is of it) and C, C++, Python, Java, JavaScript, Go, Rust, APL, etc. and I find Lisp syntax the most intuitive and clear. You might find the C/Java syntax clearer and more intuitive. And that is why a discussion about syntax is superficial and pointless. Semantics and paradigms make much better topics of discussion when talking about programming languages.
- kazinator 4y agoYou might want indexing for linear list access. Here we have z in the dotted position of a nested list, which is tricky to connect to a simple index. Even if we set nesting aside, our z could be in these positions: z ;; z is the object itself (z) (y . z) (y) (x y . z) (x y z) and so on. Numeric navigation of the tree structure is possible in various forms. TXR Lisp has the functions cxr and cyr which use a bitmask "address" argument to navigate the car/cdr links. The address 0 is invalid, 1 is a noop. Other than that, bit 1 means car and 0 means cdr. cxr and cyr differ in the order of the bits. 1> (cxr #b10101 '((x (y . z)))) z I.e. 1> (cxr 21 '((x (y . z)))) z To interpret the bitmask, we ignore the upper framing bit and follow the 0101 right to left: 1=car, 0=cdr, 1=car, 0=cdr. Of course, this just being a function argument, it can be computed. Any positive integer is a potentially valid address in some cons structure. Indexing is supported, but doens't reach dotted positions; it is geared toward sequences, not cons structure. 2> [[['(a b (c d e (f))) 2] 3] 0] f That could be compressed into some kind of path mechanism where you just have the indices as 2 3 0. The numeric address idea is inspired by the tree addressing system in Urbit's Nock. This is explained as a layer-by-layer enumeration using successive natural numbers: 1 2 3 4 5 6 7 Address is 1 is "this object". Then 2 is car, and 3 is cdr. Note that these have a binary structure 10 and 11. Similarly 4, 5, 6, 7 are 100 101 110 and 111. You can see that the bits mean 0=car 1=cdr, left to right, after skipping the framing 1 bit. I swapped their meaning in my implementation and provided a right-to-left function. I don't remember the exact reason I swapped the roles, but likely it was because linear lists are more common than nested lists. You do more cdr than car, loosely speaking. For instance, the simple positions in a linear list are all cdr operations followed by car: car, cadr, caddr, cadddr, ... if cdr is 0, then these look like 11, 110, 1100, 11000: 40> (cxr #b11 '(1 2 3 4 5 6)) 1 41> (cxr #b110 '(1 2 3 4 5 6)) 2 42> (cxr #b1100 '(1 2 3 4 5 6)) 3 43> (cxr #b11000 '(1 2 3 4 5 6)) 4 44> (cxr #b110000 '(1 2 3 4 5 6)) 5 In other words, 3 is car, and then we just arithmetically left shift the 3 to get the other positions. If cdr is 1, then we have to left shift and mask in a 1 to replace the 0 that was shifted in.
- kazinator 4y agoWhere are you getting your idea of what belongs in a production environment, in the context of Lisp? The accessor functions like caddr should probably be avoided in code which is using cons cells as linear lists. If someone is writing a server that pops requests from a list, and I see caddr in that code, I will probably flag that in the review. The functions are useful in code that manipulates nested cons cell structure, and it's important to be familiar with them. I'd prefer destructuring or pattern matching to be used. Some Lisp dialects don't have a pattern matching library included; it requires third party code. Destructuring can be cumbersome because it has a variable in every position of the pattern. Say we have a variable called obj which holds something like (1 ((2 3)) ...) and we are interested in extracting the 3, and don't care about the other values. Destructuring can be cumbersome; e.g. (destructuring-bind (a ((b c)) &rest r) obj c) ;; a, b and r are ignored Here, there may be excellent justification in just doing (cadaadr obj), if you happen to have the function to that depth. You likely don't, so a break-up into two is required such as (cadar (cadr obj)) and that may even be more readable, since "cadar" and "caddr" are recognizable idioms. In some lower level parts of a Lisp implementation, you may not have any pattern matching or destructuring available, due to the bootstrapping chicken-and-egg problem: the code is helping to implement that. The meta-circular interpreter is like that. It could use destructuring, but then the goal is for the meta-circular interpreter to be able to handle all the constructs that are used in its own code; thus a definition of destructuring would be required, making it a lot larger.