Elisp::Emacs as AutoLISP::AutoCAD. AutoLISP was my first introduction to Lisp-style language. When I first started using it (1987) for macros in AutoCAD, I really had no idea what Lisp was. It was just a fun and easy way to automate AutoCAD.
I really wanted to Lisp as a main programming language, and sometimes I still do.
I just find readability such a hurdle regardless of how long I used it. I didn't find that it ever became as natural as the other group of programming languages.
I find a procedural style of programming so much easier to reason about, both when writing and reading.
Either way, I'm really happy I took some time to learn it and use it a little at some point.
For me, the most effective way to read Lisp is to essentially forget the parentheses (I shadow them out in matching, low contrast colors) and go almost entirely by indentation. I find this makes it more similar to reading other languages, though granted not exactly the same.
You do have to keep up with the parentheses of course, but editor settings or extensions can make this automatic if not invisible.
> I find a procedural style of programming so much easier to reason about, both when writing and reading.
Then do that.
There's nothing stopping you from using pretty much any style of programming that you like. Or mix and match. Or evolve over time.
Loops, lists, arrays, structures. Simple iteration: dotimes, dolist, loop. If those are your bread and butter, then feast! CL will happily do that. That's what I do. I just don't think "functionally" when I do CL code, I'm just not there yet, so its unnatural for me, and not what comes spewing out of my fingers when I write code.
And it's "OK".
You don't have to use the other features of the language, but they're there if you want to dip your toe into it.
With CL, also, I tend to be really wordy on variable and function names. I'm really fond of kabob-case-for-identifers.
Common Lisp code can be very procedural if that's what you want to do. The entire loop macro is basically importing Algol-styled procedural loops into Lisp.
Lisp really needs extensive structural editing tools IMO, and you really have to change how you think about reading source text and that can take longer than you might think. The best Lisp experience by far and away is LispWorks. That being said, I never found it to be to my tastes either. Too much focus on making tree-representations of programs the centerpoint, which isn't how my internal grammar works. I'd much prefer less explicit delimitation where it's not actually needed, but that's also incompatible with the goal of sexprs.
I've had the same complaints when I started. I think, realistically, every programmer who's learning Lisp after getting experience in a bunch of other languages has to deal with that. The mental overhead feels real. Yet, after a while, there's some psychological threshold - Lisp starts feeling more intuitive. At some point, there's just no turning back - nothing ever will feel again more readable than Lisp code. It's just like riding a bike. Once you "get it", there's just no way to "unget it" back.
I also might have written the Common Lisp example using reduce as well, which is in the standard library, but that's preference. Nice to have the option though:
(Disclaimer: CL weenie) A decent and balanced writeup IMO. But it should really have contained the following:
Warning about the issues that come with ANSI CL's frozen spec (threads/sockets/unicode/extensible sequences/gray streams/etc... as extensions with a varying amount of support with compatibility layers often available to write portable-ish code, "bolted-on" CLOS never fully integrated) and its various rust spots, not just the good points.
Mention that CL has provisions for gradual typing (with limits) which are exploited by SBCL.
Scheme, obviously, along with the same warning as CL about pain of writing portable code that interacts with the OS (does it have compatibility layers like CL?) amplified by the R6RS vs unfinished R7RS-large mess.
A few words about the build system/third-party packaging situation and alternative implementations.
For me the complete spec is the killer feature. You can learn Common Lisp in 1990 and write it the same now. As long as we can keep the compilers alive it will be forever.
It’s funny to me that it was critiqued for being “bloated” when now it looks like a focused minimal library.
Also, SBCL has some nice features specific to them, I'm sure it's the same for other implementations. So while there's a lot that's common between them all I find myself using a lot of platform specific functions.
Yes, but it existed before then (from 1984 on), and a large amount of code written for CL pre-standardization still runs without alteration or with minimal updates.
I have a work-in-progress called Modus. 100% written by Claude, so take that however you will. The current release boots on a Raspberry Pi Zero 2 W. The next release (unreleased in the pipe for ~ months) is standard Common Lisp on bare aarch64 (pi) and x64 (qemu for now), with linux aarch64 and x64 command line interfaces à la sbcl.
Since you can't use an OS by itself, I've rounded out the Common Lisp environment with portable ssh client and server, web browser, and a bitcoin node. Framebuffer with VNC in the pipe
Since a few folks here recommended Common Lisp to me as the language that would "tick all my boxes", I've been doing a deep dive. Right now, I'm working through SICP again with DrRacket. The first time I worked through it with MIT Scheme MANY years ago. It's shocking how much I've forgotten.
What I like about this article is that it walks through the different "camps" of Lisp. Scheme is so intriguing to me because of how small it can actually be. I can build nearly any paradigm I want to exist. The problem is, if I were to actually go find a job where they were using a Lisp, (I hear those actually exist) they wouldn't want to use my "Result monad + match statement - railway pattern" that I've used from OCaml and Rust. So learning something that is truly "common" can make more sense.
As far as learning though, Scheme feels "just right". I've imposed a "no AI until I've found a working solution" rule that keeps my mind engaged. Couple that with a willingness to say, "I don't know that right now... I'll think about it throughout the day and maybe by this evening I'll have an answer".
I can also recommend clojure. For me it has the best parts of common lisp and the best of the java ecosystem. But its also quite different from common lisp and scheme. Different enough to find some unique ideas.
Writing scripts using [0] Babashka is also really nice.
I particpated in a Clojure reading group for "Getting Clojure" back around 2017. Having the entire JVM ecosystem available, is absolutely a great benefit. I even fooled around with ClojureScript a bit. David Nolen is great at making the case for both.
On a related note, there's a cross-platform Common Lisp package, "Bike" https://github.com/Lovesan/bike, that lets you use .Net assemblies from Common Lisp.
I've used it a tiny bit at work (on Windows) and at home (on Linux), and ran into one issue with "out" parameters, but otherwise it works really well.
> Common Lisp implementation on .NET. Lisp source is compiled to CIL (Common Intermediate Language) and runs on the .NET JIT — so the same Lisp image runs on Windows, macOS, and Linux across x86-64 and ARM64 without per-platform porting work.
Now there's Jank too, the first time a Lisp dialect has reached into native world since Clasp. The way it interops Clojure with the LLVM is unprecedented.
> the first time a Lisp dialect has reached into native world since Clasp.
What's that supposed to mean? Many (probably most if we only consider the non-toy ones) lisp implementations are "native" (compiling to native machine code, not interpreted).
Ya, I think that was a bit misspoken. They mostly meant direct C and C++ interop. It feels much less like a traditional FFI and more like using C and C++ libraries from C++ itself.
As a former Clojure dev (now just using Clojure in my spare time) I love Babashka. Michiel Borkent really nailed it with sci (Small Clojure Interpreter) and Babashka. Running a custom Clojure interpreter in a GraalVM compiled Clojure app is quite clever.
Now there are of course limitations to what you can do in terms of not supporting Java reflection or the full Clojure compiler. But I've made some nifty small scripts and convenience helpers with it. And the dev experience of making these scripts is so much nicer than trying to write bash scripts. The Clojure edn syntax is super simple, and the REPL connected editor let me rapidly test parts of the code just like with full Clojure apps.
I don't have experience with other lisps, but I can vouch for Clojure being very nice. The community was welcoming and friendly to newcomers when I started learning, I hope it still is. One thing I love about the Clojure ecosystem and community is the effort taken to never break libraries. I've looked at libraries I used some ten years ago, and the API is still compatible with code I wrote back then. There is very little churn. Maybe this is because the language is largely untyped and editors only partially check "types". Having breakages in libraries you consume once every couple of months would get really tiring in Clojure land. I'd imagine the same problems would present themselves in Common Lisp and others.
I love the Clojure community, it is the only one that usually talks about the host platform in a symbiotic way, not as if they would be rewriting everything into their favourite language, like in most guest languages communities.
For what it's worth, Scheme was the first programming class that I took at UIUC around 1995, as I recall.
I had used C++ for several years to make shareware games, so I took a test to challenge some programming courses. I vaguely recall doing well, but my advisor encouraged me to take them anyway. I'm glad that I did, because I had little understanding of theory.
Funny story: the instructor never mentioned that we could use more than one line of code. So every single piece of homework that I handed in, and every test, was one giant line of nested logic. Which worked better than one might expect, and completely changed how I wrote code from that point forward. That's how I made the connection a decade later that functional programming is akin to a spreadsheet, as are higher-order method chains and immutable variables.
I think of Clojure as being a layer above Lisp, sort of like how Swift might be considered a layer above Objective-C/Smalltalk. However, bare Lisp has problems around not quite giving enough out of the box. It's minimalist enough that developers end up reinventing the wheel for things that should probably be provided by a layer/library similar to Scheme or Clojure.
To digress, I feel that mutable variables and even monads are a code smell in functional programming since they can cause impurity. They're more of a crutch to ease conversion of code from imperative languages. However, monads can be useful to simulate every path through a program, sort of like superposition in quantum mechanics and SAT solvers. So they aren't necessarily bad, just taught incorrectly, probably because they're so hard to grok.
I'd vote to settle on a series of layers like Common Lisp -> Scheme/Racket -> Clojure/Elisp, with the final layer providing the intersection of features available from the most widely-used Lisp variants. Note that this is specifically to form a bridge from imperative languages, so research work might need additional DSL features brought forth from the Racket layer.
Edit: I forgot to mention that Scheme is a good fit for genetic algorithms, see books by John Koza (no affiliation). My feeling is that we haven't seen anything yet regarding what problems AI can solve, since it's having to do it the "bare hands" way with LLMs and pattern matching.
I dislike Common Lisp, and I fear it keeps ruining the reputation of Lisp itself with its archaic and obtuse systems designed by a committee of dinosaurs. This opinion will attract the ire of many a greybeard. I stand by it.
If you want a Scheme with batteries included, I recommend GNU Guile. Also worth your time are Racket, Clojure, Janet.
In Sussman's book 'Software Design For Flexibility' he uses Match? in the pattern matching chapter to run on graphs. I think OCW archived his MIT course for the book too
Yeah, guile has a library that looks close to what I want. The worry is that railway programming is an entire shift in error handling. Since it's not the standard that I've seen in the Lisp/Scheme world, I'd be forcing a paradigm that no one wants.
The Scheme Programming Language by R. Kent Dybvig is very much worth reading after you finish SICP. Truly the same caliber as K&R's C Programming Language, and more up-to-date (R6RS). The earlier exercises aren't too tricky (except call/cc headaches), but the final chapter has some good meaty Scheme projects that will teach you a ton.
Shut up and learn Common Lisp using Practical Common Lisp.
This would be my advice. Why? My own road was haphazard. Other books broaden your mind and teach you really cool tricks. This book gets you using lisp like you would say golang. But it still teaches you the lisp things and broadens your mind. Time spent choosing will be better spent reading this book. After that PAIP, On Lisp, SICP etc.
An Introduction to Programming in EMacs Lisp is also good for the first few chapters even if you don’t use emacs because you are given fundamental concepts of lisp that can be applied to the understanding of other dialects. It’s also free.
Learn you a Haskell (despite Haskell being not a flavor of list, they share similar DNA ) is great at understanding functional programming with lisp like languages.
When people have a lot of choices, that can create problems, because it's often the people with the least information trying to make that choice.
For instance "I'm new to Lisp, I want to try one..." is a person without a lot of background and information to make that choice. And they probably realize it and it makes them nervous about making that choice.
IMO if we look at Lisps today the question looks more like: SBCL, Chez Scheme, Racket or Clojure.
Common Lisp and Racket are Lisp-2s but honestly, the namespace thing seems like a minor difference compared to all the other features that differentiate them.
Ignoring all the other distinctions between lisps, the main difference between lisp-1 and lisp-2 (or lisp-n) is going to be how clean your code looks when you lean into the FP style. In a lisp-2 you'll need to do something like this:
(defun apply-twice (f x)
(funcall f (funcall f x)))
(apply-twice #'1+ 2)
Versus this with a lisp-1:
(define (apply-twice f x)
(f (f x))
(apply-twice 1+ 2) ;; assuming 1+ is defined
But there are so many other differences between the lisps in the two categories that this probably won't be the deciding factor for most people.
The argument is: if you don’t have hygienic macros, a Lisp-2 is going to be less brittle than a Lisp-1.
The classic example is, imagine you have a function with a local variable called “list”, common enough. Now imagine you invoke a macro inside that function which generates a call to the built-in “list” function - also common enough. In a Lisp-1 without hygiene that breaks - your local definition shadowed the built-in; in a Lisp-2 or hygienic Lisp-1 you’re in the clear.
People should never use non-hygienic macro systems anyway, but even if they are using a non-hygienic macro, they should always use proper hygiene. Its kind of dumb to make the whole system weirder just to avoid issues which should never happen in the first place, in my opinion.
@nathan_compton, your sibling comment to what I'm writing now is [dead] (not [flagged]) but you're not shadowbanned, newer and older comments are still alive. I vouched for it but it's still [dead], you may want to reach out to the mods.
I am a strong proponent of Lisp 1, primarily because the distinction between functions and other types of values is artificial. Functions have first class semantics in Lisp 1 and Lisp 2, but Lisp 2 makes you denote them differently but in an inconsistent manner.
Lisp 2 advocates typically make a few arguments. One is that having a separate namespace for functions makes it clearer when you are using a function vs another value. The second is that the evaluator has less work to do when examining the head of a list - it needs only look in the function environment, not the full environment.
On the first subject I must disagree - you can bind a function to a regular variable and then use that variable everywhere (except in the car of a list representing a function call), so for most positions in a set of expressions you don't really get information about whether the object being denoted is a function or not.
I suppose the second point is somewhat valid, though I suspect if you benchmarked interpreters and compilers it would barely matter. As a person who favors functional programming with a lot of combinators, I find Lisp 2 introduces a lot of pointless noise in the syntax for no reason. And I fundamentally just don't see functions as significantly different sorts of values, so I find the syntactic distinction bizarre.
Having a separate namespace for functions is silly in that it only saves you from a small set of variable shadowing problems. It’s a hack, not a serious solution.
personal taste, for the most part. I like the thought of a single namespace, it fits my intuition from pretty much every other language out there, and I like how the code looks when I can pass functions around as though their names are regular variable bindings to an underlying function object.
I hate having to come up with different variable names than `list` so I don't shadow the function `list`. Repeat that for a lot of common words: count, first, length, map, max, min, search, string; that you would then actually consider calling said function after declaring a value. Happens to "list" the most, but definitely the others on rarer occasions.
The usual argument I've seen in favour of Lisp-2 (I personally don't care as much about the function namespace as I do about the type namespace, which I find much more important) is that you can name an argument a conflicting name with a function without the conflict interfering with the code you would write:
(defun merge-sort (list before?)
(declare (type List list)
(type Function before?))
(flet ((merge-2 (a b)
(declare (type List a b)
(merge 'List a b before?)))
(unless (null list)
(reduce #'merge-2 list :key #'list))))
(merge-sort '(1 9 8 2 3 4 7 6 5)
#'<)
Instead of having to name lists 'lst' or something. Which is pretty much personal preference anyway.
In a language which doesn't normalise the case of symbols you could in theory work around that by capitalising or upper-casing either the function or the variable, but that's still not a particularly elegant solution.
Lisp-1 has the wart that it models special operators as bindings in a variable name space. So a form like (print let) actually resolves let to a binding to a special operator, and then has to be somehow pronounced as nonsense. And you can block let from working by binding that as a variable name. Allowing variables to shadow the basic operators of the language is quirky.
Would you want to use a weird POSIX shell in which "for x in *.jpg; ..." stopped working because you assigned "for=42"?
Yet Lisp-1 has a notational advantage for programs that work with functional values; programs that indirect upon functions are more succinct, free of "shim" operators for lifting values out of the function binding space, or requesting application of a function value.
In the TXR Lisp dialect, I worked out a way to have the notational convenience, without bringing in the Lisp-1 issue.
1. The substrate, including macro-expansion logic, is thoroughly Lisp-2.
2. When a compound form is written in square brackets, it indicates that immediate elements (those constituents that are symbols) are to be looked up in a single namespace that is a merger of the function and variable namespaces according to precisely documented rules. This is not recursively applied to the form; its arguments that are compound forms are not treated this way unless they use square brackets.
3. Macros are not affected. In a form [a b c ...], the element a is neither recognized as a special operator or as an operator macro. If it is a symbol, it is either a variable, or a symbol macro.
4. [ ... ] is a surface syntax with a straightforward representation: the (dwim ...) operator. The above semantics is thoroughly baked into the macro expander and correctly treated at all necessary levels of the language: the handling of lexical scopes at expansion time and compilation/evaluation.
This system leaves mainly just one small infelicity. The merged 1+2 namespace is not available in certain contexts when it would be handy. Like say we are writing a function that takes a functional argument, that we would like to default:
because the (test-fn equal) syntax is not a form to be evaluated; it is just notation within the parameter list which has not been equipped with support for the alternative brackets/dwim structure. The way to do this is:
fun* is like Common Lisp's function* operator. It's one of the few instances you ever have to use it, the others also being situations like values in binding constructs such as let. It is not worth complicating things to provide a way to take the fun out these situations.
That helped me to think about recursion, functional programming, and type driven development. After going through HTDP I was able to breeze through complex problems that were unsolvable before.
Natural language phrase generation in a very complex foreign language that follows recursive syntax trees. And making a GUI to handle and edit these syntax trees visually.
The funny thing about Lisp is that writing a Lisp interpreter is significantly fewer keystrokes than correctly installing Common Lisp and its tooling. The ratio of lisps to lisp programmers may actually be above 1.
Literally a comment I wrote then decided not to post.
> The best LISP is the one you create yourself. You learn so much about programming by implementing your own language, and LISP is brilliant for this purpose thanks to its simple syntax and homoiconicity (code is data, data is code). It's also a great way to learn a new existing language, like Rust or Go, by writing a LISP interpreter with it.
There is also MLs that have the same idea, but with less implementation, as a lisp is something you hack in a weekend. An working ML not, as it takes way more effort.
Lisps have many fantastic ideas, but are really hard to read. Lisp code is what we had before perl guys went "hold my beer".
I know its just syntax, and it usually does not matter, untill it does. I did some clojure a long time ago, and before that some CL, and god, i cant understand my own old code. Contrast that to some language that has syntax i can read it still, years later. Go being the prime example of write once, read a decade later.
I find Lisp much easier to read than most other languages. It is highly explicit syntax with very little ambiguity, especially if you have a little discipline about macros.
I'm going to take the opposite position that there is much less special about Lisp than people think. Like I have met so many programmers that travel around like itinerant martial artists looking for true functional programming and they never find it.
To be specific, you can work all of the examples in Graham's On Lisp in Python except for one of the last chapters where he implements continuations that really need macros -- but this is basically the async/await facility that Python already has. The other examples use macros for performance but work fine with just plain functional programming.
React with hooks is an example of that kind of system at work -- the JSX transformation is a very simple shim you can put in front of the JS compiler and the hooks themselves are the kind of trick that On Lisp teaches you how to do.
On Lisp doesn't use the kind of tree-walking macros that really are unique to Lisp. And... the techniques in the Dragon Book for writing compilers are the real magic. If looking to Lisp as an old shiny keeps you from learning how to write compilers, it is holding you back.
I think the homoiconic thing leads people astray. There's a real tension that, for performance reasons, mainstream compilers aren't extensible, for instance you might want to write a
unless(X) {...} => if(!X) {...}
control structure in a new language and for something in Java that is really a production rule in the grammar, maybe a class to represent the unless block, and a rewriting rule that gets applied to the AST. If the compiler was designed to be easily extensible that would be less code than the POM file for the project. But it's not so it isn't.
Many things hold us back.
The industry has been so traumzatized by slow compiles that trading speed for extensibility doesn't sell to the people who create languages. Also once you have the sophistication to make things like parser generators you know how to get things done with the terribly unergonomic parser generators we have and don't have a lot of empathy for all the programmers out there who might be using parser generators if any of them were built as if usability matters.
it compiles to bytecode but the bytecode engine is not that fast.
i had a phase when I was using PyPi a lot for branchy "old AI" kinds of workloads and felt it was an easy win but since then it has been either numpy or PIL or pytorch doing the heavy lifting or scripty stuff like uploading files to S3 where performance doesn't matter a lot.
I will grant that Common Lisp can be compiled to run amazingly quickly!
Not sure about that. I rather think it's the very dynamic nature of Python (e.g. at run time one can add members to classes) which limits performance gains achievable by compilation. Iirc PyPy got to some five fold performance gain against CPython.
Common Lisp, as an example, makes some deliberate flexibility (and elegance) vs. performance trade-offs. I'd think to speed Python up more than PyPy could, one would have to sacrifice some of its features (some of those might not be all that popular or even desirable).
Nah, Python has its place. If its performance isn't adequate for the problem at hand, rewrite the bottlenecks in C++ and be done. LLMs should make that easy, no?
The dynamic excuse always misses out fully dynamic languages like Smalltalk, Self, among others have achieved, and how it got further exploited into JavaScript engines.
However CPython isn't even half way there to either Papyrus or GraalPy.
The community attitude to rewrite into C, C++, Rust, Zig, whatever, is exactly why Python will never be like other dynamic languages, and the dynamism excuse will always be used.
Thankfully at least GPU vendors see it otherwise, so if nothing else at least it will get JITs that speak GPU machine code.
There's nothing "special" about Lisp and Lisp dialects, yes. Similar features can be or already have been implemented in other languages. Yet, after touching, using and experiencing working with a bunch of different stacks, I cannot simply ignore the enormous pragmatic level of Lisps.
Working with Clojure is an absolute delight. It strips down all the dogma and let's you deal with the "business logic" as if you're cooking steak using no BS ingredients - meat is meat, herbs are real, stove is hot.
Why would I ever choose bash for writing anything slightly more complex than simple redirection, when I can do things in way better fashion with babashka. Why would I wrestle a YAML CI pipeline that only fails on push, when I can drive the whole thing from a babashka task file, run each step locally in the REPL, and actually debug it?
Why would I ever deal with Lua, if I can't even format it for "readability" - no matter how I do it, it just looks darn ugly, and luafmt often makes it worse. Why, if I can just slash down dozen lines of Lua boilerplate compressing it into a three-liner Fennel macro? With Fennel, I can interactively poke through elements of my WM through Hammerspoon on Mac, and that's just bananas.
Why would I ever deal with JSON, when EDN is almost twice as compact and far more readable - I can align things and treat data as a literal table. Besides, I can group, sort, filter, slice, dice, salt & pepper that data easily, without ever leaving my trusted editor.
Why would I choose to build a web-scraper in Python, when I can use nbb driving Playwright and go through selectors interactively, directly from my editor, as if it is a devtools console. And I don't even have to restart anything, deal with state changes, etc.
How can I abandon Emacs where I can just open a scratch buffer, type some Elisp and change the behavior of my editor, my WM, my OS and even things on remote computers. No other text editing environment works the way Emacs does - nothing even comes close. It feels like playing a video game, where my controller in my editor.
Why would I write Flutter UIs in Dart, fighting the widget-tree ceremony and endless build() boilerplate, when ClojureDart lets me express the same tree as plain data and hot-reload it interactively? The layout is just nested maps and vectors.
Why would I reach for C when I need to embed a small, fast scripting layer. Text parsing alone would be a regex nightmare elsewhere.
Why would I bolt a templating engine onto HTML strings, when Hiccup makes markup just vectors - so my views compose, filter, and generate like any other data, no special templating DSL to learn
And with all sorts of different runtimes and dissimilar Lisp dialects, it still feels as if you're working with the same language. The mental overhead when switching is so negligible. While switching between just JS and TS - which are supposed to be of the "same family" - feels quite annoying. Despite the fact that I've put years into those - far longer than any Lisp I've ever used.
Sure, nothing special about Lisp at all. Except that practicing Lisp can actually make you a polyglot. You'd realize that it isn't syntax that makes a programming language, but runtime and semantics do. After years of dealing with different PLs, I lost a preference for one specific language - I'd choose the runtime best suitable for the task, and then see if I can bolt Lisp on top of it. And these days, it feels like there isn't a platform left where you can't meaningfully do things via Lisp.
I've got really mixed feelings about Clojure. Like when I read Graham's book I went through a phase of "he wouldn't be fighting with nconc if he was using Clojure!" I find some algorithms to be easy and fun to write using persistent data structures and others maddening though if I did it more I'd get better at it. zippers are weirdly unergonomic because of some little bad choices. I badly wanted to do a project with the Clara rules engine but never got the chance.
As for GNU emacs I got over it. Like I was introduced to it in 1989, it was the cool new editor for the Unix world, one of my friends loved scripting with it. However I experienced it getting really janky about 20 years before other software got janky and I don't know why. Some of it might have been rot in things it depends on, like it used to be curses apps actually worked right but now they suffer data corruption from the telnet/ssh connection and always seem to have an off-by-one on the screen size. X Windows got glitchier. I guess emacs was ahead of its time, now people write janky apps with Electron -- like I have to type R E A L L Y : S L O W L Y when I use Slack at work. Photoshop is like that too, computers are 100x's of times faster so Photoshop should boot up faster than Momo Chiyoda can transform but no, it doesn't.
I switched to vim around 2003 as my emergency Unix editor because I could log into a busted system and start working and not have to rely on the package system (often the thing that was busted) to install emacs or build it from scratch. Helps that it doesn't use continuation characters so cut and past just works, there are lots of little things that make vim live more comfortably side by side with modern GUI life than emacs does.
I'm a longtime emacs head and partisan ... but I learned vi to compose emails in the Berkeley computer lab in 1989 and somehow have never forgotten vi key commands ever since. Emacs is my preferred sw dev editor for most things but I feel like not knowing vi is almost professional negligence if you are on the terminal.
1. Languages that have all the Lisp features are in the Lisp family. Thus languages that are not in the Lisp familly are ones that don't have all the Lisp features. Once a langauge disappears behind the event horizon of the Lisp family, outsiders no longer distinguish it from any other member of the Lisp family; it's just Lisp.
2. Having some features of Lisp is not the same thing as having those features in that form and integrated in that way.
It's not just the roster of features, but how they blend!
If two features have to speak with each other through some interpretation layer, that spoils everything.
You're really misrepresenting Lisp macros here, being able to rewrite syntax is only one (admittedly important) facet of these.
The real thing with CL is that the entire language is available during parsing and macro expansion and that users can hook into these steps to influence them. No artificial limitations like you get in C++'s consteval, you can do everything and anything without having to use a crappy DSL to do it.
Also, I've never found SBCL slow to compile. Have you?
(1) SBCL is fine in terms of speed so far as I am concerned. But wherever you have software engineering in the large, especially involving C++, you have 40 minute builds and a split between smart people who think that's a problem and smart people who don't.
(2) The trouble with those balls-to-the-walls macros is that systems that use them tend to be "write only", like some guy writes 4000 lines of Lisp that do the work of 80,000 lines of C++ and then he moves on and there is never a version 2.0 but the system gets rewritten in C++, supposedly for performance, but really because they couldn't find anybody who could maintain it. It is really pleasing to see how little code is in Lisp systems from the golden age of AI but... when Yahoo! bought Graham's startup they kept the customers but threw away the code.
You are more likely to be sustainable if you get two people to write 12,000 lines in something like typescript or ML that uses Dragon Book technology to the same end.
While I (think) understand the sentiment (ergonomics and practicality), it is still worth studying Lisp for a number of reasons. In particular, I would recommend to everyone to study Lisp 1.5 [1] to appreciate how an entire universe of programming languages can be bootstrapped with just a few primitives and equations.
It is true that, today, we have a vast array of impressive tools at our disposal, from parser combinators and generators, code generators to entire language workbenches with projectional editing capabilities. However, if one were to design a language for any reason, having a deep understanding of the expressiveness of computational models such as the lambda calculus would certainly be an "advantage" (especially for those who end up having to use the language): A Lisp/Scheme is as close to interactive lambda calculus as it gets.
From there on, one can learn about implementing different evaluation strategies, scoping rules and continuations (William Byrd's "The Most Beautiful Program Ever Written" [2] also to mind).
Now, I am not sure whether homoiconicity tends to lead people astray. It is true that is it not strictly necessary to make a language extensible (e.g., Smalltalk has no macros and is very extensible due to its powerful meta-object protocol and an elegant syntax for closures), but it's still worth studying the concept. For example, writing a metaintpreter in Prolog [3] is surprisingly easy because of its homoiconicity.
I thought Scheme was just an educational tool - a minimal language that allows the student to explore language construction & design relatively quickly. I'm not sure I understand your comment about homoiconicity leading people astray. Isn't that the characteristic feature of lisp? Doesn't that dramatically simplify generating code (in principle)?
That's one use. It's also used in commercial and open source systems. Guile, for instance, has been used as an extension language (like lua and others) in many projects.
I find it interesting that a number of PLs were initially developed as educational or research tools but later expanded for general use. Some examples are Pascal, Scheme, Prolog. AFAIK many development languages are used in education so the distinction is rather fuzzy.
Several Scheme implementations, e.g., Chicken, Chez, Gambit, are capable of producing almost any type of program. I'm most familiar with Chicken Scheme. Comes with a large "standard" library plus a broad range of available extensions. Its easy to use FFI adds to productivity.
My experience anyway. Of course, mileage varies widely among users of any language.
Yea sorry. I didn't mean to criticize or diminish its value by saying it's just an educational tool. It's a powerful tool that lets you do some advanced things quickly. That's totally valuable outside of an educational setting. And I didn't mean to imply that there's any limitation to its use.
I just don't understand the criticism in the post I was replying to.
From my experience, to be happy I would need the perf of sbcl, the syntax, litterals and data structure of clojure, the begginer-friendlyness of drracket, the type system of ocaml, and the dev experience of rust.
Does that exist somewhere ? I hope jank gets there. Or maybe roc will.
At this point, given the progress and expectations of using LLM, writing code is going to be purely a hobby concern, like carving wood furniture became a hobby once you have Ikea.
Please say more about the "dev experience of rust." As a long time Lisper and not much of a Ruster, I found the dev experience very painful. Granted, I expect had I continued using Rust that would have abated in tjme. I also disliked Clojure at first until I became comfortable with it and productive, before coming to like and appreciate it.
- a single, canonical, sanctified toolchain for "all the things that are require but don't matter"
What's the "cargo xxx" of lisp ? It depends. Maybe it's "quicklisp something". Maybe it's "ASDF something". Maybe it's a mixture of three different things and you ask a dumb question on SO that should be in the first page of the manual, and you get 10k views : https://emacs.stackexchange.com/questions/83200/given-a-bran...
I know, you enjoy the power of having options, configuring everything, and shaving your yaks to the bone with macros over macros.
But I suspect you never hear of so many questions about "retro antagonist polymorphic macros over monadic functional arrowd", because people give up at "how do I import a lib to make an http call" ?
- a single, standard and opinionated way to lay your frigging files on your effing disk. "cargo new". And, no, it should NOT require people to create a top level "common-lisp" folder to store all their projects.
- adding and loading deps. Sorry, typing quicklisp:xxxx in every repl session you open is not "cargo add".
- a baked in way to run some sort of unit test suite. Have dozens of options if you want, but what is the "cargo test" ?
Of course all those problems have been solved by various tools somewhere in 1997, so it will sound like an "entitled millennial" comment.
But, soon, the tagline to aim for will be "a lisp for the 100 years of lisp". What's that ?
> because people give up at "how do I import a lib to make an http call" ?
Ironically, this is where I gave up on trying to learn Rust. I just wanted to make a HTTP request and the simplest solution was to… download a library with hundreds of dependencies taking up half a gigabyte?
I think OCICL will meet your expectations more readily than Quicklisp. QL does things more the CPAN way (think Gems if you're more into Ruby than Perl).
> a baked in way to run some sort of unit test suite. Have dozens of options if you want, but what is the "cargo test"?
That actually is ASDF, to be more specific:
(asdf:test-system system &rest keys &key force force-not verbose version &allow-other-keys)
Although if we're honest, the real way to run a test suite in CL is to open the REPL, load your system, and just type something like (run-test-suite).
That actually seems great ! My understanding is that it has to wrap ASDF package, so is there a risk of trying to find a well known lib, but not finding it because the authors refuse to create a ocicl package for some complex political reason (or sheer lazyness ?)
Is cargo really a reason to love rust? I'm not a full-on rustacean but in my limited experience cargo certainly had its flaws and warts like any other build system.
I find it a little puzzling when a language ships the one build system to rule them all. I get that it saves some decision stress but it also is a monoculture. Gradle vs Maven in Java at least puts competitive pressure on each; c/c++ obviously have many options; even Haskell had/has stack vs cabal.
* [clj-coll](https://github.com/dtenny/clj-coll) - Clojure collection and sequence APIs in Common Lisp, with optional Clojure collection syntax.
* provides immutable Cons, Queue, PersistentList, capabilities as well as Vector, Set, and Map analogues built on FSet (but accessed entirely via Clojure APIs).
* optional read syntax so you can type `{:a 1 :b 2}`, `#{1 2 3}`, and `[1 2 3]`.
I'm surprised there's no call out highlighting Common Lisp and Clojure's "liveness" compared to Racket (and Scheme). Racket is "batch compiled" wherein CL and Clojure you're editing a live image.
apart from anything else it has a really beautiful object system. I feel like racket and ocaml are two languages where the object system doesn't get enough love.
It is typed, but it's not very well typed. List is 'null or cons' for instance, not 'null or (cons * list)'. Also lack of generics so you can't have a class which holds an element of a generic type which can be determined later on in the compilation process (e.g. a weight-balanced tree which holds elements of a given type).
> Common Lisp lacks some conveniences that have become common in newer languages, such as concise literals for a wider variety of data structures, persistent immutable collections, lazy sequences and built-in general-purpose pattern matching.
I'm really quite drawn to lisp (in general) but I keep bouncing off it. I think the main reason is I just don't know how to effectively work with a dynamically typed programming language. I think for me the affordances that a language provides to change a program over time matter much more than the features that aid in writing programs. I want to know before I run my program whether my program is sound. I appreciate what other people are able to do with lisp(s) and I want to experience that for myself. But I think what I need is to let go of having a compiler yell at me when I remove a field from a type but that I still have functions that expect that field (for example). Is it just more tests? Live REPL editing? It seems like this can only cover so much surface area compared to compile time type checking. I don't think statically typed languages are better than dynamic ones or vice-versa. I just am not as effective with the dynamic ones.
Really should include Emacs lisp, as it's a something many in academia happen upon for LaTeX or some other side use.
While Emacs Lisp is confined to the editor, it also shows that the Editor is not that, and much has been said of it as an OS, an Application Platform, and so on.
Since about 1992, I've used it as platform for many things as well as a general purpose text editor I couldn't initially figure out how to quit.
I'm sure many here are more fervent about Emacs, living in the terminal switched to living in Emacs for a lot of folks... that's really a testament to the usability gravity of an integrated computing platform with Lisp as it's only scripting language.
hnarayanan | a day ago
:)
nobleach | a day ago
tmtvl | a day ago
nathan_compton | a day ago
criddell | a day ago
Elisp::Emacs as AutoLISP::AutoCAD. AutoLISP was my first introduction to Lisp-style language. When I first started using it (1987) for macros in AutoCAD, I really had no idea what Lisp was. It was just a fun and easy way to automate AutoCAD.
hnarayanan | a day ago
timonoko | a day ago
Strange they did not make OpenSCAD in AutoLISP-style.
I have to read the manual all the time, because I never learn the weird syntax of OpenSCAD for-statement.
0xb0565e486 | a day ago
I just find readability such a hurdle regardless of how long I used it. I didn't find that it ever became as natural as the other group of programming languages.
I find a procedural style of programming so much easier to reason about, both when writing and reading.
Either way, I'm really happy I took some time to learn it and use it a little at some point.
dieggsy | a day ago
You do have to keep up with the parentheses of course, but editor settings or extensions can make this automatic if not invisible.
groundzeros2015 | a day ago
I do find that most of my lisp skills carry over to JavaScript quite well while allowing me to write imperative functions more fluently.
Prog blocks are pretty good. I wonder if another DSL could be better.
hnarayanan | a day ago
lvncelot | a day ago
iLemming | a day ago
groundzeros2015 | 22 hours ago
whartung | a day ago
Then do that.
There's nothing stopping you from using pretty much any style of programming that you like. Or mix and match. Or evolve over time.
Loops, lists, arrays, structures. Simple iteration: dotimes, dolist, loop. If those are your bread and butter, then feast! CL will happily do that. That's what I do. I just don't think "functionally" when I do CL code, I'm just not there yet, so its unnatural for me, and not what comes spewing out of my fingers when I write code.
And it's "OK".
You don't have to use the other features of the language, but they're there if you want to dip your toe into it.
With CL, also, I tend to be really wordy on variable and function names. I'm really fond of kabob-case-for-identifers.
Jtsummers | a day ago
em-bee | a day ago
ux266478 | a day ago
iLemming | a day ago
I've had the same complaints when I started. I think, realistically, every programmer who's learning Lisp after getting experience in a bunch of other languages has to deal with that. The mental overhead feels real. Yet, after a while, there's some psychological threshold - Lisp starts feeling more intuitive. At some point, there's just no turning back - nothing ever will feel again more readable than Lisp code. It's just like riding a bike. Once you "get it", there's just no way to "unget it" back.
dieggsy | a day ago
- https://sr.ht/~dieggsy/whisper/
- https://dieggsy.com/json-literals.html
And could also be used to build languages, supporting more modern programming paradigms (though yes, I believe Racket does make this easier):
- https://coalton-lang.github.io/
I also might have written the Common Lisp example using reduce as well, which is in the standard library, but that's preference. Nice to have the option though:
BoingBoomTschak | a day ago
dieggsy | a day ago
pooyamo | 14 hours ago
BoingBoomTschak | a day ago
Warning about the issues that come with ANSI CL's frozen spec (threads/sockets/unicode/extensible sequences/gray streams/etc... as extensions with a varying amount of support with compatibility layers often available to write portable-ish code, "bolted-on" CLOS never fully integrated) and its various rust spots, not just the good points.
Mention that CL has provisions for gradual typing (with limits) which are exploited by SBCL.
Scheme, obviously, along with the same warning as CL about pain of writing portable code that interacts with the OS (does it have compatibility layers like CL?) amplified by the R6RS vs unfinished R7RS-large mess.
A few words about the build system/third-party packaging situation and alternative implementations.
groundzeros2015 | a day ago
It’s funny to me that it was critiqued for being “bloated” when now it looks like a focused minimal library.
dismalaf | a day ago
Also, SBCL has some nice features specific to them, I'm sure it's the same for other implementations. So while there's a lot that's common between them all I find myself using a lot of platform specific functions.
Jtsummers | a day ago
pfdietz | a day ago
ChrisArchitect | a day ago
A road to Lisp: Why Lisp
https://news.ycombinator.com/item?id=48845209
ynniv | a day ago
https://github.com/modus-lisp/modus
Since you can't use an OS by itself, I've rounded out the Common Lisp environment with portable ssh client and server, web browser, and a bitcoin node. Framebuffer with VNC in the pipe
whartung | a day ago
God help me if I fell down a hole like that.
I must say, however, that e.g. code like (compile-compound) is something only an AI can love!
ynniv | a day ago
BoingBoomTschak | a day ago
nobleach | a day ago
What I like about this article is that it walks through the different "camps" of Lisp. Scheme is so intriguing to me because of how small it can actually be. I can build nearly any paradigm I want to exist. The problem is, if I were to actually go find a job where they were using a Lisp, (I hear those actually exist) they wouldn't want to use my "Result monad + match statement - railway pattern" that I've used from OCaml and Rust. So learning something that is truly "common" can make more sense.
As far as learning though, Scheme feels "just right". I've imposed a "no AI until I've found a working solution" rule that keeps my mind engaged. Couple that with a willingness to say, "I don't know that right now... I'll think about it throughout the day and maybe by this evening I'll have an answer".
AyanamiKaine | a day ago
Writing scripts using [0] Babashka is also really nice.
[0] https://babashka.org/
nobleach | a day ago
jlarocco | a day ago
I've used it a tiny bit at work (on Windows) and at home (on Linux), and ran into one issue with "out" parameters, but otherwise it works really well.
nobleach | a day ago
vindarel | 23 hours ago
> Common Lisp implementation on .NET. Lisp source is compiled to CIL (Common Intermediate Language) and runs on the .NET JIT — so the same Lisp image runs on Windows, macOS, and Linux across x86-64 and ARM64 without per-platform porting work.
jlarocco | 18 hours ago
arikrahman | a day ago
Jtsummers | a day ago
What's that supposed to mean? Many (probably most if we only consider the non-toy ones) lisp implementations are "native" (compiling to native machine code, not interpreted).
erichocean | a day ago
Jtsummers | a day ago
That's a unique definition of "native". It suggests that C is not a native language, which is going to be a hard thing to convince others of.
didibus | 23 hours ago
yogthos | a day ago
I've already got enough of JVM compatibility to run Ring apps, and have some fun libraries like a Reagent style library on top of GTK https://yogthos.net/posts/2026-07-02-jolt.html
xedrac | a day ago
waffletower | a day ago
snorremd | a day ago
Now there are of course limitations to what you can do in terms of not supporting Java reflection or the full Clojure compiler. But I've made some nifty small scripts and convenience helpers with it. And the dev experience of making these scripts is so much nicer than trying to write bash scripts. The Clojure edn syntax is super simple, and the REPL connected editor let me rapidly test parts of the code just like with full Clojure apps.
I don't have experience with other lisps, but I can vouch for Clojure being very nice. The community was welcoming and friendly to newcomers when I started learning, I hope it still is. One thing I love about the Clojure ecosystem and community is the effort taken to never break libraries. I've looked at libraries I used some ten years ago, and the API is still compatible with code I wrote back then. There is very little churn. Maybe this is because the language is largely untyped and editors only partially check "types". Having breakages in libraries you consume once every couple of months would get really tiring in Clojure land. I'd imagine the same problems would present themselves in Common Lisp and others.
pjmlp | a day ago
zem | a day ago
erichocean | a day ago
Codex can one shot the bindings flawlessly, and the interface is significantly faster for downcalls vs. JNI.
pjmlp | 17 hours ago
zackmorris | a day ago
I had used C++ for several years to make shareware games, so I took a test to challenge some programming courses. I vaguely recall doing well, but my advisor encouraged me to take them anyway. I'm glad that I did, because I had little understanding of theory.
Funny story: the instructor never mentioned that we could use more than one line of code. So every single piece of homework that I handed in, and every test, was one giant line of nested logic. Which worked better than one might expect, and completely changed how I wrote code from that point forward. That's how I made the connection a decade later that functional programming is akin to a spreadsheet, as are higher-order method chains and immutable variables.
I think of Clojure as being a layer above Lisp, sort of like how Swift might be considered a layer above Objective-C/Smalltalk. However, bare Lisp has problems around not quite giving enough out of the box. It's minimalist enough that developers end up reinventing the wheel for things that should probably be provided by a layer/library similar to Scheme or Clojure.
To digress, I feel that mutable variables and even monads are a code smell in functional programming since they can cause impurity. They're more of a crutch to ease conversion of code from imperative languages. However, monads can be useful to simulate every path through a program, sort of like superposition in quantum mechanics and SAT solvers. So they aren't necessarily bad, just taught incorrectly, probably because they're so hard to grok.
I'd vote to settle on a series of layers like Common Lisp -> Scheme/Racket -> Clojure/Elisp, with the final layer providing the intersection of features available from the most widely-used Lisp variants. Note that this is specifically to form a bridge from imperative languages, so research work might need additional DSL features brought forth from the Racket layer.
Edit: I forgot to mention that Scheme is a good fit for genetic algorithms, see books by John Koza (no affiliation). My feeling is that we haven't seen anything yet regarding what problems AI can solve, since it's having to do it the "bare hands" way with LLMs and pattern matching.
sph | a day ago
If you want a Scheme with batteries included, I recommend GNU Guile. Also worth your time are Racket, Clojure, Janet.
hackermailman | a day ago
nobleach | a day ago
Diogenesian | 20 hours ago
It's free online: https://scheme.com/tspl4/
wollowollo | a day ago
bbkane | a day ago
pratikdeoghare | a day ago
This would be my advice. Why? My own road was haphazard. Other books broaden your mind and teach you really cool tricks. This book gets you using lisp like you would say golang. But it still teaches you the lisp things and broadens your mind. Time spent choosing will be better spent reading this book. After that PAIP, On Lisp, SICP etc.
mktemp-d | a day ago
An Introduction to Programming in EMacs Lisp is also good for the first few chapters even if you don’t use emacs because you are given fundamental concepts of lisp that can be applied to the understanding of other dialects. It’s also free.
Learn you a Haskell (despite Haskell being not a flavor of list, they share similar DNA ) is great at understanding functional programming with lisp like languages.
em-bee | a day ago
davidw | a day ago
For instance "I'm new to Lisp, I want to try one..." is a person without a lot of background and information to make that choice. And they probably realize it and it makes them nervous about making that choice.
See: https://en.wikipedia.org/wiki/The_Paradox_of_Choice
goatking | a day ago
I would be happy with (neo)Vim setup as well, but that was way behind Emacs and broken when I tried.
jacobobryant | a day ago
goatking | a day ago
dieggsy | a day ago
I have not tried it, I'm an Emacs nerd.
ux266478 | a day ago
goatking | a day ago
However, price for hobby user license at 750 USD is laughable.
aptenoforst | a day ago
https://coalton-lang.github.io/20260424-mine/ and https://lem-project.github.io/
iFire | a day ago
Why prefer lisp-1 over lisp-2 or vice-versa?
dismalaf | a day ago
Common Lisp and Racket are Lisp-2s but honestly, the namespace thing seems like a minor difference compared to all the other features that differentiate them.
gus_massa | a day ago
dismalaf | a day ago
jwr | a day ago
guenthert | 13 hours ago
Jtsummers | a day ago
wk_end | a day ago
The classic example is, imagine you have a function with a local variable called “list”, common enough. Now imagine you invoke a macro inside that function which generates a call to the built-in “list” function - also common enough. In a Lisp-1 without hygiene that breaks - your local definition shadowed the built-in; in a Lisp-2 or hygienic Lisp-1 you’re in the clear.
nathan_compton | a day ago
Jtsummers | a day ago
nathan_compton | a day ago
Lisp 2 advocates typically make a few arguments. One is that having a separate namespace for functions makes it clearer when you are using a function vs another value. The second is that the evaluator has less work to do when examining the head of a list - it needs only look in the function environment, not the full environment.
On the first subject I must disagree - you can bind a function to a regular variable and then use that variable everywhere (except in the car of a list representing a function call), so for most positions in a set of expressions you don't really get information about whether the object being denoted is a function or not.
I suppose the second point is somewhat valid, though I suspect if you benchmarked interpreters and compilers it would barely matter. As a person who favors functional programming with a lot of combinators, I find Lisp 2 introduces a lot of pointless noise in the syntax for no reason. And I fundamentally just don't see functions as significantly different sorts of values, so I find the syntactic distinction bizarre.
sph | a day ago
zem | a day ago
coryrc | 23 hours ago
tmtvl | 23 hours ago
xigoi | 14 hours ago
tmtvl | 13 hours ago
kazinator | 20 hours ago
Would you want to use a weird POSIX shell in which "for x in *.jpg; ..." stopped working because you assigned "for=42"?
Yet Lisp-1 has a notational advantage for programs that work with functional values; programs that indirect upon functions are more succinct, free of "shim" operators for lifting values out of the function binding space, or requesting application of a function value.
In the TXR Lisp dialect, I worked out a way to have the notational convenience, without bringing in the Lisp-1 issue.
1. The substrate, including macro-expansion logic, is thoroughly Lisp-2.
2. When a compound form is written in square brackets, it indicates that immediate elements (those constituents that are symbols) are to be looked up in a single namespace that is a merger of the function and variable namespaces according to precisely documented rules. This is not recursively applied to the form; its arguments that are compound forms are not treated this way unless they use square brackets.
3. Macros are not affected. In a form [a b c ...], the element a is neither recognized as a special operator or as an operator macro. If it is a symbol, it is either a variable, or a symbol macro.
4. [ ... ] is a surface syntax with a straightforward representation: the (dwim ...) operator. The above semantics is thoroughly baked into the macro expander and correctly treated at all necessary levels of the language: the handling of lexical scopes at expansion time and compilation/evaluation.
This system leaves mainly just one small infelicity. The merged 1+2 namespace is not available in certain contexts when it would be handy. Like say we are writing a function that takes a functional argument, that we would like to default:
There is no variable equal. We cannot do this: because the (test-fn equal) syntax is not a form to be evaluated; it is just notation within the parameter list which has not been equipped with support for the alternative brackets/dwim structure. The way to do this is: fun* is like Common Lisp's function* operator. It's one of the few instances you ever have to use it, the others also being situations like values in binding constructs such as let. It is not worth complicating things to provide a way to take the fun out these situations.belmarca | a day ago
Both highly recommended.
kscarlet | a day ago
adamddev1 | a day ago
https://htdp.org
That helped me to think about recursion, functional programming, and type driven development. After going through HTDP I was able to breeze through complex problems that were unsolvable before.
fuzztester | 7 hours ago
examples?
adamddev1 | 6 hours ago
QuadmasterXLII | a day ago
pfdietz | a day ago
zem | a day ago
lioeters | a day ago
> The best LISP is the one you create yourself. You learn so much about programming by implementing your own language, and LISP is brilliant for this purpose thanks to its simple syntax and homoiconicity (code is data, data is code). It's also a great way to learn a new existing language, like Rust or Go, by writing a LISP interpreter with it.
gnabgib | 18 hours ago
lioeters | 17 hours ago
tmtvl | 13 hours ago
phplovesong | a day ago
Lisps have many fantastic ideas, but are really hard to read. Lisp code is what we had before perl guys went "hold my beer".
I know its just syntax, and it usually does not matter, untill it does. I did some clojure a long time ago, and before that some CL, and god, i cant understand my own old code. Contrast that to some language that has syntax i can read it still, years later. Go being the prime example of write once, read a decade later.
nathan_compton | a day ago
spdegabrielle | a day ago
prakashrj | a day ago
Once you get hang of it, it easy. Following is video of my son who learnt Scheme as first programming language.
spdegabrielle | a day ago
PaulHoule | a day ago
To be specific, you can work all of the examples in Graham's On Lisp in Python except for one of the last chapters where he implements continuations that really need macros -- but this is basically the async/await facility that Python already has. The other examples use macros for performance but work fine with just plain functional programming.
React with hooks is an example of that kind of system at work -- the JSX transformation is a very simple shim you can put in front of the JS compiler and the hooks themselves are the kind of trick that On Lisp teaches you how to do.
On Lisp doesn't use the kind of tree-walking macros that really are unique to Lisp. And... the techniques in the Dragon Book for writing compilers are the real magic. If looking to Lisp as an old shiny keeps you from learning how to write compilers, it is holding you back.
I think the homoiconic thing leads people astray. There's a real tension that, for performance reasons, mainstream compilers aren't extensible, for instance you might want to write a
control structure in a new language and for something in Java that is really a production rule in the grammar, maybe a class to represent the unless block, and a rewriting rule that gets applied to the AST. If the compiler was designed to be easily extensible that would be less code than the POM file for the project. But it's not so it isn't.Many things hold us back.
The industry has been so traumzatized by slow compiles that trading speed for extensibility doesn't sell to the people who create languages. Also once you have the sophistication to make things like parser generators you know how to get things done with the terribly unergonomic parser generators we have and don't have a lot of empathy for all the programmers out there who might be using parser generators if any of them were built as if usability matters.
pjmlp | a day ago
PaulHoule | a day ago
i had a phase when I was using PyPi a lot for branchy "old AI" kinds of workloads and felt it was an easy win but since then it has been either numpy or PIL or pytorch doing the heavy lifting or scripty stuff like uploading files to S3 where performance doesn't matter a lot.
I will grant that Common Lisp can be compiled to run amazingly quickly!
BoingBoomTschak | a day ago
This is how https://github.com/marcoheisig/Petalisp#why-is-petalisp-writ... and https://github.com/numcl/specialized-function can exist.
pjmlp | 17 hours ago
guenthert | 13 hours ago
Common Lisp, as an example, makes some deliberate flexibility (and elegance) vs. performance trade-offs. I'd think to speed Python up more than PyPy could, one would have to sacrifice some of its features (some of those might not be all that popular or even desirable).
Nah, Python has its place. If its performance isn't adequate for the problem at hand, rewrite the bottlenecks in C++ and be done. LLMs should make that easy, no?
pjmlp | 12 hours ago
However CPython isn't even half way there to either Papyrus or GraalPy.
The community attitude to rewrite into C, C++, Rust, Zig, whatever, is exactly why Python will never be like other dynamic languages, and the dynamism excuse will always be used.
Thankfully at least GPU vendors see it otherwise, so if nothing else at least it will get JITs that speak GPU machine code.
BoingBoomTschak | 11 hours ago
Which is something that can be done in CLOS, though. And in a much more thought out fashion, as usual with the CLOS/MOP, cf https://www.lispworks.com/documentation/HyperSpec/Body/04_cf...
iLemming | a day ago
Working with Clojure is an absolute delight. It strips down all the dogma and let's you deal with the "business logic" as if you're cooking steak using no BS ingredients - meat is meat, herbs are real, stove is hot.
Why would I ever choose bash for writing anything slightly more complex than simple redirection, when I can do things in way better fashion with babashka. Why would I wrestle a YAML CI pipeline that only fails on push, when I can drive the whole thing from a babashka task file, run each step locally in the REPL, and actually debug it?
Why would I ever deal with Lua, if I can't even format it for "readability" - no matter how I do it, it just looks darn ugly, and luafmt often makes it worse. Why, if I can just slash down dozen lines of Lua boilerplate compressing it into a three-liner Fennel macro? With Fennel, I can interactively poke through elements of my WM through Hammerspoon on Mac, and that's just bananas.
Why would I ever deal with JSON, when EDN is almost twice as compact and far more readable - I can align things and treat data as a literal table. Besides, I can group, sort, filter, slice, dice, salt & pepper that data easily, without ever leaving my trusted editor.
Why would I choose to build a web-scraper in Python, when I can use nbb driving Playwright and go through selectors interactively, directly from my editor, as if it is a devtools console. And I don't even have to restart anything, deal with state changes, etc.
How can I abandon Emacs where I can just open a scratch buffer, type some Elisp and change the behavior of my editor, my WM, my OS and even things on remote computers. No other text editing environment works the way Emacs does - nothing even comes close. It feels like playing a video game, where my controller in my editor.
Why would I write Flutter UIs in Dart, fighting the widget-tree ceremony and endless build() boilerplate, when ClojureDart lets me express the same tree as plain data and hot-reload it interactively? The layout is just nested maps and vectors.
Why would I reach for C when I need to embed a small, fast scripting layer. Text parsing alone would be a regex nightmare elsewhere.
Why would I bolt a templating engine onto HTML strings, when Hiccup makes markup just vectors - so my views compose, filter, and generate like any other data, no special templating DSL to learn
And with all sorts of different runtimes and dissimilar Lisp dialects, it still feels as if you're working with the same language. The mental overhead when switching is so negligible. While switching between just JS and TS - which are supposed to be of the "same family" - feels quite annoying. Despite the fact that I've put years into those - far longer than any Lisp I've ever used.
Sure, nothing special about Lisp at all. Except that practicing Lisp can actually make you a polyglot. You'd realize that it isn't syntax that makes a programming language, but runtime and semantics do. After years of dealing with different PLs, I lost a preference for one specific language - I'd choose the runtime best suitable for the task, and then see if I can bolt Lisp on top of it. And these days, it feels like there isn't a platform left where you can't meaningfully do things via Lisp.
PaulHoule | a day ago
As for GNU emacs I got over it. Like I was introduced to it in 1989, it was the cool new editor for the Unix world, one of my friends loved scripting with it. However I experienced it getting really janky about 20 years before other software got janky and I don't know why. Some of it might have been rot in things it depends on, like it used to be curses apps actually worked right but now they suffer data corruption from the telnet/ssh connection and always seem to have an off-by-one on the screen size. X Windows got glitchier. I guess emacs was ahead of its time, now people write janky apps with Electron -- like I have to type R E A L L Y : S L O W L Y when I use Slack at work. Photoshop is like that too, computers are 100x's of times faster so Photoshop should boot up faster than Momo Chiyoda can transform but no, it doesn't.
I switched to vim around 2003 as my emergency Unix editor because I could log into a busted system and start working and not have to rely on the package system (often the thing that was busted) to install emacs or build it from scratch. Helps that it doesn't use continuation characters so cut and past just works, there are lots of little things that make vim live more comfortably side by side with modern GUI life than emacs does.
spopejoy | 6 hours ago
kazinator | 21 hours ago
2. Having some features of Lisp is not the same thing as having those features in that form and integrated in that way.
It's not just the roster of features, but how they blend!
If two features have to speak with each other through some interpretation layer, that spoils everything.
BoingBoomTschak | a day ago
The real thing with CL is that the entire language is available during parsing and macro expansion and that users can hook into these steps to influence them. No artificial limitations like you get in C++'s consteval, you can do everything and anything without having to use a crappy DSL to do it.
Also, I've never found SBCL slow to compile. Have you?
PaulHoule | a day ago
(2) The trouble with those balls-to-the-walls macros is that systems that use them tend to be "write only", like some guy writes 4000 lines of Lisp that do the work of 80,000 lines of C++ and then he moves on and there is never a version 2.0 but the system gets rewritten in C++, supposedly for performance, but really because they couldn't find anybody who could maintain it. It is really pleasing to see how little code is in Lisp systems from the golden age of AI but... when Yahoo! bought Graham's startup they kept the customers but threw away the code.
You are more likely to be sustainable if you get two people to write 12,000 lines in something like typescript or ML that uses Dragon Book technology to the same end.
patterns | a day ago
It is true that, today, we have a vast array of impressive tools at our disposal, from parser combinators and generators, code generators to entire language workbenches with projectional editing capabilities. However, if one were to design a language for any reason, having a deep understanding of the expressiveness of computational models such as the lambda calculus would certainly be an "advantage" (especially for those who end up having to use the language): A Lisp/Scheme is as close to interactive lambda calculus as it gets. From there on, one can learn about implementing different evaluation strategies, scoping rules and continuations (William Byrd's "The Most Beautiful Program Ever Written" [2] also to mind).
Now, I am not sure whether homoiconicity tends to lead people astray. It is true that is it not strictly necessary to make a language extensible (e.g., Smalltalk has no macros and is very extensible due to its powerful meta-object protocol and an elegant syntax for closures), but it's still worth studying the concept. For example, writing a metaintpreter in Prolog [3] is surprisingly easy because of its homoiconicity.
[1] https://softwarepreservation.computerhistory.org/LISP/book/L...
[2] https://www.youtube.com/watch?v=OyfBQmvr2Hc
[3] https://www.metalevel.at/acomip/
loose-cannon | a day ago
Jtsummers | 23 hours ago
That's one use. It's also used in commercial and open source systems. Guile, for instance, has been used as an extension language (like lua and others) in many projects.
jrapdx3 | 23 hours ago
Several Scheme implementations, e.g., Chicken, Chez, Gambit, are capable of producing almost any type of program. I'm most familiar with Chicken Scheme. Comes with a large "standard" library plus a broad range of available extensions. Its easy to use FFI adds to productivity.
My experience anyway. Of course, mileage varies widely among users of any language.
loose-cannon | 23 hours ago
I just don't understand the criticism in the post I was replying to.
kazinator | 21 hours ago
Can confirm; got a few hints from the 1988 edition when working on the TXR Lisp compiler.
Oh, and also when implementing regexes, not to forget!
However, the Lisp compiler has its own magics that are not exactly covered in the Dragon Book. Some really nice way of doing things.
arikrahman | a day ago
phtrivier | a day ago
Does that exist somewhere ? I hope jank gets there. Or maybe roc will.
At this point, given the progress and expectations of using LLM, writing code is going to be purely a hobby concern, like carving wood furniture became a hobby once you have Ikea.
So we might as well use fun tools :)
totalperspectiv | a day ago
SomeHacker44 | a day ago
phtrivier | 16 hours ago
What's the "cargo xxx" of lisp ? It depends. Maybe it's "quicklisp something". Maybe it's "ASDF something". Maybe it's a mixture of three different things and you ask a dumb question on SO that should be in the first page of the manual, and you get 10k views : https://emacs.stackexchange.com/questions/83200/given-a-bran...
I know, you enjoy the power of having options, configuring everything, and shaving your yaks to the bone with macros over macros.
But I suspect you never hear of so many questions about "retro antagonist polymorphic macros over monadic functional arrowd", because people give up at "how do I import a lib to make an http call" ?
- a single, standard and opinionated way to lay your frigging files on your effing disk. "cargo new". And, no, it should NOT require people to create a top level "common-lisp" folder to store all their projects.
- adding and loading deps. Sorry, typing quicklisp:xxxx in every repl session you open is not "cargo add".
- a baked in way to run some sort of unit test suite. Have dozens of options if you want, but what is the "cargo test" ?
Of course all those problems have been solved by various tools somewhere in 1997, so it will sound like an "entitled millennial" comment.
But, soon, the tagline to aim for will be "a lisp for the 100 years of lisp". What's that ?
xigoi | 14 hours ago
Ironically, this is where I gave up on trying to learn Rust. I just wanted to make a HTTP request and the simplest solution was to… download a library with hundreds of dependencies taking up half a gigabyte?
tmtvl | 14 hours ago
> a baked in way to run some sort of unit test suite. Have dozens of options if you want, but what is the "cargo test"?
That actually is ASDF, to be more specific:
Although if we're honest, the real way to run a test suite in CL is to open the REPL, load your system, and just type something like (run-test-suite).phtrivier | 12 hours ago
spopejoy | 7 hours ago
I find it a little puzzling when a language ships the one build system to rule them all. I get that it saves some decision stress but it also is a monoculture. Gradle vs Maven in Java at least puts competitive pressure on each; c/c++ obviously have many options; even Haskell had/has stack vs cabal.
spdegabrielle | a day ago
vindarel | 23 hours ago
* [clj-coll](https://github.com/dtenny/clj-coll) - Clojure collection and sequence APIs in Common Lisp, with optional Clojure collection syntax.
* [clj-con](https://github.com/dtenny/clj-con) - Clojure-style concurrency operations in Common Lisp.* [clj-re](https://github.com/dtenny/clj-re/) - Clojure-style regular expression functions.
* [clj-arrows](https://github.com/dtenny/clj-arrows) - Clojure-compatible threading/transformation/arrow macros for Common Lisp.
editor: https://coalton-lang.github.io/20260424-mine/
type system: Coalton
Octplane | a day ago
https://janet-lang.org/
dmux | a day ago
SilentM68 | a day ago
zem | a day ago
lgrapenthin | a day ago
xvilka | a day ago
kanbankaren | a day ago
xvilka | 23 hours ago
tmtvl | 23 hours ago
avph | a day ago
ozhero | a day ago
Motivates me to learn more about Lisp (and Clojure)
vindarel | 23 hours ago
we can explore libraries on https://github.com/CodyReichert/awesome-cl/
concise literals: serapeum's dict or other libraries bring you { } for hash tables and the like.
persistent collections: FSet (and more)
lazy sequences: series, gtwiwtg (generators) (and more)
pattern-matching: Trivia
many libraries are shipped in CIEL: https://ciel-lang.org/ (shameless plug)
---
> It’s currently used in …
more example companies: https://github.com/azzamsa/awesome-lisp-companies/
also: https://lisp-screenshots.org/
---
> at the cost of slightly slower startup times
that means ±30ms, not the Java-esque slow startup time ;)
---
editors: don't miss https://lispcookbook.github.io/cl-cookbook/editor-support.ht... and the new Mine, OLIVE for VSCode, ICL repl (terminal and browser).
birdfood | 22 hours ago
jasonm23 | 18 hours ago
While Emacs Lisp is confined to the editor, it also shows that the Editor is not that, and much has been said of it as an OS, an Application Platform, and so on.
Since about 1992, I've used it as platform for many things as well as a general purpose text editor I couldn't initially figure out how to quit.
I'm sure many here are more fervent about Emacs, living in the terminal switched to living in Emacs for a lot of folks... that's really a testament to the usability gravity of an integrated computing platform with Lisp as it's only scripting language.
ymmv of course
jiehong | 14 hours ago
jasonm23 | 13 hours ago
again.
cyrc | 15 hours ago
There is a very nice IDE called Mine
https://coalton-lang.github.io/20260424-mine/