Goodbye, Clojure
After ~7 years, I was done with Clojure. I was writing a some CLI apps, and I hated how long they took to start up. The community at large seemed not to care about this problem, except for the babashka folks. However, I spent long, hard hours banging my head against native-image and it just wasn't working out. It was incredibly painful, and at the end of it, I still didn't have standalone, fast-starting native executables. I decided that that was a requirement for my main driving hobby language, and that Clojure didn't have it. It was then that I decided to move on from Clojure.
On the Hunt for a New Lisp
I started shopping around for a new lisp to use after hours like I'd done before with my home projects. I had specific requirements in mind, though I didn't actually list them when I started. I can list them now in hindsight, though:
- It must be easy to create standalone, fast-starting executables using a reasonable toolchain (to address my main concern with Clojure)
- I can't use Emacs, so it's got to be usable in Vim.
- It must have good support for Windows and Mac, in addition just Linux/POSIX operating systems.
- It would be nice if it allows plugging into some other, large-community imperative language, like Clojure does with Java.
- It must have a reasonably fast runtime -- hopefully as good or better as Clojure's.
- The language must have a strong multithreading story. It would be grand if whatever language it was didn't have a GIL, for example.
- Must have a strong community.
- Must have a good ecosystem, with at least the following libraries
well-implemented, thus supporting my common use cases of making CLI tools:
- JSON parsing and serialization
- Sqlite3 support
- HTTP requests library
- Would be nice if it had functional data structures like Clojure's (this ended up being less of an issue later)
I looked at Scheme, but that community seemed to still be fractured over the r6rs/r7rs mess. Also, there wasn't a big enough ecosystem to suit my liking.
I'd already tried Racket in school and didn't like it. The runtime was a bit slow and bloated for my tastes.
I had seen lisp-lang.org shared on the Orange Site. I was impressed with the site. I came back to it later after I first saw it and thought, why not. Maybe I'll give Common Lisp a shot.
Magic Happens Here
I will spare the reader the full narrative of my learning CL. It was a rough ride learning the language. (I went about it the wrong way, getting CLtLv2 for Christmas and reading through that. I eventually found the HyperSpec and started reading that as well.)
There were some weird things I didn't expect to find about CL. It's a standardized language, more like C than Java that way. There are many compilers, interpreters, and runtimes that implement that standard. There's even a tool to help install them all and wrangle them. The most popular one according to the community is SBCL.
If I had heard about Janet when starting this hunt, I might have stopped there and not gone on to CL. Nice syntax, small, fast executables, C FFI, a fun intro book. It checks all my boxes.
However, I'm glad I did learn CL first, because I think I'd miss the CLOS and the Conditions System, things I learned about later in my journey. Common Lisp is just a bit of a stronger language.
Requirements Met
I found answers to all my questions, and decided my next lisp was going to be Common Lisp. I've been coding in it ever since. Here are the things I found:
- Standalone Executables: there are lots of ways to do this in Common Lisp. I summarize my favorite way in another blog post. Start up times range from a fraction of a second to nearly instant, depending on if you compile the executable with compression or not. This is not a bolt-on feature; it's a first-class-citizen way to distribute Lisp programs.
- Vim Workflow: There are a lot of good ones here, but I eventually settled into a Vim workflow of my own. Readers may also be interested to know that I found VS Code perfectly usable as a Common Lisp IDE.
- Windows/Mac/Linux Support: SBCL, a popular implementation and compiler for Common Lisp, supports The Big Three relatively well, as outlined in the blog post linked in point #1.
- Larger Imperative Ecosystem: Most implementations actually hook into the C programming language pretty well through the CFFI. That works for me.
- Runtime Speed: SBCL is crazy fast.
- Multithreading: While the Common Lisp standard does not make provisions for multithreading, all major implementations do support it and their differences are papered over with a library called Bordeaux-Threads. This library serves as an underpinning for the lparallel library, an excellent library for multithreading. There's also cl-async and blackbird for asynchronous programming and promises, respectively.
- Strong Community: I discovered the community as I did the rest of the language -- in fits and starts. A good summary of the community (as it was in 2024) is summarized in the Common Lisp Community Survey 2024. CL features prominently at the European Lisp Symposium. CL has a strong blogosphere and subreddit.
- Ecosystem: The ecosystem is pretty great. Most folks use Quicklisp, though I use OCICL for package management. The Common Lisp Cookbook, the CLiki, and Awesome CL provide nice survey of available libraries and techniques. Here are some answers to those particular libary queries I had:
New Friends Take Note
I wrote this blog post because I noticed that there have been more newcomers on the Common Lisp Discord and they've been asking the same questions I was when I was first looking at the language. I wanted to lay down a bit of history around why I came to Common Lisp, and how I acclimated to the language. I hope it may be helpful to those new to or curious about Common Lisp.