home | blog

One Week With Common Lisp

I've actually been doing a lot of development on personal programming projects lately, which is notable because I'm now in university, and in high school I failed to motivate myself to do much of anything outside of summers. But despite my classes taking up much of my time, I've started... doing things out of boredom. Almost all of my free time that isn't spent chatting with online friends or playing Counter-Strike 2 feels like it's spent inside of Emacs hacking away at some pet project.

Perhaps the most interesting of these was a Qt GUI application for remotely controlling cameras I started building on top of libgphoto2. I got a surprising amount of functionality integrated, even with a technically-unsupported camera—a Panasonic Lumix GH6—and after actually using it I really adore Qt for GUI development. It takes an impressively small amount of effort to get a really nice-looking native Linux app running.

But libgphoto2 has... issues, to say the least. For what it is it's remarkable it works at all with such a swath of cameras, but to me it felt like it had subpar documentation, and extremely questionable thread-safety and general reliability. I found myself fighting the library almost every step of the way, so for the time being I'm shelving the project in a half-working state in favor of more enjoyable things. But what to do without any other C++ GUI applications kicking around in my head and begging to be built? Well, it was time to learn a language that had allured me for quite some time, but which I never seemed to have the motivation to pursue. It was time for me to learn a Lisp.

After asking the programming-inclined of my largely-furry online friend group, I was recommended Practical Common Lisp by Peter Seibel. I started reading the freely-available online version (I love this style of distribution for books, by the way! Another of my favorite programming books, Is Parallel Programming Hard, And, If So, What Can You Do About It? is also distributed freely online, though it's closer to an open-source book to begin with) and I was immediately and totally enraptured and obsessed. The extent of this was so great that I'm now spending my time writing this post to try and document my experiences and explain why I think this is so.

I think the best place to start is to describe the feeling of programming. I've had some people I know tell me they don't enjoy programming, and I can't relate. To me there is a substantial, often visceral rush from the acts of learning and understanding what you are doing in the context of programming. Seeing functionality materialize from nothing, or looking at code and being able to map out what it does in your head just feels plain good. If that feeling is absent, either you're not really 'programmer-brained' in the way people who write REPLs or static site generators for fun are, or maybe you're simply not sufficiently understanding what you're doing as you work through a project. In other words, I think copy-pasting code is often doing yourself a disservice if you're not examining it to fully understand what it's doing, because you might be depriving yourself of a dopamine rush. Especially when you have a virtually finished product but no clue how half of it works because it's bodged-together code snippets taken off StackExchange or, worse, from a large language model.

So why is Common Lisp or, more accurately, why are Lisps in general so special? I've spent a week playing with example code, shoving stuff into the REPL, and using the Common Foreign Function Interface to write Lisp where I really should just be using C++ and... I still don't know why. It's too early to say because this might just be, at least in part, a perverse autistic obsession with The New Thing. I do think there's some tangible differences between it and other languages, though, because learning a Lisp feels quite different compared to learning modern C++ or a bit of Rust.

Some time ago I started learning Rust. Just basic stuff—I only really ended up making a program that read data from a file, loaded it into an array, and partially-randomly ouput array elements in specific patterns. While doing this, I kept having moments where I felt like I was on the edge of some entirely different, alien world of programming. And in retrospect, I know I was; Rust is vastly different language with different and arguably more capable abstractions than the C I was used to. I'm just barely groking some of the easier ones in C++ where they're useful to me. But at the end of the day, glimpses are all I got. Rust felt like a tool that I should probably learn at some point, and that I truly wanted to leverage fully, but that would take a lot of effort to do, and at the time I didn't have the energy.

Common Lisp isn't like that. I don't know for sure that it's not the better documentation/materials for learning it, but I think the simple syntax and small core language makes Common Lisp an excellent vehicle for learning high-level, near-zero-cost abstractions and less-common programming patterns like closures. I am learning far more quickly than I have before, and it comes easier, and it's fun and just looking at the code I write makes me happy in a strange way. For the first time, I feel that I want to program just for the activity itself and in a specific language, rather than for the cognitive reward of solving problems and seeing a finished product at the end of my endeavors. In other words, the act of writing code has been made enjoyable, rather than the completion of programs or units of programs.

Some of this might be the famed iterative/incremental development style the Lisp REPL encourages. When I write a function or macro, I don't need to write a test framework or integrate it into program code to test or debug it, or to see it working—I can just run it. This is apparently a trivial thing in interpreted languages like Python, but I absolutely despise Python more than almost any other languages, and I generally refuse to program in 'slow' languages due to my experiences using terrible Linux phone hardware where performance and resources are extremely limited. But in Common Lisp? It's not wickedly fast, but it is wickedly fast relative to what it actually is, and it's a language where I have an interpreter that I can command to evaluate/run whatever I want. In practice, I would expect this to substantially reduce code churn. If I'm writing an init function for a program, I don't need to write a stub main function that calls the init and dies—I can just run the function in question and redefine it virtually instantaneously every time I make a change. Regardless, though, the end result is that the aforementioned cognitive rewards and dopamine of programming are delivered more frequently in smaller bursts, as opposed to in larger floods as large chunks of functionality are gotten working. This might promote commitment to a project by providing more consistent motivation to work on it.

So thus far I am in love. I'm learning new things, like trying to get better with OpenGL, purely so I can write more Common Lisp. I'm half tempted to start hacking on my Emacs config but I don't know what I'd add. I sincerely hope this isn't just a honeymoon phase, and that my adoration and motivation continue. If I have my way, in 10 years I'll be running Common Lisp programs I wrote on a friend's Symbolics 3640, or better yet on a modern Lisp Machine architecture implemented on a low-power FPGA in a Tandy 100/102-style portable computer.


Now with (forced) HTTPS support!
Now hosted on "Lapis Lazuli" (Dell T620), successor to "Peridot" (HP ProLiant DL380e Gen 8)!