And, Why Didn't Dijkstra Like Lisp?
kazimirmajorinc.blogspot.com
kazimirmajorinc.blogspot.com
http://userweb.cs.utexas.edu/users/EWD/transcriptions/EWD03x...
"The third project I would not like to leave unmentioned is LISP, a fascinating enterprise of a completely different nature. With a few very basic principles at its foundation, it has shown a remarkable stability. Besides that, LISP has been the carrier for a considerable number of in a sense our most sophisticated computer applications. LISP has jokingly been described as "the most intelligent way to misuse a computer". I think that description a great compliment because it transmits the full flavour of liberation: it has assisted a number of our most gifted fellow humans in thinking previously impossible thoughts."
http://userweb.cs.utexas.edu/users/EWD/transcriptions/EWD12x...
"I must confess that I was very slow on appreciating LISP's merits. My first introduction was via a paper that defined the semantics of LISP in terms of LISP, I did not see how that could make sense, I rejected the paper and LISP with it. My second effort was a study of the LISP 1.5 Manual from MIT which I could not read because it was an incomplete language definition supplemented by equally incomplete description of a possible implementation; that manual was so poorly written that I could not take the authors seriously, and rejected the manual and LISP with it. (I am afraid that tolerance of inadequate texts was not my leading characteristic)."
Thanks for the heads up!
It'd be an error to modify code in Common Lisp anyway. But presumably, back when Lisp was mostly interpreted, it would have been possible. I wonder how much that was used.
Hey, in a dynamically-scoped, interpreted Lisp, would it be possible to simulate a closure by modifying code? Assuming that a list whose CAR is 'LAMBDA is a proper function, then one could simply replace the closed over variables with SUBLIS, at run-time. Then add in a level of indirection to get mutability. That'd be horribly slow, granted.
"[the language] will be more like a declarative description of the facts about a goal and the means available for attaining it than a program per se."
It sounds like he's talking about Prolog.
This seems to take a similar approach, by simplifying (once you understand it) the programming method, and by doing so, drastically reducing the LOC needed for a functional system.
Of course, most of the time, you won't need most of that syntax. And, it does have the advantage of mostly being defined in Common Lisp and being fairly consistent.
But parentheses are definitely not the only punctuation in Lisp.
How do you express any of the following constructs using only parentheses and symbols?
#:foo #.(foo) #+ FOO (bar) #- FOO (baz)
For the original Lisp, parentheses and symbols were all that was necessary. For more recent derivatives, that is not necessarily the case.
#+FOO (bar)
This could almost be: (when (member FOO features) (bar)) Just wrap it in an eval-when.
However, the code is data idea seems genius to me.
Most of the criticisms seem subjective, perhaps McCarthy and Dijkstra had different problems to solve and thus chose different tools.
(Mea culpa: every now and then when I use a goto)
I wonder what Dijkstra would have thought about this: grep -R goto linux-2.6.34
Come up with a better way of representing an Abstract Syntax Tree in text, please.
> However, the code is data idea seems genius to me.
You know, I think that the reluctance to use functions as values in programming by some of the early luminaries is that functions as values in Mathematics just isn't considered as "normal" outside of fields concerned with the fundamental basis of mathematics. Since the computer itself doesn't need to be described nor does it necessarily benefit from those terms, it doesn't immediately take hold that programs running on computers are a lot easier to reason about once you've got functions being treated as values.
I mean, take differentiation and integration: fundamentally, those are two functions that you can run on another function to get a third function as a result. Does it ever actually get taught that way? I mean, is the implication of that really remarked upon? You go over the fundamental theorem of calculus in about twenty minutes in the first introduction to it, and then bam, here's all the shortcuts for differentiating. No reflection on just what the implications of that are.
Abstract algebra is the subject dedicated to that notion in general, category theory the one that, it would seem, is explicitly dedicated so.
If in programming what we're really doing is telling the compiler stuff, and the compiler only sees an abstract syntax tree, traditional syntaxes are a way of representing an AST in text, and commonly considered to be "better" by the particular language's adherents (Ruby programmers like Ruby syntax, Java programmers like Java syntax, etc.). Sure, you don't get to apply proper macros, but Java/Ruby/etc. programmers don't care about that. What representation is "better" really boils down to taste and opinion.
After all, if you believe that, then Python is a Lisp too[1]. And when people say "Lisp", they don't mean "Scheme and CL and [...] and Python".
Sure, like any class of languages, there is not a single syntax. But every lisp has a syntax. And the languages people associate with the class "Lisp" use parens. And in many peoples' opinions, there are better ways of representing an AST. Like indentation, for example. Or curly brackets.
> Python supports all of Lisp's essential features except macros, and you don't miss macros all that much because it does have eval, and operator overloading, and regular expression parsing, so some--but not all--of the use cases for macros are covered.
Macros are a huge part of Lisp. Were they not, I don't think you'd see Lisp programmers defending the bare bones not-syntax so fiercely.
For what it's worth, if you get good at a language, you start decomposing things into trees yourself when you're faced with some tricky code.
*Actually, I'm not 100% sure that this is true - it would be more accurate to say that I am not aware of any other language that can be used to produce an arbitrary s-expr, but that such may indeed exist. If anyone can correct me on this point it would be much appreciated :-)
Actually, no. They're a way of unambiguously representing said AST. The just happen to be a lousy way of doing so. Let's see why.
> and commonly considered to be "better" by the particular language's adherents
Interestingly enough, almost none of those adherents actually know their favorite language's syntax.
Why do I say that? Because very few people know the full precedence of expressions. So, they break expressions in unnecessary places and/or add redundant parentheses.
And, the few folks who actually do know their language's syntax know better than to actually use that syntax because they know that the person reading their code doesn't know it, so they, the experts, are forced to over-parenthesize.
> What representation is "better" really boils down to taste and opinion.
And then there's the problem of manipulating said AST. To do so for a traditional language requires a parser and a bunch of datastructures, which every project reimplements. Everything that you need for lisp is built-in.
And then there's the fact that Lisp's AST macro processing is built into the language processing scheme.
> Sure, you don't get to apply proper macros, but Java/Ruby/etc. programmers don't care about that.
The C programmers care, but they're used to being abused by C's macro system.
I'm not sure how that's a contradiction. "unambiguously representing" is still representing.
> Why do I say that? Because very few people know the full precedence of expressions. So, they break expressions in unnecessary places and/or add redundant parentheses.
Interesting, but not really relevant. They prefer the syntax, knowing that there are things they don't know about it.
> And then there's the problem of manipulating said AST. To do so for a traditional language requires a parser and a bunch of datastructures, which every project reimplements. Everything that you need for lisp is built-in.
Er, I don't understand how you can make that claim. A parser is necessarily built in to any interpreter/compiler for language with syntax, whether that language be a lisp or an assembly language or Java. Some of them don't have the parsers available at all (e.g. C), but some do (e.g. Python), and for some the parser is irrelevant (Lisps, machine code) because the AST (or "AST" perhaps, in the case of machine code) is always directly available regardless of explicitly calling out to the parser. In any case, your claim does not hold for "traditional languages" in any generality, there is too much variety.
The existence of a parser does not imply its availability. As you said "Some of them don't have the parsers available at all (e.g. C),"
> but some do (e.g. Python),
Python does make a parse tree available, but not in a very useful fashion. You can use it to rewrite programs but not as part of ordinary programming.
> In any case, your claim does not hold for "traditional languages" in any generality, there is too much variety.
Actually it does because there are very few "traditional languages" that even approach Python's in-language support for their own AST, let alone come close to Lisp's. (Prolog and some of the logic programming languages have some AST support but I wouldn't call them "traditional". I don't remember if APL does.)
No, but it guarantees that, at least in theory, such a parser could be made available. In practice many languages do make it available. Your argument was a general one, that is that all "traditional languages" have this property.
> Actually it does because there are very few "traditional languages" that even approach Python's in-language support for their own AST
It only takes one counterexample to break a general claim. "Very few" is enough. Nothing about traditional languages has any intrinsic property of being parser and AST-free-- it is merely laziness, minimalism, or coincidence that causes many to not. As examples like Python show, it is quite easy to find a mainstream "traditional language" with a full-blown (actually several full-blown) interfaces to the parser available. Other languages have this too, although examples are not incredibly widespread. Lua has implementations which provide an AST, and even the hyper-traditional language C# has the Expressions framework, which lets you parse/modify/etc. the AST of an expression (including, of course, the new lambdas).
It's just not true that Lisp is the only game in town for AST support, nor that "traditional languages" don't have it in general (although it may be true that most do not, this is not the same as claiming that as a group they do not).
By that standard, all languages are equivalent. That's true in a rather meaningless sense (Turing equivalence), but very few people think that Haskell is the same as C.
> it is merely laziness, minimalism, or coincidence that causes many to not.
The reason doesn't matter. If you don't have reasonable "out of the box" support for manipulating ASTs, it isn't part of the language.
This cuts both ways - few lisp implementations have reasonable libraries.
> It's just not true that Lisp is the only game in town for AST support
Feel free to show how ordinary {pick your language} programmers could implement and use something like the LOOP macro. Compare with how easily that can be done in lisp.
Ease (like speed) matters.
You could use type size, with operators one point larger than their operands. The nice thing is that this would discourage nesting things too deeply.
Perhaps you could use indentation like Python or YAML, for example?
It seems to be fairly common in linear algebra. The usual way to represent a linear function is as a matrix, and a function on that matrix is a function on a function. You also get fields of polynomials, in which differentiation is a linear operator.
i.hate(parentheses).split().doyou()
or
(doyou (split (i.hate parentheses)))
"GOTO Considered Harmful" Considered Harmfulhttp://www.ecn.purdue.edu/ParaMount/papers/acm_may87.pdf
You use GOTO every time you turn on the computer, though JMP is the mnemonic instead.
I expoect a different class of problems in their place but self modifying code is rightfully highlighted as unprovably correct.
Lisp is not the only language that can build a string and throw it at an eval function.
Not exactly. The totally unsafe programming model of C is what has caused this. Once you have untyped pointers that need not point to objects owned by your program, it is very easy to fuck something up.
> Lisp is not the only language that can build a string and throw it at an eval function.
Well, evaluating a string in (Common) Lisp just returns the string. Evaluating a symbol or list will actually do something interesting.
As for C, it's not just the fault of pointers. You just can't shellcode something that doesn't execute data.
Like I say, we'd just have a different class of problems, so it's not a panacea.