16 ms·
What are the enduring innovations of Lisp? (2022)
- smegsicle 3y agonotably not 'lisp features that have been absorbed elsewhere' but instead the opposite, lisp features that still make it unique tl;dr 1. no part of the system off limits 2. pervasive interactivity 3. homoiconicity
- Lyngbakr 3y agoPerhaps I've misunderstood, but several languages have REPLs so that isn't unique to Lisps, is it?
- phyrex 3y agoThere’s very rarely any where you can actually interact with the running program. At best you can usually load in a method and poke it, but that’s not the same.
- Lyngbakr 3y agoSo, the REPL mentioned in the article differs from, say, the Julia REPL[0]? Is it simply using the same term for slightly different things? [0] https://docs.julialang.org/en/v1/stdlib/REPL/ https://docs.julialang.org/en/v1/stdlib/REPL/
- selimthegrim 3y agoThe Julia REPL front end is written in Scheme IIRC
- sundarurfriend 3y agoI believe it's Julia's current default parser that's written in a Scheme, the REPL is a normal Julia standard library written in Julia itself: https://github.com/JuliaLang/julia/blob/master/stdlib/REPL/src/ https://github.com/JuliaLang/julia/blob/master/stdlib/REPL/s...
- adgjlsfhk1 3y agoalso we are very close to moving Julia's parsing (but not lowering) to pure Julia as well.
- selimthegrim 3y agoOK thanks for the correction!
- sundarurfriend 3y agoOne of the other comments in this thread links to: https://mikelevins.github.io/posts/2020-12-18-repl-driven/ https://mikelevins.github.io/posts/2020-12-18-repl-driven/ which makes a distinction between "having a REPL" and "supporting repl-driven programming". Modern languages which have a REPL generally don't support the sort of repl-driven programming they refer to. Julia's Revise.jl [1] supports one way of repl-driven programming, and it is in fact the recommended way of doing Julia, but from my understanding it's still very different from the Lisp ways. [1] https://timholy.github.io/Revise.jl/stable/ https://timholy.github.io/Revise.jl/stable/
- fsckboy 3y agothat would suggest it's an "enduring innovation" as per the question
- Lyngbakr 3y agoBut the previous post argues that the article is about "lisp features that still make it unique".
- schemescape 3y agoThis blog post explains the sort of workflow a Lisp/Smalltalk-style REPL enables: https://mikelevins.github.io/posts/2020-12-18-repl-driven/ https://mikelevins.github.io/posts/2020-12-18-repl-driven/
- Lyngbakr 3y agoThat is exactly the clarification I needed. Thanks!
- askvictor 3y agoInteresting; when you define a function (or anything) in a breakloop, does it get saved to source? (or is there an option to?) I'm trying to imagine using this style of programming in Python, but the type-redefinition problem is still there
- schemescape 3y agoMy understanding is that you usually write the code in your editor and use an editor command to send the code directly to the Lisp process (that’s what I do, anyway). Or just copy-paste. I’m not aware of a way to get code back out of the Lisp environment, but I’m fairly new to all this.
- lispm 3y agoSometimes a break-loop is all you have. But usually there is something around it. Typically a Lisp system can run several REPLs at the same time, one might be a break loop. You can define a function in another REPL and use it in the break-loop. You can define the function by loading code in a break-loop. An interaction might be (here in LispWorks): CL-USER 6 > (> (sin-x2 3) 0) Error: Undefined operator SIN-X2 in form (SIN-X2 3). 1 (continue) Try invoking SIN-X2 again. 2 Return some values from the form (SIN-X2 3). 3 Try invoking something other than SIN-X2 with the same arguments. 4 Set the symbol-function of SIN-X2 to another function. 5 Set the macro-function of SIN-X2 to another function. 6 (abort) Return to top loop level 0. Type :b for backtrace or :c <option number> to proceed. Type :bug-form "<subject>" for a bug report template or :? for other options. CL-USER 7 : 1 > (load "~/sin-x2.lisp") ; Loading text file /Users/foo/sin-x2.lisp #P"/Users/foo/sin-x2.lisp" CL-USER 8 : 1 > :c 1 T A function SIN-X2 does not exist. There is an error and we get a break-loop. The break-loop is one level deep. All of Lisp is available in a break-loop: the compiler, the code loader, the evaluator, ... Lisp stays with the break-loop in the context of the error. I define/write the function in a file and the load the file. Loading the makes the function available in the running Lisp. Then I use the continue restart to try to find the function again -> the error is gone
- amock 3y agoThe first two sound like core parts of Smalltalk as well.
- SeanLuke 3y agoSmalltalk was proudly a descendent of Lisp.
- deckard1 3y agoForth.
- neilv 3y agoTo anyone not already experienced with Lisps, there's varying schools of thought and opinions on what's important and valuable, within Common Lisp (CL), and within the broader Lisp family. For example, although this writer doesn't think macros are important, the Scheme (and especially Racket) branch of Lisp ran with macros, then with various other DSL support that take macros further (like Racket `#lang`). Racket also moved towards a strict definition of phases, and a very nice module system that works with that. That might horrify some CL people, because it moves further away from the dynamic REPL live manipulation strength of CL, but others of us have found the tradeoffs very practical for our needs.
- ilrwbwrkhv 3y agoHonestly just using Steel Bank Common Lisp is the best choice if you are looking to build high quality software which just works. A lot of my software which earns a $4 million profit per year has SBCL sub systems though that is slowly decreasing as we are moving away from Lisp.
- neilv 3y agoIf you have some great Lisp hackers who are also experienced in industry team software engineering, you step back and let them use whatever they decide is best to use. :)
- valbaca 3y agoI'm curious. May I ask what you're moving toward and what the motivations and reasons were? What was great and not great about building on/with Lisp?
- ilrwbwrkhv 3y agoI am moving towards Go. The greatest benefit of SBCL is that it's got great performance and the REPL jack in allows you to debug any application state. Building CL software is just amazing. So as the company is growing really fast, and I never want to talk to a VC, I need to add those reliability into the system as I can't spend a lot of time training people. That can only be done by having the highest performance to simplest code ratio (since we lose the repl jack in). Go is the clear winner here after trying a bunch of them. It is also easy for people to learn and the amount of tutorials and resources online is great. It is a bit sad, but the Lisp hacker bucket is a really small pool if you want to hire from so at the end of the day I had to compromise. Having said that Go is quite a workhorse and has the simplicity of C so it is actually not that bad.
- fsckboy 3y agothe innovation of lisp led to the innovations of scheme: Lambda: The Ultimate Imperative Lambda: The Ultimate Declarative Lambda: The Ultimate GOTO (Procedure Call Implementations Considered Harmful) https://research.scheme.org/lambda-papers/ https://research.scheme.org/lambda-papers/
- mcdonje 3y agoΛΛΛ
- bafe 3y agoIndeed,from my understanding of history it seems that lisp brought the idea of homoiconicity through the use S-expressions. Scheme was the first to introduce lexical scoping and in general the idea of constructing the language on a small set of well thought out primitives (see the lambda papers)
- Jtsummers 3y agobafe, I vouched for your comment. You may want to reach out to the mods (hn@ycombinator.com) because your account appears to have been shadowbanned. You only have 4 comments and all were dead (not marked as [flagged] which is typically there with user flags). You may have triggered one of the system's rules which sometimes catch up new users (for instance, creating an account through some VPNs, reportedly).
- dang 3y agoNot banned - but yes, subject to extra restrictions because it's a new account. I've marked the account legit now so this won't happen again. Thanks for watching out for a fellow user!
- bafe 3y agoThank you all! I'm a very new user, and perhaps not as good as a writer as most of you, but I am definitely a legit poster, not a spammer
- jjtheblunt 3y agohttps://a.co/d/6NaRjQG https://a.co/d/6NaRjQG The "condition system" is niftier than i've seen elsewhere.
- Jtsummers 3y agohttps://www.amazon.com/Common-Lisp-Condition-System-Mechanisms/dp/148426133X https://www.amazon.com/Common-Lisp-Condition-System-Mechanis... Unshortened URL: The Common Lisp Condition System by Michał "phoe" Herda.
- thequux 3y agoIndeed. For those who aren't familiar with the concept, you can consider them as the logical conclusion of exceptions. Traditional error handling, as found in C for example, forces you to handle the error at the moment you detect it. Often, though, that's deep in a library, and what to do about the error depends on the context. Exceptions allow the a function to declare that when an error occurs in its dynamic scope, it should receive control to handle it. This is, in many ways, a major improvement. However, consider a program that is parsing a data file. Halfway through the file, it encounters a malformed record. In an exception-based language it would throw an exception, unwinding the stack until you get to the main program logic. However, at that point you've closed the file, losing your position in it and any partially-parsed records. The only real recovery options are to abort reading that particular file or abort the load entirely. Conditions allow the function that parses a record to declare that it can recover from a malformed record by replacing the binary data with something else, producing an error record, or producing some record that is given from the outside. Similarly, the code that loops over the records can declare a recovery path that skips the malformed record and continues with the next one. Then, when an error occurs, the main program logic can inspect the broken record (possibly by presenting it to the user) and instruct the condition system as to which recovery path to execute. Only then does the stack unwind, and only as much as necessary to get to that recovery path. In short, exceptions separate detecting an error from handling it. Conditions add a third part, deciding how to handle the error.
- pierrebai 3y ago
- akkad33 3y agoWhich Lisp is the most practical and easy to pick up for a programmer? I already tried emacs lisp but the experience of running emacs was not great, so I gave up
- Nihilartikel 3y agoClojure is a serious contender. To be sure, it's still niche but it has a good ecosystem, piggybacks on mature java libs, and has an active community. It's thoughtfully opinionated, which I appreciate. Also clojurescript is almost the only front end dev experience I will tolerate. The hot reloading is magic and it is gangsta as F to use the exact same logic on the fast jvm (or CLR if you're nasty) backend as in the js front end.
- akkad33 3y agoI tried to get started with Clojure but their set up page is so confusing? The Windows version is still in beta? And intellij does not have a template for it like for Scala and Jetbrains even supports F#. I thought it was a mature language
- mike_ivanov 3y agoWhat is practical depends on your goals. Clojure can do pretty much everything, though it's a bit less practical for things like high-FPS video games and desktop GUI.
- deleted 3y ago[deleted]
- cellularmitosis 3y agoJanet might be a good contender for games. It is very easy to write C modules for the performance-critical bits.
- giraffe_lady 3y agoEmacs lisp is one of the worst lisps for most things, though it's a decent fit for its specific purpose. People are suggesting clojure and clojure is great but it also has rigorous immutability semantics. If you're not familiar with that model you'll spend as much effort learning it as learning lisp, and it'll be unclear which things come from lisp weirdness and which from immutable weirdness. Someone will also probably suggest racket, which has its strengths as a learning language but is also very large and complex, with numerous extensions to the core language that make it kind of a disorienting ecosystem. I like janet a lot. It uses "normal" data structures as its primitives rather than the traditional cons cells. So if you want to understand and be connected to historical lisp it will feel very different, and be a poor choice. This also applies to clojure though now that I think of it. otoh if you just want to use parens & prefix notation and play with macros either will work.
- CalChris 3y agoNo, Lisp did not invent if statements. They're in Fortran's 1956 Programmer's Reference Manual. http://bitsavers.informatik.uni-stuttgart.de/pdf/ibm/704/704_FortranProgRefMan_Oct56.pdf http://bitsavers.informatik.uni-stuttgart.de/pdf/ibm/704/704...
- thequux 3y agoFortran 56's IF statement is very different from the Lisp if statement; the former is a conditional jump, and the latter is a structured control flow construct. Further, Fortran 56's DO isn't a do/while loop as we know it today, but rather a conditional come from. The entire concept of structured control flow was invented by Lisp, and it was very controversial at the time.
- CalChris 3y agoDid Lisp improve the IF statement? Certainly. But here's John freakin' McCarthy citing Fortran's IF statement which he had used. That means Lisp didn't invent the IF statement. Lisp invented better syntax. http://jmc.stanford.edu/articles/lisp/lisp.pdf http://jmc.stanford.edu/articles/lisp/lisp.pdf
- lispm 3y agoHe invented the conditional expression. (print (cond ((> a 10) "a is larger than 10") ((> a 20) "a is larger than 20") (t "a is smaller than or equal to 10"))) COND takes zero or more conditional expressions and returns a value.
- Max_Limelihood 3y agoThis is a really good explanation of why I find Julia (effectively a Lisp in terms of these features) to be indispensable. The ability to generate code on the fly makes life so much easier that I just can't live without it.
- vchuravy 3y agoYeah I often describe Julia as a Lisp in sheep's clothing. Or as the m-Lisp promised to us :) I chuckled when I read: > The way that common Lisp systems produce executable binaries to be used as application deliverables is by literally dumping the contents of memory into a file with a little header to start things back up again. Which is pretty much of Julia's sys-/pkgimages work. Pkgimages are an incremental variation on this idea. One of the novelties in Julia is the world-age system and the limits on dynamisim it introduces on eval.
- pjmlp 3y agoPity that Apple didn't push Dylan.
- JanisErdmanis 3y agoI agree that Julia satisfies the first two properties however, it's not clear how it satisfies the third one (homonicity). In particular, how the argument with regard to Python does not apply to Julia as well?
- adgjlsfhk1 3y agoI think this answer https://stackoverflow.com/a/31734725/5141328 https://stackoverflow.com/a/31734725/5141328 by one of Julia's creators fits here. The TLDR is that homoiconicity is a mix of two separate things: how willing a language is to represent itself, and how close the syntax for code is to the syntax for the data-structure representing that code. Julia meets the first of these, but not the second. Whether this matters depends on why you care about homoiconicity. The biggest difference between Julia and Python here is that Julia has syntactic macros and python doesn't (although see https://peps.python.org/pep-0638/ https://peps.python.org/pep-0638/)
- pfdietz 3y agoLisp was a very early, if not the first, case of a language where types are associated with values, not variables. The evolution of implementations of Lisps showed that such languages could be implemented efficiently, even on stock hardware. This last realization took a while (witness how lisp machines were being developed into the 1980s.)
- dmvdoug 3y agoWhat will be interesting to see is whether the idea of a Lisp Machine comes back as ASICS become more popular. Will hole machines be designed to optimize performance with a DSL created for some particular domain? In other words, architectures optimized for specific languages (because those languages are optimized for specific domains), not necessarily literally Lisp Machines. That thought popped into my head because of the “enduring“ in the title. What endures depends entirely on the needs of, and approaches taken by, people in the present.
- pjmlp 3y agoIn a way, the ongoing adoption of hardware memory tagging to get rid of C memory corruption issues feels like Lisp Machine's revenge.
- pfdietz 3y agoI don't think lisp machines as such will come back, since experience showed many of their features were unnecessary. Instead, it will be interesting to see if there are any specific things that hardware assist could help with. The tagged pointers referred to in a sibling comment here could be an example. On the flip side, it would be interesting to see if a "modern" lisp-like language could fit better with modern hardware realities. Today's efficient data structures are more array oriented, for example.
- netbioserror 3y agoHomoiconicity wins in my book. Clojure is the best proof I have of this: What mattered was not the linked list or the cons cell. What mattered was that all code was just nested calls to functions and macros with the SAME general form: (function arg1 arg2 arg3 ...) Expression-based programming using this core form is trivially easy, and makes immutable data structures downright pleasurable to work with. It also makes code generation easy, though I've never taken full advantage of it; even so, a DSL made of runtime functions is not only possible, but a natural extension of anything you're already doing. Backing those homoiconic expression forms with typical high-performance data structures like arrays and hash maps, as Clojure and Janet do, is a winning formula for making complex tasks simple. All the patterns of yesteryear for modeling problems begin to look quaint when your program eventually just becomes a pyramid of expression calls, with `main` at the top and database hits at the bottom. The organization of the stuff in between really just becomes a question of standardizing function signatures and organizing modules so humans can easily navigate them.
- theLiminator 3y agoDefinitely, when you can directly manipulate your AST with easy, metaprogramming becomes a breeze.
- gleenn 3y agoVery much agree. It also gets you some really useful structural editing capabilities as a side-effect. I can shiffle code around so easily and blindly by just telling it to move an expression right or left, or adjust parens in or out. I also don't have to learn new weird syntax every time the language wants to do something kinda new. So many new Clojure "language" features are actually just libraries which is awesome. Tooling just keeps working and others can provide competing implementations. For instance clojure.spec
- skydhash 3y agoThis is the best part. Especially with JavaScript with so much difference in runtime and the importance of babel. Even the `loop` DSL can be broken down to basic lisp structure. The flexibility of the language, by allowing itself to be altered, is the most important aspect for me. After completing a first version of a solution, I often find my mind wandering searching for a cleaner, more understandable version of the code. And this is usually done by molding the language to the problem domain. Something like React in JavaScript exemplifies this. JSX was added for an easier developer experience, but the issue itself is created by the separation of code and data in the language. And CLOS would be perfect for the Component model.
- baq 3y agoYAML.
- AtNightWeCode 3y agoUseless parentheses?
- WJW 3y agoThere are many parentheses, but they're hardly useless.
- AtNightWeCode 3y agoLove to meet you at a party! ;) Cheers!
- temporallobe 3y agoMy nickname for Clojure/Lisp is (parenthetical hell).
- shaunxcode 3y ago(nirvana (in hand))
- beders 3y agoSigh. How many parentheses does this have: myFn(x,y,z); vs. (myFn x y z)
- bmacho 3y agoThat's strawmanning. Indeed, function calling needs the same amount of parentheses. But literally every popular languages (C/C++/Java/Javascript) have syntactic clues for : - assigning values to variables - function definitions - denoting elements of structures - if, for and while control flow - fields of structs or objects - etc Which also play a huge role in structured/OOP programming. Less popular languages usually have syntactic clues for the important parts of their paradigm. Once I transpiled the example code from the Janet site[0] to Python (I don't speak Janet.) You can compare them, and count the parentheses, and the syntactic clues in general: https://news.ycombinator.com/item?id=34846516 https://news.ycombinator.com/item?id=34846516 [0] : https://janet-lang.org/ https://janet-lang.org/
- agumonkey 3y ago- ~no syntax - induction - induction - some taste for minimalism (although CL/CLOS might feel different back in the days) - human exploration oriented (repl, mop/updates) - open homoiconicity, as a programmer lisp is an open box, makes you grow more in depth - understanding of high and low levels in one place - radical taste for innovation.. do whatever, you're near free
- dmvdoug 3y agoI think that Lisp taste for minimalism was very much a product of the world adjacent to Lisp and early Lispers: logic. Look at the lambda calculus itself. That whole golden era of metamathematics in the first half of the 20th century.
- agumonkey 3y agoYeah, induction and minimalism are highly linked to mathematics and logic. They always try to find smallest expression of highest abstraction. Unlike hacking culture (like engineering, accepts compromises for the context at hand) or commercial ones (enjoy bloat for finance/psychological reasons).
- nathants 3y agosolving frontend dev in a permanent way. https://reagent-project.github.io/ https://reagent-project.github.io/
- opportune 3y agoThe third point is a little convoluted to me and doesn’t seem to be a beneficial innovation so much as a design choice. Really for most of these benefits you can reductively boil them down to “code is data” because serialization-of-code, first class functions, REPLs, etc all more or less follow from that single major innovation. This single innovation has more far reaching effects than many people realize as once people figured out that code-is-data and AST serialization means little pieces of code (not full programs) could be transmitted and executed over a network, it has enabled massive improvements in data processing through things like MapReduce and distributed databases.
- drcode 3y agodidn't garbage collection originate with lisp? if so, that should really be considered its most pervasive innovation
- zabzonk 3y agoprobably so, but surely the most widely used early garbage collector was in line-based BASIC.
- sbjs 3y agoEverything worthwhile about Lisp has been integrated into TypeScript and JavaScript.
- coldtea 3y agoYeah, the same way everything worthwhile about spring water can be found in mud.
- erik_seaberg 3y agoExtending the JavaScript grammar into a domain-specific language, and then generating conforming code to run, almost requires adding Babel.js to your build and then doing a lot more work than writing a Lisp macro. Most devs probably never will.
- DonHopkins 3y agoNot homoiconicity and macros. And in the case of TypeScript, not interactivity. Besides that, Mrs. Lincoln, how did you enjoy the play?
- narag 3y ago...code generation (and therefore metaprogramming itself) are also not fundamentally innovations of Lisp. For example, in C++... Wait, what? Maybe I'm confused but doesn't innovation mean doing something not done before? C++ and even C are more recent than Lisp, they can't be used as counterexamples. Or am I missing something? (Edit> other than that, I forgot to say: nice article.)
- dmvdoug 3y agoI think it’s actually a problem throughout most of the article. But I think we can get the point of what the author means: certain programming language features that have enduring impact rose to because they were in Lisp (and Lisp had a period of real prominence). So, they’re not strictly speaking innovations of Lisp, but Lisp d was responsible, as a matter of historical fact(ish), for them becoming more widespread. I say “(ish)” because I very much suspect that if you did actual careful historical investigation of the sources, you would find that there’s vanishingly less genuine creation ex nihilo with computers than the standard stories say. Features or ideas that later become prominent, typically seem to be “in the air” or inchoate when the person or people we give credit to for creating them “created” them.
- deleted 3y ago[deleted]
- temporallobe 3y agoIn Clojure, thread macros. Allows very terse but powerful operations on data structures in a very readable way that’s easier to reason about than nested calls in C -style languages.
- jacknews 3y agoI just started learning Janet, and one of the things I really like is being able to use '-' in variable names. I didn't realize how much friction snake-case or camel case, ie having to use shift in var names, generate. It's a small thing, but a thing that differentiates lisps and algols.
- drekipus 3y agoI love kebab case. I want it everywhere.
- sigjuice 3y agoI discovered recently that bash function names and aliases allow - pi@4b:~ $ kebab-case-ftw () { echo yum; } pi@4b:~ $ kebab-case-ftw yum pi@4b:~ $ alias kebab-kebob='echo yum' pi@4b:~ $ kebab-kebob yum
- kazinator 3y agoDoesn't work for variable names: $ foo-bar=3 foo-bar=3: command not found Consistency in Unix? Sacrilege. Make is better in this regard. You can have variables with . in them and with computed variables, that can simulate structures. $($(VAR).member). $(VAR) expands to some abc, and so the $(abc.member) evaluates that variable with a dot in its name.
- ibiza 3y agoSame. Outside of Lisp & some shell, you've got the OG Cobol and Raku. HTML attrs? CSS? It's a shame really. So easy to type and read.
- ggm 3y agoIt’s also worth noting that code generation (and therefore metaprogramming itself) are also not fundamentally innovations of Lisp. For example, in C++, it is entirely possible to link your application to libclang and build Clang ASTs inside your application C++ code, and use the Clang compiler to emit and run that code. Ahem: can we discuss the word innovations and fundamentally above, and put two facts on the table: LISP: 1959 C++: 1979 If they'd said "is not unique" I could agree. The innovation is a statement of origination. C++ did not originate this concept into a language system from 19 years prior to C++
- brabel 3y agoC++ is not from 1979. What is from 1979 is the "beginning of work on C with Classes", which is entirely different. It was not until 1985 C++ got its first real implementation and a specification.
- adityaathalye 3y agoLISP gave us eval/apply, encoding all of computing in purely symbolic terms [1]. Consequently, we can manipulate a Lisp program as-is under almost any computing medium; as VMs for Turing machines and Quantum machines, as direct silicon hardware (Symbolics), under (paper-assisted) wetware (e.g. solve chapter 1 of SICP entirely by hand). [1] https://www.gnu.org/software/mes/manual/html_node/LISP-as-Maxwell_0027s-Equations-of-Software.html https://www.gnu.org/software/mes/manual/html_node/LISP-as-Ma...
- jonjacky 3y agoHere are John McCarthy's own answers to this question, from 1980, about 20 years after he invented Lisp: The Survival of LISP http://www-formal.stanford.edu/jmc/lisp20th/node2.html http://www-formal.stanford.edu/jmc/lisp20th/node2.html He describes 15 innovations -- it is impressive that these were all innovations when he invented Lisp. He adds, "Of course, the above doesn't mention features that LISP has in common with most programming languages".
- deleted 3y ago[deleted]
- chromoblob 3y agoAny language where you can define the type for its own AST is homoiconic, isn't it? So what is the big deal?