I’ve been experimenting with lisps for about 6 month now, clojure, common lisp,
emacs lisp, and a little racket. I recently finished my biggest code so far,
solving all the Protohackers challenges in Common
Lisp, so I thought I’d share my
thoughts. I also contributed a little to “real software” with bug fixes and
small features in projects but for the most part I don’t have any real
experience yet, this is just my first impressions, not the words of someone
experienced in these languages. The TLDR is: don’t be scared off by the
parenthesis, and don’t assume languages are better because they’re more
“modern”.
If you’re tired of discussion of old topics (parenthesis, editors, languages),
then skip ahead to the protohackers section for dissection of some tricky
concurrency bugs.
(Parenthesis)
Why dedicate the first section to a superficial discusion of syntax? Because I’m
superficial. I like learning different languages, and I like dynamically typed
languages and languages that make functional programming ergonomic but don’t
strictly enforce it. That sounds like a perfect fit for lisp, but I’d never
taken a serious look at it until now, 90% because I thought all the parenthesis
were ugly (the other 10% because I thought lisp is a functional language, and
I’d already done some elixir, ocaml, and haskell, so I thought I wouldn’t really
be learning much new ideas, which was another big misconception).
It really is just a question of what we have most exposure to, english speakers
will think arabic or hebrew are “unreadable” with their different alphabet and
text right-to-left. They will think english is “unreadable” at first. Lisp is
visually different enough from other languages to be harder to read at first, if
we have many years experience with them and none with lisp. My progression has been more or less:
Starting out: this is ugly and harder to read
After a few weeks: this is not so bad after all
After a few months: maybe I’m actually coming to prefer this
It seems to happen to everyone, and is said the be the reason no alternative
syntax has taken off: by the time programmers have enough experience with lisp
to design an alternative syntax based on whitespace or something rather than
parenthesis, they no longer want to. Is it just lisper’s stockholm syndrome? It
is objectively a simple and consistent syntax and there are various benefits
that come from that:
-
Macros
How many rust programers are comfortable writing procedural macros? How many
ocaml programmers are comfortable with ppx or haskellers with template haskell?
All lisp programmers are comfortable writing macros, because it is almost like
writing any ordinary function, as the AST you are manipulating is simple and is
the code you see on the page. -
Structural Editing
Lisp has this since forever with paredit, and I thought treesitter would bring
this to other languages. I tried adding some paredit style keybindings for
python based on treesitter and now I no longer think so. For the same reason
macros are more natural in lisp (the AST is the code you see on the page), I
think structural editing will always be more natural in lisp. -
Extensibility
As code and data are both just lists, it is easy to embed one in the other, and
to add new constructs to the language. For example for protohackers I used the
lisp-binary library which provides a
declarative DSL for specifying binary formats, and uses macros to turn that into
efficient code to parse and write it. The equivalent for common languages is
Kaitai Struct, which uses code generation instead of macros, and embeds their
own expression
language to handle
parsing complex formats, whereas lisp-binary just embeds lisp. With lisp
implementing such a library from scratch is one
chapter of an
introductory programming book. For other languages it is a much more complex
task.Another example is GUIX, which is still in development and not really ready to
replace all DevOps yet but has the potential to replace what is currently done
as shell scripts in yaml lists, a mix of templating languages that each have
their own syntax for conditionals and loops and inheritance, and a long list of
DSL’s that slowly accrue the features of general purpose programming languages,
with just guile scheme.It seems the lisp approach of using the same language and extending it for
different purposes, but with a consistent syntax, with a full general purpose
language available, and without having to reimplement syntax highlighting,
debuggers, compilers and the rest of the tooling for each bespoke language, is a
much more sane approach than the current mess of config languages, templating
formats and DSLs.
Emacs
Like parenthesis, editors are another topic already discussed to death, but I
think as the largest and longest-running open source lisp project, and the best
free development environment for lisps, it is worth a mention. Before I thought
I already know an editor well (vim) that I’ve been using forever, and I thought
the people reading email and doing everything inside emacs must be crazy, why
would you want your editor to do all that? And why do would they spend so much
time tweaking their emacs config?
I haven’t customized it much, I mostly just use the defaults of Doom
Emacs and they work well, but I now
understand the people who do. They’re not just editing a config file, they’re
coding lisp and extending their environment. Presumably you also enjoy coding as
a hobby or you wouldn’t be reading these ramblings. And I’m not reading email in
emacs but I understand those who do. I got two huge productivity gains from
using emacs:
- using magit replaced my use of git cli
- org mode replaced my loosely organized plaintext files for note taking, todo
lists, and calendar
For email I already have a good setup I know so I just keep using it but if I
didn’t then it’d make sense to do in emacs also. Emacs isn’t an editor but a
lisp runtime and platform for text-oriented applications, one of which is a
great implementation of vim (evil-mode). For any given task (git interface, note
taking software, todo list, text editor, etc) there is probably some more
polished software than emacs, but in the same way lisp can unite various DSLs in
a single host language, emacs unites many different programs into sharing the
same UI patterns, the same configuration and extension language, and being able
to easily interoperate with each other. I don’t have time to become a power user
of the best tool for everything, but I do have enough time to learn just emacs
and get a decent tool for everything.
It is also a good example I think of the productivity of lisp that people talk
about but also say has never been demonstrated. Despite all the warts of emacs’
lisp dialect (slow, lack of any namespacing, no good way to write asynchronous
code besides callbacks, no lexical scoping until relatively recently), it has
managed, with 10% of the users vim has, and a tiny fraction compared to VSCode
and the rest, to produce a code editor and a whole set of text-oriented
applications with an impressive depth of functionality.
Clojure
I liked the language and would pick it over the others for anything where
access to JVM libraries is important, or anything to do with web development in
general where