"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."
"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)."
Yeah, I always thought Dijkstra liked LISP because of the "the most intelligent way to misuse a computer"-quotation. When I read that text, I was completely confused.
It's not fair to say that Lisp programs "modify their own instructions", certainly not in the sense that some assembly programs do. Lisp macros are better thought of as code generators, or as small compilers. They should never modify their inputs.
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.
Also sounds a little like O-Meta (at least from what I understand of it...) - which is something Alan Kay and others have been working on for a fair while now. And this seems a little more alive and modern than 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.
I felt like I had a flash back to 1983, staring at a black screen with green letters. It took a second to realize I would not going to be reading BASIC.
I've been teaching Lisp to a friend and one of the things he likes best is the syntax. He likes that there is only one kind of punctuation, the parenthesis.
Well, that's not really the case. Parentheses are the main type of bracket used in most Lisps(which can be nicer than having [], (), and {}, some of which frequently have entirely different meanings depending on what syntactic context they are used in(for example, languages which use "{}" for both blocks and dictionaries, or "[]" for both list/array construction and indexing)). However, in Common Lisp for example,(with many present in other lisps) there's also the quote, the semicolon, the double quote for strings, the backquote and comma for partial quoting, #\ for character literals, #' for functions, #( ) for vectors, #* for bitvectors, #: for uninterned symbols, #. for read-time evaluation, #B for binary rational literals, #O for octal, #X for hexadecimal, #R for arbitrary bases, #C for complex numbers, #A for arrays, #S for structures, #P for paths, #n=foo and #n# for labeling objects for back-reference, #+ and #- for read-time testing for available features, and #| |# for comments. And of course, Common Lisp supports reader macros, so you could end up with a lot of potential syntactic punctuation.
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.
But parenthesis are the only required punctuation. Any of those others you mentioned can be equivalently expressed by parenthetical expressions. Homoiconicity is preserved.
> I can't say that I love the sheer number of parentheses required by the language.
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.
> Come up with a better way of representing an Abstract Syntax Tree in text, please.
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.
> traditional syntaxes are a way of representing an AST in text,
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.
> Actually, no. They're a way of unambiguously representing said AST.
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.
> 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.
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.)
> 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),"
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).
> No, but it guarantees that, at least in theory, such a parser could be made available.
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.
It's exactly the C programmers that care that use macros that will also use those extra parentheses. Especially when calling macros. Man you can be so badly bitten by bugs when your code expands to something almost identical, but not quite equal to working code. (free after Douglas Adams).
I think you're focusing on the wrong part of Abstract Syntax Tree. Lisp, fundamentally, has no syntax. It's just trees. That's the crux of how you write it. When fleitz said that he doesn't like the parens, I was just commenting, perhaps in too pithy a fashion, that the parentheses are incidental to the language. A vessel to hold the substance, as a glass holds water, if you will.
I see what you mean. But I don't think that's what he meant by Lisp.
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.
You'll notice that in the first paragraph there, he says:
> 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.
Yes, exactly. The power of Lisp's 'syntax' is that anything expressible as an s-expr can be written in Lisp. This is not the case with other languages, even though they are all parsed into s-exprs. The parser simply can't produce certain s-exprs.
*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 :-)
> functions as values in Mathematics just isn't considered as "normal" outside of fields concerned with the fundamental basis of mathematics.
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.
While a cute idea, I suspect the sheer number of newlines required would make it hard to read. Even a simple compound statement like (+ (* x y) (* z w)) would require at least three lines.
There is at least one attempt at this called 'Sweet Expressions' still floating out on the web somewhere. It got horribly ripped by the c.l.l community. Personally, I don't see that it adds anything to the language but ambiguity. It's worth looking, however, at if you are interested in alternative syntaxes for Lisp.
I am ill equipped to express it eloquently, unfortunately. But in poor words, it means that we can find unification between fields of mathematical thought. Taking ideas from one area, finding ways to regularly manipulate them, and then applying them in a novel way. Being able to find parallels between unrelated work because they are similar in how they are transformed.
Abstract algebra is the subject dedicated to that notion in general, category theory the one that, it would seem, is explicitly dedicated so.
I think it's a bit disingenuous to say that such dislike is nailed to Lisp. Mixing data and code in the memory of a running program is what has lead us to every exploit involving a buffer overflow or a stack smash.
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.
> Mixing data and code in the memory of a running program is what has lead us to every exploit involving a buffer overflow or a stack smash.
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.
I should also say that I have experience working with Manchester cpus (AVR microcontrollers) where the code is fixed when you flash it and you cannot jmp into RAM. Of course one then writes an interpreter instead :)
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)."