3 ms·
Languages you must know #10 (Lisp)
Lisp
Lisp is a shapeless language. It enables you to shape your solution to your problem.
Lisp, unlike every other language I know, enable you to think about the problem and write the solution. Almost every other language has an "impedance problem", like connecting a soda straw to a firehose. Other languages require you to "forcefit your solution" to the language needs.
Lisp is THE language to learn. Thinking in Lisp is thinking about your problem.
Of all the languages I know, Lisp is the ultimate language.
Unlike other languages, Lisp is an "epiphany language". You don't "get it" until you "get it"... and then you wonder why other people don't "get it". From the dictionary we find:
An epiphany (from the ancient Greek ἐπιφάνεια, epiphanea, "manifestation, striking appearance") is an experience of a sudden and striking realization. ... Epiphanies are relatively rare occurrences and generally follow a process of significant thought about a problem.
Clearly the "process of significant thought" is about the problem. Lisp is the language of that breakthrough experience.
There are no words I can say, you have to have your own epiphany. (I could, I suppose, make some parenthetical remarks but that would be a bit meta).
Lisp is a "must know" language. It is, in fact, the most important "must know" language.
- martyalain 6y agoI agree with you, Lisp is "the" language to meet. But I think that one can go a little beyond ... I would say before, towards its foundations. Lisp is a child of the lambda-calculus, Lisp focuses on lists (cons, car, cdr, ...) but IMHO I think that there are other choices, different ways which can be followed with some interesting results. I would like to know what you can think of this one, the lambdaway project (http://lambdaway.free.fr/lambdaspeech/ http://lambdaway.free.fr/lambdaspeech/), easy to install and use in any web browser. Alain Marty
- kazinator 6y agoHi Marty; remember we had that discussion a few years ago about scoping and I pointed out that you can implement lexical scoping in the substitution-based evaluation strategy, like lambdaway. Did you ever try that?
- martyalain 6y agoHi Kazinator, Thank you for reply, I didn't remember our discussion and no, sadly, I didn't found how to implement lexical scoping until now. The workaround is a kind of "manual closure" as illustrated in http://lambdaway.free.fr/lambdawalks/?view=closure http://lambdaway.free.fr/lambdawalks/?view=closure http://lambdaway.free.fr/lambdawalks/?view=lambda http://lambdaway.free.fr/lambdawalks/?view=lambda where is used the capability of lambdas to accept partial calls and IIFE build the bridge. Time going on I improved lambdatalk, in a total loneliness. Except Ward Cunningham who finds some interest in a language which could replace in wikis the boring standard Markdown syntax. I would like to know what you think of such an introduction http://lambdaway.free.fr/lambdawalks/?view=fromroots2canopy http://lambdaway.free.fr/lambdawalks/?view=fromroots2canopy and, for instance http://lambdaway.free.fr/lambdawalks/?view=reduce http://lambdaway.free.fr/lambdawalks/?view=reduce Thank you once more for your kind reply. Alain Marty
- kazinator 6y agoI found some past discussions but not sure if that was the right one. Anyway, suppose we have a pencil-and-paper Lisp dialect, where everything is evaluated by rewriting objects on paper. We can have lexical scoping. That is to say, lexical scoping is achievable by substituting terms for variables. In particular, let us restrict to immutable variables. Like say we have (let ((x <whatever>) (y <whatever-else>)) (body x y)) Suppose that is just a picture: a character string or pencil marks on paper. We see what the scope is of the let and can basically just substitute: (body <whatever> <whatever-else>) It is the same with lambda. Say we have a lambda definition like (lambda (x y) (body x y)) When we call this with parameters <foo> and <bar>, whatever those things are, we substitute the arguments for x and y into the body: (body <foo> <bar>). This substitution can take place by a recursive walk of the code which respects scope, so for instance, if we have: (lambda (x y) (let ((x <xyzzy>)) (body x y))) the substitution will not just blindly replace the x as a character string. It walks the expression, and processes the (let ...) recursively, recognizing that x is shadowed by a new definition. The (let ...) part is recursively expanded and then substituted in place and so on. You need to pass down some environment object during the expansion. But: your lambdas do not have to have captured environments! They can be fully substituted. So that is to say: (let ((x <captured>)) (lambda (y) (+ x y))) can produce something like (lambda (y) (+ <captured> y)) no environment. The expansion/substitution process of (let ...) naturally took care of substituting the x value. The lambda parameter y remains unsubstituted: that happens at call time. The substitute function should probably be aware of lambda and keep track of the y, but specially annotated in the environment as "this is a lambda variable: do not substitute now". That sort of thing. My main point is that if you don't need mutable environments, then pencil-and-paper lexical scoping can be simulated in character strings by substitution. (Mutation throws a monkey wrench into things, like if you want to have stateful lambda procedures that return an incrementing counter whose initial value is a captured lexical.)
- kazinator 6y agoThis is all false and misleading. Lisp is a language family, not any one language. There is no the Lisp, from which it follows that Lisp cannot be "THE language". Are you saying that you can solve any problem with no impedance mismatch in AutoLisp?
- daly 6y agoFalse and misleading? The chosen languages have changed the way I've thought about programming. In my 'lived experience', Lisp is 'THE language' of thought. I'd welcome your choice of language and why you think it deeply affects the way you program. I've used 'lisp' since 1970, in a FORTRAN implementation of Lisp 1.5 out of Bell Labs. Clearly 'lisp' has evolved since that time. But all versions I've used, like MacLisp, Symbolics, Interlisp, Lisp/370, VMLISP, CCL/MCL, Portable Standard Lisp, KCL, AKCL (I'm a minor author), Emacs Lisp, CLISP, Lucid, Gold Hill, ABCL, Clojure, and a few others I've probably forgotten. They are all 'different' but all are recognizably 'lisp'. I don't know AutoLisp but I suspect that it might have the 'clay-like' property of other 'lisps'. The point of this series is to find languages that change the way you think. Don't confuse an implementation with the concepts.
- kazinator 6y agoIs this non-implementation language of thought documented somewhere, so that people can learn it and think with it? And does this language have mutable cons cells, with lists terminated by nil, which is a self-evaluating symbol that also denotes false? Are there interned symbols, quote and all that?
- martyalain 6y agoHi kazinator, are you replying to "daly" or to "martyalain"? If it is to me and about lambdatalk, some answer can be found in the links given previously.
- daly 6y agoAssuming you're not trying to be sarcastic... As a "language of thought" it is often the case that I will write my solution in Lisp and then "hand translate" it to the target language. This has two effects. First, assume I'm "translating" to Python, for example. The Lisp "Gedanken" keeps me from depending on strange local language behavior. For example, I won't have to catch missing dictionary entries. But if I happen to want a local language behavior I can just make a macro. Second, the Lisp "Gedanken" allows the ability to "factor" solutions since Lisp tends to "encourage" functions. For example, I'm working on a design for a dependently-typed language that allows proofs of the resulting programs. Lisp allows me to create a domain-specific language easily. Gedanken experiments led me to factor the category, domain, and representation for the specification, implementation, and proof languages. I know from experience that whatever domain-specific language constructs I dream up, they can all be implemented as macros and compiled efficiently. When "thinking in Python", you'll find yourself restricted by implementation issues or searching for some library and then shaping your problem to fit the library. When "thinking in Java", you'll find yourself trying to force your solution into "objects". When "thinking in C++", you'll find that (a) you have to choose WHICH C++ (there are so many) and (b) by the time you implement something C++ will have changed. I can still run Lisp code I wrote in 1980. Assuming you ARE being sarcastic... there's Greenspun Tenth Rule: Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp The fact that you've immediately gone to "interned symbols" means you aren't used to using your language for thinking. These things don't arise in Lisp Gedanken. Cogito, ergo Lisp