4 ms·
The Problem with Lisp
- Tomte 8y agoI don't understand the author. The evaluation order in Lisp is not "highly unpredictable", it is dependent on the form. I don't understand why he even constructs this strawman of "you must start in the innermost sub-form". That's not a rule that anyone ever made. That's the rule for arithmetic expressions, sure, but what does that have to do with Lisp? The rule in Lisp – as he later admits – is "look at the first symbol in the (outer) form". And the whole point of special forms is to make your own rules about evaluations. In order to help the programmer. Can you obfuscate the meaning? Hell, yes! Is that a reason not to allow it and to force obfuscated code by using unsuitable evaluation orders everywhere? Hell, no! And even in this bad example Lisp is certainly not more unpredictable than any other language. Let's take C: int func(int a, int b) { return a + b; } If you start at the innermost point "a + b" you get exactly the same questions: "Oh gosh, what is the + operator?", "How do I apply + to a number?" (really?) and "where do I store the result?" I think the author has misled himself into some extreme notion of the divine simplicity and purity of Lisp that nobody ever claimed to exist.
- heinrichhartman 8y agoAuthor here. Thanks for your comment. I appreciate your feedback! Let me first comment on your C example: > "a + b; // Oh gosh, what is the + operator?" There is no problem with the "+" operator. It's a language builtin. You can look that up in the manual. In the Lisp case, I did not pick on the "+" operator, at all. The problem is a line below: > "(sum x) ;; ??? Where does "sum" come from?" There is no function "sum" at all! It just looks like a function call, which is it not. You have to know what the enclosing special form "let" does to it's arguments. You could have those problems with C macros as well, but not in this example. > Can you obfuscate the meaning? Hell, yes! > Is that a reason not to allow it and to force obfuscated code by using unsuitable evaluation orders everywhere? Hell, no! I am not picking sides here. I am pinpointing a source of confusion. The Python/Ruby authors took another path and made the language less powerful, so they don't give developers the power to obfuscate the code in this way.
- heinrichhartman 8y agoReading this again, I realize that the variable name "sum" was chosen badly. I'll change that in the post.
- lispm 8y agoNeither Ruby nor Python support the idea of 'code is data'. Sure they are simpler. The concept of code is data is not about simplicity, it's there to support code translations. Most languages have constructs which don't behave like function calls and which have special evaluation rules. A simple example is the IF statement. It takes a condition and two expressions. Depending on the condition only one of the expressions will be evaluated. If you look around, you'll find a bunch of such constructs in most languages. Lisp also has such an IF operator. Lisp additionally allows us to write any amount of additional operators which have evaluation rules, which are not the evalation rule of a function call. Is the IF statement irregular? It isn't. It's just one of many operators which does not follow the evaluation rule of a function call. Something like Ruby has a lot of weirdness in its calling mechanism. Python has strange non-local transfers going on. There are lots of ways we can perceive 'irregularities'.
- heinrichhartman 8y ago> Neither Ruby nor Python support the idea of 'code is data'. Well, Python and Ruby have an "eval" statement. So "code as data" is supported, but discouraged. In contrast Lisp macros / "code as data" are commonly used. Why is there a difference in culture? Is the only reason, that strings are a lot harder to manipulate than lists? Is there more to it? Maybe translated code is harder to maintain and reason about? Maybe debugging and profiling is a lot harder (this is true for C macros at least). I have just shared one confusion that I was struggling with. I found python and ruby much easier to read and pick up. One reason for this is, that programmers are not allowed to define new irregularities. Maybe this insight is helpful for others, who are getting Lisp as well. At least this was my motivation for writing this post.
- 8y ago
- kazinator 8y agoThe evaluation order in ANSI Lisp is well-defined. In some Lisp-like languages such as Scheme, evaluation of function arguments is unspecified, similarly to C.
- lispm 8y agoIt's a bit like claiming that riding a bicycle is hard because steering, moving forward and keeping balance at the same time is too difficult. Yet, people learn it and do it just fine. They just don't think consciously about it. Riding a bicycle takes a bit of training but after that the body movements are virtually effortless. Lisp is slightly more difficult than some other languages because lists/symbols are both used in code and data, because there are a bunch of different types of lists, and because code is a token tree. (defun silly (a b c) (let ((my-sum (+ a b c))) (* my-sum (+ 1 my-sum)))) Now what's wrong with that? Nothing. But people don't write that. Lisp is written like that: (defun silly (a b c) (let ((my-sum (+ a b c))) (* my-sum (+ 1 my-sum)))) There are a limited amount of tokens on a line. Syntactic constructs are not one line. Any definition has this pattern: (DEFsomething name ...) Oh, it begins with DEF, then it is a macro, followed by the name. Function definitions have this pattern: (DEFun name arglist body) The code layout is: (defun name arglist body ...) A method defintion: (DEFmethod name arglist body ...) The difference between a function and a method is in the arglist. LET is: (LET binding-list body ...) Now you need to know what a binding list is: ((arg0 value0) (arg1 value1) ... (arg2 value3)) Such a binding list is used in other places, too. If you give shortcuts to it: df function definition bl binding list nm name bd body al arglist DEFUN: df nm al bd LET LET bl bd PROG prog bl bd lambda LAMBDA al bd DESTRUCTURING-BIND DESTRUCTURING-BIND al form bd BLOCK BLOCK nm bd CATCH CATCH nm bd etc. Basically much of Lisp consists of a small number of basic patterns which are being combined. lists, assoc lists, property lists, arglists, binding lists, body, ... You've learned then a new alphabet of visual/structural patterns. Beyond that, code reading gets intuitive. Names give a clue what kind of pattern combination follows. The IDE can then help with syntax highlighting, syntax documentation display, etc. Lisp can even show you useful code layout: CL-USER 68 > (let ((*PRINT-RIGHT-MARGIN* 30)) (pprint '(defun silly (a b c) (let ((my-sum (+ a b c))) (* my-sum (+ 1 my-sum)))))) (DEFUN SILLY (A B C) (LET ((MY-SUM (+ A B C))) (* MY-SUM (+ 1 MY-SUM))))
- heinrichhartman 8y agoThanks for this elaboration! Obviously there is lot of community know-how, that I am not aware of. Thanks for explaining them in detail. Learning to spot all those pattern is crucial if you want to be come effective at reading lisp. For the untrained eye lisp code often looks horrid. I don't think we disagree, though. All I am saying is that: "Riding the Lisp bicycle has been difficult for me. Here one thing I struggled with a lot." Maybe other people have the same difficulty and find this helpful.