1277 lines
77 KiB
HTML
1277 lines
77 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>A Road to Common Lisp / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>A Road to Common Lisp</a></h1><p class='date'>Posted on August 27th, 2018.</p><p>I've gotten a bunch of emails asking for advice on how to learn Common Lisp in
|
|||
|
the present day. I decided to write down all the advice I've been giving
|
|||
|
through email and social media posts in the hopes that someone might find it
|
|||
|
useful.</p>
|
|||
|
|
|||
|
<p>One disclaimer up front: this is <em>a</em> road to Common Lisp, not <em>the</em> road to
|
|||
|
Common Lisp. It's what I followed (without some of the dead ends) and has
|
|||
|
a <em>lot</em> of my personal opinions baked in, but it is by no means the only way to
|
|||
|
learn the language.</p>
|
|||
|
|
|||
|
<p>This post has been translated into
|
|||
|
<a href="https://gist.github.com/y2q-actionman/49d7587912b2786eb68643afde6ca192">Japanese</a>.
|
|||
|
I can't vouch for the accuracy of any translations.</p>
|
|||
|
|
|||
|
<ol class="table-of-contents"><li><a href="index.html#s1-context">Context</a><ol><li><a href="index.html#s2-history">History</a></li><li><a href="index.html#s3-consequences">Consequences</a><ol><li><a href="index.html#s4-escaping-the-hamster-wheel-of-backwards-incompatibility">Escaping the Hamster Wheel of Backwards Incompatibility</a></li><li><a href="index.html#s5-practicality-begets-purity">Practicality Begets Purity</a></li><li><a href="index.html#s6-extensibility">Extensibility</a></li><li><a href="index.html#s7-power">Power</a></li><li><a href="index.html#s8-ugliness">Ugliness</a></li></ol></li></ol></li><li><a href="index.html#s9-a-road-to-learning-common-lisp">A Road to Learning Common Lisp</a><ol><li><a href="index.html#s10-get-a-lisp">Get a Lisp</a></li><li><a href="index.html#s11-pick-an-editor">Pick an Editor</a></li><li><a href="index.html#s12-hello-lisp">Hello, Lisp</a></li><li><a href="index.html#s13-a-gentle-introduction">A Gentle Introduction</a></li><li><a href="index.html#s14-getting-practical">Getting Practical</a></li><li><a href="index.html#s15-make-something">Make Something</a></li><li><a href="index.html#s16-lisp-as-a-system">Lisp as a System</a></li><li><a href="index.html#s17-learning-paradigms">Learning Paradigms</a></li><li><a href="index.html#s18-switch-things-up">Switch Things Up</a></li><li><a href="index.html#s19-recipes-for-success">Recipes for Success</a></li><li><a href="index.html#s20-final-patterns">Final Patterns</a></li></ol></li><li><a href="index.html#s21-where-to-go-from-here">Where to Go From Here</a><ol><li><a href="index.html#s22-macros">Macros</a></li><li><a href="index.html#s23-object-oriented-programming-with-clos">Object-Oriented Programming with CLOS</a></li><li><a href="index.html#s24-low-level-programming">Low-Level Programming</a></li><li><a href="index.html#s25-web-development">Web Development</a></li><li><a href="index.html#s26-game-development">Game Development</a></li><li><a href="index.html#s27-window-management">Window Management</a></li><li><a href="index.html#s28-unit-testing">Unit Testing</a></li><li><a href="index.html#s29-more-implementations">More Implementations</a></li></ol></li><li><a href="index.html#s30-modern-common-lisp">Modern Common Lisp</a><ol><li><a href="index.html#s31-structure">Structure</a><ol><li><a href="index.html#s32-packages">Packages</a></li><li><a href="index.html#s33-systems">Systems</a></li><li><a href="index.html#s34-projects">Projects</a></li><li><a href="index.html#s35-recap">Recap</a></li></ol></li><li><a href="index.html#s36-common-libraries">Common Libraries</a><ol><li><a href="index.html#s37-alexandria">Alexandria</a></li><li><a href="index.html#s38-bordeaux-threads">Bordeaux Threads</a></li><li><a href="index.html#s39-cffi">CFFI</a></li><li><a href="index.html#s40-cl-ppcre">CL-PPCRE</a></li><li><a href="index.html#s41-drakma">Drakma</a></li><li><a href="index.html#s42-iterate">Iterate</a></li><li><a href="index.html#s43-local-time">local-time</a></li><li><a href="index.html#s44-lparallel">lparallel</a></li><li><a href="index.html#s45-named-readtables">Named Readtables</a></li><li><a href="index.html#s46-roswell">Roswell</a></li><li><a href="index.html#s47-series">SERIES</a></li><li><a href="index.html#s48-st-json">st-json</a></li><li><a href="index.html#s49-usocket">usocket</a></li></ol></li></ol></li><li><a href="index.html#s50-good-luck">Good Luck!</a></li></ol>
|
|||
|
|
|||
|
<h2 id="s1-context"><a href="index.html#s1-context">Context</a></h2>
|
|||
|
|
|||
|
<p>I think it's important to have a sense of where Common Lisp came from and what
|
|||
|
kind of a language it is before you start learning it. There are some things
|
|||
|
that will seem very strange if you're coming straight from modern languages,
|
|||
|
but will make more sense if you've got a bit of background context.</p>
|
|||
|
|
|||
|
<h3 id="s2-history"><a href="index.html#s2-history">History</a></h3>
|
|||
|
|
|||
|
<p>Common Lisp has a long, deep history. I'm not going to try to cover it all here
|
|||
|
— if you're interested you should check out some of the following (in roughly
|
|||
|
increasing order of detail):</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Wikipedia's <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)#History">History of Lisp</a> and <a href="https://en.wikipedia.org/wiki/Common_Lisp#History">History of Common Lisp</a>.</li>
|
|||
|
<li>The <a href="http://www.gigamonkeys.com/book/introduction-why-lisp.html#where-it-began">Where it Began section in Practical Common Lisp</a>.</li>
|
|||
|
<li>The <a href="https://www.cs.cmu.edu/Groups//AI/lang/lisp/faq/lisp_2.faq">History: Where did Lisp come from?</a> section of the comp.lang.lisp FAQ.</li>
|
|||
|
<li><a href="http://www.nhplace.com/kent/Papers/cl-untold-story.html">Common Lisp: the Untold Story</a> by Kent Pitman.</li>
|
|||
|
<li><a href="https://www.dreamsongs.com/Files/HOPL2-Uncut.pdf">The Evolution of Lisp</a> by Guy Steele and Richard Gabriel.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>I realize you probably won't want to read all of the links above immediately, so
|
|||
|
here's a whirlwind tour of sixty years of Lisp.</p>
|
|||
|
|
|||
|
<p>Lisp began in the late 1950's. It was invented by John McCarthy at MIT.</p>
|
|||
|
|
|||
|
<p>Over the next twenty or so years various versions and dialects of Lisp grew and
|
|||
|
flourished. Some of the more notable dialects were Maclisp, BBN Lisp/Interlisp,
|
|||
|
Franz Lisp, Spice Lisp, and Lisp Machine Lisp. There were others too. The
|
|||
|
point is that there were a <em>lot</em> of different implementations, all growing,
|
|||
|
changing, and trying out different things.</p>
|
|||
|
|
|||
|
<p>(Scheme also originated in this time frame, but took a very different route and
|
|||
|
diverged from the path we're looking at. I won't cover Scheme in this post.)</p>
|
|||
|
|
|||
|
<p>In the early 1980s people decided that having a whole slew of
|
|||
|
mutually-incompatible dialects of Lisp might be not be ideal. An effort was
|
|||
|
made to take these different languages that had grown organically and produce
|
|||
|
one common language that would satisfy the needs of everyone (or at least
|
|||
|
a reasonable subset of "everyone"). In 1984 the first edition of Guy Steele's
|
|||
|
<a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html">Common Lisp: the Language</a> was published.</p>
|
|||
|
|
|||
|
<p>If you do some math you'll see that at the time the book was published Lisp had
|
|||
|
around twenty-five years of real-world use, experimentation, experience, and
|
|||
|
history to draw upon. Even so, the book alone didn't quite satisfy everyone and
|
|||
|
in 1986 a committee (X3J13) was formed to produce an ANSI specification for
|
|||
|
Common Lisp.</p>
|
|||
|
|
|||
|
<p>While the committee worked on the standardization process, in 1990 the second
|
|||
|
edition of Common Lisp: the Language was published. This was more
|
|||
|
comprehensive and contained some of the things the committee was working on
|
|||
|
(see the comp.lang.lisp FAQ linked above for more on this). At this point the
|
|||
|
Lisp family of languages had over thirty years of experience and history to
|
|||
|
draw upon. For comparison: Python (a "modern" language many people think of as
|
|||
|
also being "kind of old") <a href="https://en.wikipedia.org/wiki/History_of_Python#Early_history">was released</a> for the first time the
|
|||
|
following year.</p>
|
|||
|
|
|||
|
<p>In 1992 the X3J13 committee published the first draft of the new Common Lisp
|
|||
|
ANSI standard for public review (see Pitman's paper). The draft was approved in
|
|||
|
1994 and the approved specification was finally published in 1995. At this
|
|||
|
point Lisp was over thirty-five years old. The first version of Ruby <a href="https://en.wikipedia.org/wiki/Ruby_(programming_language)#First_publication">was
|
|||
|
released</a> in December of that year.</p>
|
|||
|
|
|||
|
<p>That's the end of the history lesson. There has not been another revision of
|
|||
|
the ANSI specification of Common Lisp. The version published in 1995 is the one
|
|||
|
that is still used today — if you see something calling itself "an
|
|||
|
implementation of Common Lisp" today, that is the specification it's referring
|
|||
|
to.</p>
|
|||
|
|
|||
|
<h3 id="s3-consequences"><a href="index.html#s3-consequences">Consequences</a></h3>
|
|||
|
|
|||
|
<p>I wanted to give you a quick overview of the history of Common Lisp because I
|
|||
|
want you to know what you're getting yourself into. I want you to realize that
|
|||
|
Common Lisp is a stable, large, practical, extensible, ugly language.
|
|||
|
Understanding these characteristics will make a lot of things make more sense
|
|||
|
as you learn the language, and I want to talk a little bit more about each of
|
|||
|
them before I start offering recommendations.</p>
|
|||
|
|
|||
|
<h4 id="s4-escaping-the-hamster-wheel-of-backwards-incompatibility"><a href="index.html#s4-escaping-the-hamster-wheel-of-backwards-incompatibility">Escaping the Hamster Wheel of Backwards Incompatibility</a></h4>
|
|||
|
|
|||
|
<p>If you're coming from other languages, you're probably used to things breaking
|
|||
|
when you "upgrade" your language implementation and/or libraries. If you want
|
|||
|
to run Ruby code you wrote ten years ago on the latest version of Ruby, it's
|
|||
|
probably going to take some effort to update it. My current day job is in Scala,
|
|||
|
and if a library's last activity is more than 2 or 3 years old on Github I just
|
|||
|
assume it won't work without a significant amount of screwing around on my part.
|
|||
|
The Hamster Wheel of Backwards Incompatibility we deal with every day is a fact
|
|||
|
of life in most modern languages, though some are certainly better than others.</p>
|
|||
|
|
|||
|
<p>If you learn Common Lisp, this is usually not the case. In the next section of
|
|||
|
this post I'll be recommending a book written in 1990. You can run its code,
|
|||
|
unchanged, in a Common Lisp implementation released last month. After years of
|
|||
|
jogging on the Hamster Wheel of Backwards Incompatibility I cannot tell you how
|
|||
|
much of a <em>relief</em> it is to be able to write code and reasonably expect it to
|
|||
|
still work in twenty years.</p>
|
|||
|
|
|||
|
<p>Of course, this is only the case for the language itself — if you depend on any
|
|||
|
libraries there's always the chance they might break when you update them. But
|
|||
|
I've found the stability of the core language is contagious, and overall the
|
|||
|
Common Lisp community seems fairly good about maintaining backwards
|
|||
|
compatibility.</p>
|
|||
|
|
|||
|
<p>I'll be honest though: there are exceptions. As you learn the language and
|
|||
|
start using libraries you'll start noticing some library authors who don't
|
|||
|
bother to document and preserve stable APIs for their libraries, and if
|
|||
|
staying off the Hamster Wheel is important to you you'll learn to avoid relying
|
|||
|
on code written by those people as much as possible.</p>
|
|||
|
|
|||
|
<h4 id="s5-practicality-begets-purity"><a href="index.html#s5-practicality-begets-purity">Practicality Begets Purity</a></h4>
|
|||
|
|
|||
|
<p>Another thing to understand about Common Lisp is that it's a large, practical
|
|||
|
language. The second edition of Common Lisp: the Language (usually abbreviated
|
|||
|
as "CLtL2" by Common Lisp programmers) is 971 pages long, not including the
|
|||
|
preface, references, or index. You can get a surprising amount done by writing
|
|||
|
pure Common Lisp without much extra support.</p>
|
|||
|
|
|||
|
<p>When programming applications in Common Lisp people will often depend on
|
|||
|
a small(ish) number of stable libraries, and library writers often try to
|
|||
|
minimize dependencies by utilizing as much of the core language as possible.
|
|||
|
I try to stick to fewer than ten or so dependencies for my applications and no
|
|||
|
more than two or three for my libraries (preferably zero, if possible), but I'm
|
|||
|
probably a bit more conservative than most folks. I <em>really</em> don't like the
|
|||
|
Hamster Wheel.</p>
|
|||
|
|
|||
|
<p>It's also worth noting that since Common Lisp has been around and stable for so
|
|||
|
long, it has <em>libraries</em> older and more stable than many programming languages.
|
|||
|
For example: Bordeaux Threads (the de-facto threading library for Common Lisp)
|
|||
|
was first proposed in 2004 and released soon after (2006 at the latest but
|
|||
|
possibly earlier, it's hard to tell because so many links are dead now), which
|
|||
|
makes it about fourteen years old. So yes, threading is handled by a library,
|
|||
|
but I'm not worried about it breaking my code in the next decade or two.</p>
|
|||
|
|
|||
|
<p>My advice is this: as you learn Common Lisp and look for libraries, try to
|
|||
|
suppress the voice in the back of your head that says "This project was last
|
|||
|
updated six years ago? That's probably abandoned and broken." The stability of
|
|||
|
Common Lisp means that sometimes libraries can just be <em>done</em>, not <em>abandoned</em>,
|
|||
|
so don't dismiss them out of hand.</p>
|
|||
|
|
|||
|
<h4 id="s6-extensibility"><a href="index.html#s6-extensibility">Extensibility</a></h4>
|
|||
|
|
|||
|
<p>Part of Common Lisp's practicality comes from its extensibility. No one has
|
|||
|
been clamoring for a new version of the specification that adds features
|
|||
|
because Common Lisp's extensibility allows users to add new features to the
|
|||
|
language as plain old libraries, without having to alter the core language.
|
|||
|
Macros are what might come to mind when you hear "Lisp extensibility", and of
|
|||
|
course that's part of it. Macros allow users to write libraries that would
|
|||
|
need to be core language features in other languages.</p>
|
|||
|
|
|||
|
<p>Common Lisp doesn't include string interpolation. You want it? No problem, you
|
|||
|
don't have to wait for <a href="https://docs.scala-lang.org/overviews/core/string-interpolation.html">Scala
|
|||
|
2.10</a> or
|
|||
|
<a href="https://www.python.org/dev/peps/pep-0498/">Python 3.6</a>, just <a href="https://edicl.github.io/cl-interpol/">use
|
|||
|
a library</a>.</p>
|
|||
|
|
|||
|
<p>Want to try some nondeterministic programming without any boilerplate? <a href="https://nikodemus.github.io/screamer/">Grab
|
|||
|
a library</a>.</p>
|
|||
|
|
|||
|
<p>Pattern matching syntax can make for some really beautiful, readable code.
|
|||
|
Common Lisp doesn't include it, but of course <a href="https://github.com/guicho271828/trivia/wiki/What-is-pattern-matching%3F-Benefits%3F">there's a library</a>.</p>
|
|||
|
|
|||
|
<p>Enjoying algebraic data types in Haskell or Scala? Here's your
|
|||
|
<a href="https://github.com/tarballs-are-good/cl-algebraic-data-type">library</a>.</p>
|
|||
|
|
|||
|
<p>All of these libraries rely on macros to make using them feel seamless. Of
|
|||
|
course you could <em>do</em> all of that without macros, but you've have to add a layer
|
|||
|
of boilerplate to manage evaluation. This:</p>
|
|||
|
|
|||
|
<pre><code>(match foo
|
|||
|
'(list x y z) (lambda (x y z) (+ x y z))
|
|||
|
'(vector x y) (lambda (x y) (- x y)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>just doesn't flow off the fingers like:</p>
|
|||
|
|
|||
|
<pre><code>(match foo
|
|||
|
((list x y z) (+ x y z))
|
|||
|
((vector x y) (- x y)))
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>No one's up in arms trying to get a new revision of the Common Lisp standard to
|
|||
|
add pattern matching because you can write it as a library and get 90% or more
|
|||
|
of what you've get if it were built in. The language gives you enough power to
|
|||
|
extend it in a way that feels like the extension was there from the beginning.</p>
|
|||
|
|
|||
|
<p>Having things that are core features in other languages be provided by libraries
|
|||
|
might seem at odds with the previous section about minimizing dependencies, and
|
|||
|
to some extent that's true. But I think there's a happy medium where you can
|
|||
|
write stable libraries in the core language and then depend on a small number of
|
|||
|
those libraries in your applications to add exactly the features you need for
|
|||
|
any particular problem.</p>
|
|||
|
|
|||
|
<h4 id="s7-power"><a href="index.html#s7-power">Power</a></h4>
|
|||
|
|
|||
|
<p>Macros are one of the things that make Lisp so extensible, because they let you
|
|||
|
transform arbitrary code into other arbitrary code. This is true for macros in
|
|||
|
languages like C too, but Common Lisp macros are different because they're <em>part
|
|||
|
of the language</em>.</p>
|
|||
|
|
|||
|
<p>In C you have a layer of macros on top, written in a preprocessor macro
|
|||
|
language. The macro layer and the language layer are separate from each other,
|
|||
|
with the macro layer providing one one extra level of abstractive power (which,
|
|||
|
don't get me wrong, is certainly useful).</p>
|
|||
|
|
|||
|
<p>In Common Lisp, you write macros <em>in Common Lisp itself</em>. You can then use
|
|||
|
those macros to write functions, and use those functions to write more macros.
|
|||
|
Instead of two stratified layers it's a <em>feedback loop</em> of abstractive power.</p>
|
|||
|
|
|||
|
<p>But macros aren't the only thing about Common Lisp that make it so practical and
|
|||
|
extensible. Something people often don't realize is that while Common Lisp is
|
|||
|
an extremely high-level language thanks to macros, it also has plenty of
|
|||
|
low-level facilities as part of the language. It's never going to be as
|
|||
|
low-level as something like C, Rust, or Forth, but you might be surprised at
|
|||
|
some of the things that the ANSI spec includes.</p>
|
|||
|
|
|||
|
<p>Want to see the assembly code a particular function compiles down to?
|
|||
|
<a href="http://clhs.lisp.se/Body/f_disass.htm"><code>DISASSEMBLE</code></a> it!</p>
|
|||
|
|
|||
|
<p>Want to stack-allocate something to avoid some garbage collection? X3J13
|
|||
|
<a href="http://clhs.lisp.se/Body/d_dynami.htm">thought of that</a>.</p>
|
|||
|
|
|||
|
<p>Need arrays of unboxed floats to ship to a graphics card? <a href="http://clhs.lisp.se/Body/15_ab.htm">The standard allows
|
|||
|
for that</a>.</p>
|
|||
|
|
|||
|
<p>Think <code>GOTO</code> should be considered helpful, not harmful? Well, okay, we're all
|
|||
|
adults here. <a href="http://clhs.lisp.se/Body/s_tagbod.htm">Good luck</a>, try not to shoot your foot off.</p>
|
|||
|
|
|||
|
<p>Need to do unsigned 8-bit arithmetic in your Game Boy emulator, but would prefer
|
|||
|
it to compile down to just a machine instruction or two? <a href="https://pdfs.semanticscholar.org/7089/0eff0e93aba49174a9346731f4bf9225706d.pdf">It's
|
|||
|
possible</a>.</p>
|
|||
|
|
|||
|
<p>Not all Common Lisp implementations actually perform all these optimizations,
|
|||
|
but the designers of Common Lisp had the foresight to include the language
|
|||
|
features needed to support them. You can write vanilla Common Lisp as defined
|
|||
|
by the standard and trust that it will run everywhere, and implementations that
|
|||
|
<em>do</em> support these kinds of things will take advantage of the optimization
|
|||
|
opportunities.</p>
|
|||
|
|
|||
|
<p>This combination of supporting extremely high-level programming with macros and
|
|||
|
a reasonable amount of low-level optimization mean that even though the
|
|||
|
specification is over twenty years old, it's still a good solid base to build on
|
|||
|
today. The thirty years of experience and history the designers were drawing
|
|||
|
from allowed them to create a very practical language that has survived for
|
|||
|
decades.</p>
|
|||
|
|
|||
|
<h4 id="s8-ugliness"><a href="index.html#s8-ugliness">Ugliness</a></h4>
|
|||
|
|
|||
|
<p>It's also important to realize that while Common Lisp might be very practical,
|
|||
|
the need to accommodate existing users and dialects means that there are plenty
|
|||
|
of ugly parts. If you buy a paper copy of the second edition of Common Lisp:
|
|||
|
the Language and look up "kludges" in the index you'll find this:</p>
|
|||
|
|
|||
|
<p><a href="../../../../static/images/blog/2018/07/lisp-kludge.jpeg"><img src="../../../../static/images/blog/2018/07/lisp-kludge.jpeg" alt="Photo of a page of CLtL2's Index, listing "kludges" as pages 1 to 971"></a></p>
|
|||
|
|
|||
|
<p>Common Lisp is not a beautiful crystal of programming language design. It's
|
|||
|
a scruffy workshop with a big pegboard wall of tools, a thin layer of sawdust on
|
|||
|
the floor, a filing cabinet in the office with a couple of drawers that open
|
|||
|
perpendicular to the rest, and there's a weird looking saw with <code>RPLACD</code> written
|
|||
|
on the side sitting off in a corner where no one's touched it for twenty years.</p>
|
|||
|
|
|||
|
<p>This historical baggage is a price paid to ensure Common Lisp had a future. It
|
|||
|
made it practical for people using the old dialects to actually adopt Common
|
|||
|
Lisp with a reasonable amount of effort. If the designers had tried to make it
|
|||
|
perfect and beautiful this could have made it too different to port
|
|||
|
implementations and code to and might have resulted in the language being
|
|||
|
ignored, instead of being adopted and embraced.</p>
|
|||
|
|
|||
|
<h2 id="s9-a-road-to-learning-common-lisp"><a href="index.html#s9-a-road-to-learning-common-lisp">A Road to Learning Common Lisp</a></h2>
|
|||
|
|
|||
|
<p>If all of this hasn't scared you away from the language, let's talk about how
|
|||
|
you can learn it in 2018.</p>
|
|||
|
|
|||
|
<p>If you search around on the internet for Common Lisp tutorials and guides,
|
|||
|
you're not going to find as much as you might expect. This is because a lot of
|
|||
|
Common Lisp reference material was created before or during the infancy of the
|
|||
|
internet. There are a <em>lot</em> of books about Common Lisp out there. Some are
|
|||
|
better than others. I'll recommend the ones I think are the best, but don't
|
|||
|
hesitate to browse around and find others.</p>
|
|||
|
|
|||
|
<h3 id="s10-get-a-lisp"><a href="index.html#s10-get-a-lisp">Get a Lisp</a></h3>
|
|||
|
|
|||
|
<p>To get started with Common Lisp you'll need to install a Common Lisp
|
|||
|
implementation. Common Lisp is an ANSI specification, so there are multiple
|
|||
|
implementations of it, which gives you choices. There are a bunch of options,
|
|||
|
but I'll make it simple for you:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>If you're using MacOS and want a single GUI app you can download from the App
|
|||
|
Store, choose <a href="https://ccl.clozure.com/">ClozureCL</a> (often abbreviated "CCL").</li>
|
|||
|
<li>Otherwise, choose <a href="http://www.sbcl.org/">SBCL</a>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>That's Clozure with a Z. Clojure is something entirely different that just
|
|||
|
happens to have a confusingly similar name.</p>
|
|||
|
|
|||
|
<p>You might also hear of something called CLISP, which sounds like it might be
|
|||
|
what you want. It's not. CLISP is just another implementation, but it hasn't
|
|||
|
had a release in eight years (even though development is still ongoing in its
|
|||
|
source repos!) and it's not as commonly used as CCL or SBCL, so it'll be harder
|
|||
|
to find help if you have questions about the installation, etc.</p>
|
|||
|
|
|||
|
<p>You might also hear about something called Roswell. Don't use Roswell, you don't
|
|||
|
need it (yet (or at all)).</p>
|
|||
|
|
|||
|
<p>Just install SBCL or CCL for now, you can explore the other options once you've
|
|||
|
got your bearings a bit better.</p>
|
|||
|
|
|||
|
<h3 id="s11-pick-an-editor"><a href="index.html#s11-pick-an-editor">Pick an Editor</a></h3>
|
|||
|
|
|||
|
<p>You might hear people tell you that you <em>must</em> learn Emacs before learning
|
|||
|
Common Lisp. They're wrong. You can get started learning the language just
|
|||
|
fine in whatever text editor you're comfortable in.</p>
|
|||
|
|
|||
|
<p>If you don't have a preference, CCL itself comes bundled with a text editor on
|
|||
|
MacOS. That one will work just fine to start.</p>
|
|||
|
|
|||
|
<p>Emacs, Vim, Sublime Text, Atom, whatever, for now it doesn't matter. As long as
|
|||
|
it can balance parentheses, highlight comments and strings, and autoindent Lisp
|
|||
|
code that's all you need to start. Worry about shaving the editor yak once
|
|||
|
you're more comfortable in the language.</p>
|
|||
|
|
|||
|
<h3 id="s12-hello-lisp"><a href="index.html#s12-hello-lisp">Hello, Lisp</a></h3>
|
|||
|
|
|||
|
<p>To check that you've got everything set up properly, make a <code>hello.lisp</code> file
|
|||
|
with the following contents:</p>
|
|||
|
|
|||
|
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> hello <span class="paren2">(<span class="code"></span>)</span>
|
|||
|
<span class="paren2">(<span class="code">write-line <span class="string">"What is your name?"</span></span>)</span>
|
|||
|
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">(<span class="code"><span class="paren4">(<span class="code">name <span class="paren5">(<span class="code">read-line</span>)</span></span>)</span></span>)</span>
|
|||
|
<span class="paren3">(<span class="code">format t <span class="string">"Hello, ~A.~%"</span> name</span>)</span></span>)</span></span>)</span></span></code></pre>
|
|||
|
|
|||
|
<p>Don't worry about what this means yet, it's just a check that everything's
|
|||
|
working properly.</p>
|
|||
|
|
|||
|
<p>Open an SBCL or CCL REPL (<a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop">Read/Eval/Print
|
|||
|
Loop</a>) and
|
|||
|
load the file by entering <code>(load "hello.lisp")</code>, then call the function and make
|
|||
|
sure it works. It should look something like this if you picked SBCL:</p>
|
|||
|
|
|||
|
<pre><code>$ sbcl
|
|||
|
* (load "hello.lisp")
|
|||
|
|
|||
|
T
|
|||
|
* (hello)
|
|||
|
What is your name?
|
|||
|
Steve
|
|||
|
Hello, Steve.
|
|||
|
NIL
|
|||
|
*
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Or if you chose CCL but still want to use the command line, rather than the
|
|||
|
MacOS app (the command line program might be annoyingly named <code>ccl64</code> if you're
|
|||
|
on a 64-bit system):</p>
|
|||
|
|
|||
|
<pre><code>$ ccl64
|
|||
|
Clozure Common Lisp Version ...
|
|||
|
|
|||
|
? (load "hello.lisp")
|
|||
|
#P"/home/sjl/Desktop/hello.lisp"
|
|||
|
? (hello)
|
|||
|
What is your name?
|
|||
|
Steve
|
|||
|
Hello, Steve.
|
|||
|
NIL
|
|||
|
?
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>If your arrow keys and backspace don't work in the REPL, use <a href="https://github.com/hanslub42/rlwrap"><code>rlwrap</code></a>
|
|||
|
to fix that. <code>rlwrap sbcl</code> will give you a non-miserable REPL. <code>rlwrap</code> is
|
|||
|
a handy tool to have in your toolbox anyway.</p>
|
|||
|
|
|||
|
<h3 id="s13-a-gentle-introduction"><a href="index.html#s13-a-gentle-introduction">A Gentle Introduction</a></h3>
|
|||
|
|
|||
|
<p>The best book I've found for getting started in Common Lisp is <a href="https://www.cs.cmu.edu/~dst/LispBook/">Common Lisp:
|
|||
|
A Gentle Introduction to Symbolic Computation</a>. This book really
|
|||
|
does strive to be gentle. Even if you've programmed before I'd still recommend
|
|||
|
starting here because it eases you into the language.</p>
|
|||
|
|
|||
|
<p>The 1990 edition is available free from the site, and there's a 2013 reprint
|
|||
|
which fixes some minor errors in the 1990 version. If you can afford it I'd
|
|||
|
recommend buying the 2013 edition, but the 1990 version will also do fine.</p>
|
|||
|
|
|||
|
<p>Go through the book and <em>do all the exercises</em>. This will take a while, and
|
|||
|
is mainly meant to get you started overcoming some of the main obstacles to
|
|||
|
being comfortable in Common Lisp, such as:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>How am I ever going to remember all these weird function names?</li>
|
|||
|
<li>Why do people use strings so rarely?</li>
|
|||
|
<li>When do I need the god damn quotation mark?</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>If you find the book is moving too slow, just skim forward a bit. Skimming is
|
|||
|
a very useful skill to practice as a programmer. I think it's better for
|
|||
|
authors to err on the side of explaining too much when writing books and
|
|||
|
documentation — expert readers should be comfortable skimming if you explain too
|
|||
|
<em>much</em>, but new users will be stuck wallowing in confusion if you're too terse.
|
|||
|
Creating hours of newbie misery and confusion to save a few flicks of an
|
|||
|
expert's scroll wheel is a poor tradeoff to make.</p>
|
|||
|
|
|||
|
<p>You should also join the <code>#clschool</code> channel on the Freenode IRC network so you
|
|||
|
can ask questions if you get stuck. For the most part people there are friendly
|
|||
|
and helpful, though I'll warn you in advance that there's at least one person
|
|||
|
who can sometimes be abrasive. There's also a <code>#clnoobs</code> channel, but that was
|
|||
|
mostly abandoned during the latest wave of Freenode spam because no one had ops
|
|||
|
to help combat the spam.</p>
|
|||
|
|
|||
|
<p>If IRC isn't your thing there's also a <a href="https://discord.gg/tffeu2x">Discord
|
|||
|
server</a> that some of us hang out in. Join the
|
|||
|
<code>#common-lisp</code> channel there and we'll be happy to help you.</p>
|
|||
|
|
|||
|
<h3 id="s14-getting-practical"><a href="index.html#s14-getting-practical">Getting Practical</a></h3>
|
|||
|
|
|||
|
<p>Once you've finished that book the next one you should attack is <a href="http://www.gigamonkeys.com/book/">Practical
|
|||
|
Common Lisp</a>. You can get a paper copy if you want, but the full book
|
|||
|
is available on the site for free.</p>
|
|||
|
|
|||
|
<p>You can skip the editor/programming environment part because the environment it
|
|||
|
recommends (Lisp in a Box) is abandoned and no longer works. Just keep using
|
|||
|
the programming environment you're comfortable with for now.</p>
|
|||
|
|
|||
|
<p>Unfortunately the book doesn't include exercises. If you <em>really</em> want to get
|
|||
|
the most out of it you can type in all the code as you're reading it and poke at
|
|||
|
it, but if you've already done the exercises in the previous book it's probably
|
|||
|
safe to just sit down and read the book carefully. Don't read more than
|
|||
|
a chapter or two a day. It will take a while for your brain to digest all the
|
|||
|
information.</p>
|
|||
|
|
|||
|
<p>Make sure you understand everything as you go through the book. Don't be afraid
|
|||
|
to ask questions on IRC or Discord (or email me if you want, I don't mind) if
|
|||
|
something's not clear.</p>
|
|||
|
|
|||
|
<p>You should also begin to get comfortable looking up things in <a href="http://www.lispworks.com/documentation/lw70/CLHS/Front/Contents.htm">the Common Lisp
|
|||
|
language specification</a> itself. It's the ultimate manual for Common Lisp.
|
|||
|
It can be pretty dense at points, but can answer many questions you might have
|
|||
|
if you read it slowly and carefully. You can either use the index page to find
|
|||
|
what you're looking for or just search on Google for "clhs whatever" (CLHS
|
|||
|
stands for "Common Lisp HyperSpec", which is the hyperlinked, HTML version of
|
|||
|
the spec). If you already use the Dash app for MacOS, it has the Common Lisp
|
|||
|
spec available.</p>
|
|||
|
|
|||
|
<p>(Some people will tell you to learn the language by just reading the spec. That's
|
|||
|
ridiculous — it's like trying to learn French by reading a dictionary. It's
|
|||
|
a useful tool to have, but not the only one you'll need.)</p>
|
|||
|
|
|||
|
<h3 id="s15-make-something"><a href="index.html#s15-make-something">Make Something</a></h3>
|
|||
|
|
|||
|
<p>Once you've got those two books under your belt and some practice using the
|
|||
|
spec, it's time to make something without someone holding your hand. It doesn't
|
|||
|
have to be anything big or special, the goal is to just write some Lisp without
|
|||
|
having the answer on the next page.</p>
|
|||
|
|
|||
|
<p>If you need some ideas:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Do some <a href="https://projecteuler.net/">Project Euler</a> problems.</li>
|
|||
|
<li>Do some <a href="https://adventofcode.com/">Advent of Code</a> exercises.</li>
|
|||
|
<li>Make a <a href="https://twitter.com/git_commands">stupid Twitter bot</a>.</li>
|
|||
|
<li>Make a personal calendar program that records your appointments, checks the
|
|||
|
weather forecast the day of, etc.</li>
|
|||
|
<li>Use <a href="https://github.com/vydd/sketch">Sketch</a> to implement the stuff in some <a href="https://www.youtube.com/user/codingmath/videos">Coding Math videos</a>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>It doesn't really matter what you make, just make <em>something</em> on your own.</p>
|
|||
|
|
|||
|
<h3 id="s16-lisp-as-a-system"><a href="index.html#s16-lisp-as-a-system">Lisp as a System</a></h3>
|
|||
|
|
|||
|
<p>At this point it's time to take your Common Lisp skills up a notch. Up until
|
|||
|
now I've told you to just use any text editor because it's more important to
|
|||
|
get you some experience with the language, but now it's time to dive into the
|
|||
|
deep end.</p>
|
|||
|
|
|||
|
<p>In most languages the development process looks something like this:</p>
|
|||
|
|
|||
|
<ol>
|
|||
|
<li>Edit some code in the project with an editor.</li>
|
|||
|
<li>Compile the project (some languages skip this step).</li>
|
|||
|
<li>Run the project (or the tests).</li>
|
|||
|
<li>Observe the output (in the console, a browser, etc).</li>
|
|||
|
<li>Go to 1.</li>
|
|||
|
</ol>
|
|||
|
|
|||
|
<p>This is not how most Common Lisp users interact with the language. In Common
|
|||
|
Lisp, the development cycle looks more like this:</p>
|
|||
|
|
|||
|
<ol>
|
|||
|
<li>Start a Lisp process.</li>
|
|||
|
<li>Load the project.</li>
|
|||
|
<li>Edit some code with your editor.</li>
|
|||
|
<li>Tell the running process to compile <em>only the code you edited</em>.</li>
|
|||
|
<li>Interact with the changed code in the process via the REPL, an HTTP request, etc.</li>
|
|||
|
<li>Observe the output (in the console, a browser, etc).</li>
|
|||
|
<li>Go to 3.</li>
|
|||
|
</ol>
|
|||
|
|
|||
|
<p>When you embrace the Lisp way of working you'll rarely recompile and reload an
|
|||
|
entire project. Usually you'll write a function (or a macro, or parameter, or
|
|||
|
whatever), compile <em>just that function</em>, maybe poke at it in the REPL a bit, and
|
|||
|
then move on to the next function. This has some advantages over the
|
|||
|
traditional compile-everything-then-run approach.</p>
|
|||
|
|
|||
|
<p>First: compiling a small chunk of code is fast. I just timed compiling a few of
|
|||
|
the larger functions in one of my projects and they took around 50-80
|
|||
|
microseconds. You don't have to wait for the compiler, so your
|
|||
|
concentration/thought process never has time to wander.</p>
|
|||
|
|
|||
|
<p>Another advantage is that when you get back the results of your compilation (and
|
|||
|
running), any errors or warnings you receive are almost certainly related to the
|
|||
|
few lines of code you just compiled. If you compile a ten-line function, run
|
|||
|
it, and get a division by zero error you can immediately focus in on the ten
|
|||
|
lines you just compiled and think about what changed.</p>
|
|||
|
|
|||
|
<p>Because the Lisp process is always running, as soon as you compile a function
|
|||
|
it's ready to be used in the REPL. You can throw some arbitrary data at it and
|
|||
|
inspect the results to see how it behaves in isolation before you build more
|
|||
|
things on top of it. This cycle of making a function, compiling it, poking at
|
|||
|
it to make sure it's working as expected, and moving on happens <em>constantly</em>.</p>
|
|||
|
|
|||
|
<p>In contrast, when working in languages like Scala or Python I almost never find
|
|||
|
myself writing one single function and compiling or running the project
|
|||
|
immediately. Spinning up the compiler or running the unit tests takes at
|
|||
|
<em>least</em> a second or two (or sometimes <em>minutes</em> in Scala, unfortunately) so to
|
|||
|
avoid having a constant stream of gaps in my thought I end up writing a bunch of
|
|||
|
functions at once, and then I run the project or tests once I know they have
|
|||
|
a chance of working.</p>
|
|||
|
|
|||
|
<p>But then when I get back an error I have much more surface area to check,
|
|||
|
because I've added a lot of new code! So now I have to track down a problem
|
|||
|
that might be in something I wrote four minutes ago, whereas in Lisp I would
|
|||
|
only have to ever look at the code I wrote in the last few seconds.</p>
|
|||
|
|
|||
|
<p>I've started using IntelliJ with Scala to help make this a bit less painful. It
|
|||
|
does help with the compile times because it recompiles things on the fly, but it
|
|||
|
doesn't solve the rest of the problem. I can write a Scala function in IntelliJ
|
|||
|
and it will be compiled immediately, but I can't <em>interact</em> with it immediately
|
|||
|
like I can in Common Lisp.</p>
|
|||
|
|
|||
|
<p>When you work in this style with Common Lisp I think you'll really grow to love
|
|||
|
it. Writing in other languages will begin to feel like shipping your code off
|
|||
|
to the DMV and getting it back a week later with a page full of red ink
|
|||
|
somewhere in the hundred forms you filled out. Writing in Common Lisp feels
|
|||
|
like interacting with a living, breathing organism, or like <a href="https://www.reddit.com/r/lisp/comments/4oo1cp/common_lisp_for_clojure_programmer/d4eec68/">teaching things to
|
|||
|
an eager assistant</a>.</p>
|
|||
|
|
|||
|
<p>This philosophy of Lisp being not just a programming <em>language</em> but a living,
|
|||
|
breathing programming <a href="https://www.dreamsongs.com/Files/Incommensurability.pdf"><em>system</em></a> goes beyond just the short
|
|||
|
feedback loop and interactive REPL, too. </p>
|
|||
|
|
|||
|
<p>As an example: imagine you're making a video game and have a bug somewhere in
|
|||
|
your damage calculation that will occasionally cause a division by zero. Now
|
|||
|
let's say you're working on the code for a particular quest. You'll start the
|
|||
|
game, load a save file at the beginning of the quest, and start going through
|
|||
|
the steps. All of a sudden, in the middle of killing the final monster for the
|
|||
|
quest, you hit the damage bug! In traditional languages, one of two things
|
|||
|
might happen:</p>
|
|||
|
|
|||
|
<ol>
|
|||
|
<li>The game crashes, and you get a stack trace and maybe a core dump.</li>
|
|||
|
<li>You've wrapped a <code>try</code> block around the main game loop that logs a stack
|
|||
|
trace and ignores errors and allows the game to continue.</li>
|
|||
|
</ol>
|
|||
|
|
|||
|
<p>Case 1 is pretty bad. You've got to try to track down the bug from a snapshot
|
|||
|
of what things looked like at the time (the stack trace and core dump). And
|
|||
|
even if you manage to fix it, now you've got to redo all that playing to get
|
|||
|
back to testing your quest code that you were originally working on.</p>
|
|||
|
|
|||
|
<p>Case 2 is bad, in a different way. If you just ignore errors all the time, the
|
|||
|
game might now be in a weird state. You also might lose some critical context
|
|||
|
that's necessary to debug the problem, unless you're also saving a core dump
|
|||
|
(but I don't know of many people who save a core dump on every exception).</p>
|
|||
|
|
|||
|
<p>In Common Lisp you can certainly choose to panic on or ignore errors, but
|
|||
|
there's a better way to work. When an error is signaled in Common Lisp, it
|
|||
|
doesn't unwind the stack. The Lisp process will pause execution at that point
|
|||
|
and open a window in your editor showing you the stack trace. Your warrior's
|
|||
|
sword is hovering over the monster, waiting for you. At this point you can
|
|||
|
communicate with the running process at the REPL to see what's going on. You
|
|||
|
can examine variables in the stack, or even run any arbitrary code you want.</p>
|
|||
|
|
|||
|
<p>Once you figure out the problem ("Oh, I see, the <code>calculate-armor-percentage</code>
|
|||
|
function returns <code>0</code> if a shielding spell ran out during the same frame") you
|
|||
|
can fix the code, recompile the problematic function, and <em>restart the execution
|
|||
|
of that function (or any other one!) in the call stack</em>! Your warrior's sword
|
|||
|
lands, and you move back to what you were doing before.</p>
|
|||
|
|
|||
|
<p>You don't have to track down the bug from just a stack trace, like a detective
|
|||
|
trying to piece together what happened by the <a href="https://www.usenix.org/system/files/1311_05-08_mickens.pdf">blood stains</a> on
|
|||
|
the wall. You can examine the crime <em>as it's happening</em> and intervene to save
|
|||
|
the victim. It's like if you could run your code in a debugger with
|
|||
|
a breakpoint at every single line that only activates if something goes wrong!</p>
|
|||
|
|
|||
|
<p>Maybe you don't make video games, sure, but this process can be useful in all
|
|||
|
kinds of contexts. Maybe you're writing a web app that talks to an API
|
|||
|
somewhere, and are debugging a request that fails between two calls to the API,
|
|||
|
e.g. between "create widget <code>foo</code>" and "add <code>foo</code> to widget list <code>bar</code>".
|
|||
|
Instead of just aborting the request, logging a stack trace, and now leaving
|
|||
|
things in a possibly weird state (<code>foo</code> having been created without being in the
|
|||
|
expected <code>bar</code> list), you can fix the problem and allow the request to finish
|
|||
|
properly.</p>
|
|||
|
|
|||
|
<p>Of course this won't always work. If you've got a big function that does some
|
|||
|
side effects and then crashes, restarting execution of the function would make
|
|||
|
the side effects happen again. But if you divide up your functions well (<a href="https://groups.google.com/forum/message/raw?msg=comp.lang.lisp/9SKZ5YJUmBg/Fj05OZQomzIJ">one
|
|||
|
function to a function!</a>) this case is pretty rare. And even when it
|
|||
|
does happen, it just means you're back in the same situation you're in <em>by
|
|||
|
default</em> with other languages!</p>
|
|||
|
|
|||
|
<p>Support for this style of interactive development doesn't just come from some
|
|||
|
fancy editor plugins — it's baked into the bones of the language. For example:
|
|||
|
the standard specifies a method named
|
|||
|
<a href="http://clhs.lisp.se/Body/f_upda_1.htm"><code>update-instance-for-redefined-class</code></a>
|
|||
|
that lets you customize what happens to objects when their class is redefined!
|
|||
|
This isn't something you'll use all the time, but something like Sketch (a
|
|||
|
Common Lisp equivalent of Java's Processing library) <a href="https://github.com/vydd/sketch/blob/19fe20502d26fc75752e710dfe5106ed13345c5a/src/sketch.lisp#L118-L121">uses it</a> to
|
|||
|
automatically update the running sketch when you redefine its class.
|
|||
|
Dynamically updating running code in a safe, consistent way doesn't require any
|
|||
|
dark magic in Common Lisp because it's the expected, usual way to work.</p>
|
|||
|
|
|||
|
<p>So how do you actually <em>get</em> this wonderful interactive experience? The bad
|
|||
|
news is that you're going to need to shave the editor yak. You really only have
|
|||
|
two choices here:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Emacs with <a href="https://common-lisp.net/project/slime/">SLIME</a> or <a href="https://github.com/joaotavora/sly">Sly</a>.</li>
|
|||
|
<li>Vim (or Neovim) with <a href="https://github.com/l04m33/vlime">Vlime</a> or <a href="https://github.com/kovisoft/slimv">Slimv</a>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>I wish this weren't the case, but those are really only the realistic options
|
|||
|
today (aside from the editing environments for the (expensive) commercial
|
|||
|
Lisps).</p>
|
|||
|
|
|||
|
<p>If you're like me and already have Vim burned too deeply into your fingers to
|
|||
|
ever get it out, I'd recommend Vim with Vlime. It will give you 80% of the
|
|||
|
experience you'll get with Emacs.</p>
|
|||
|
|
|||
|
<p>Otherwise go with Emacs. You might want to look into
|
|||
|
<a href="https://portacle.github.io/">Portacle</a>, which bundles Emacs and SLIME and
|
|||
|
a bunch of other things together, or you might want to have a go at setting up
|
|||
|
Emacs and SLIME or Sly yourself. I can't really give you much advice on the
|
|||
|
Emacs side of things because I haven't had much experience with it, so you'll
|
|||
|
need to do a bit of research here.</p>
|
|||
|
|
|||
|
<p>Whatever you choose, spend some time setting up your editor and environment of
|
|||
|
choice. This will be a lot of fiddly metawork, but will pay off handsomely as
|
|||
|
you continue working in Lisp.</p>
|
|||
|
|
|||
|
<p>On a side note: if anyone is interested in making a Common Lisp <a href="https://langserver.org/">LSP</a>
|
|||
|
language server, I think it would be a hugely useful contribution to the
|
|||
|
community. Having an LSP server would mean you could get a much nicer
|
|||
|
programming experience in many editors out of the box, which would help new
|
|||
|
people quite a lot.</p>
|
|||
|
|
|||
|
<p>I think you could piggyback on top of Swank to do a lot of the language-side
|
|||
|
stuff, and it would mostly be a matter of implementing the LSP interface. If
|
|||
|
this sounds interesting to you, please let me know — I'd be willing to help.
|
|||
|
I've done some work at my day job making a Scala LSP language server that uses
|
|||
|
IntelliJ as a backend, so I have at least some idea of how that sausage gets
|
|||
|
made. I just don't have the time or motivation to do an entire LSP server for
|
|||
|
Common Lisp all by myself.</p>
|
|||
|
|
|||
|
<h3 id="s17-learning-paradigms"><a href="index.html#s17-learning-paradigms">Learning Paradigms</a></h3>
|
|||
|
|
|||
|
<p>At this point you should have a pretty good handle on the basics of Common Lisp,
|
|||
|
and have set up one of the more powerful development environments. Your next
|
|||
|
goals should be to learn how to write idiomatic Common Lisp and to get some
|
|||
|
practice using your fancy new environment.</p>
|
|||
|
|
|||
|
<p>I think the perfect book for both of these is <a href="https://github.com/norvig/paip-lisp">Paradigms of Artificial
|
|||
|
Intelligence Programming</a>, often abbreviated as PAIP. The book was
|
|||
|
recently made available for free as a PDF, or you can buy a used paper copy if
|
|||
|
you prefer.</p>
|
|||
|
|
|||
|
<p>This book was written in 1992 so it's not about the hyped up AI fields you've
|
|||
|
been hearing about in the news like machine learning — instead it's a tour of
|
|||
|
<a href="https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence">Good Old-Fashioned AI</a>. Even if you're not particularly interested in
|
|||
|
this kind of AI, the book is a great example of how to write Common Lisp code.</p>
|
|||
|
|
|||
|
<p>One thing I really love about this book is that almost all the functions in it
|
|||
|
have docstrings. If you look at most other programming books they omit the
|
|||
|
documentation strings, presumably for space reasons and because they feel the
|
|||
|
surrounding text is documentation enough. But writing helpful docstrings is an
|
|||
|
art in and of itself, and I think books that omit them train readers that "good
|
|||
|
code omits docstrings", which is a bad habit to get into.</p>
|
|||
|
|
|||
|
<p>The book contains <em>plenty</em> of exercises, conveniently categorized by how
|
|||
|
difficult or involved they are:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><code>S</code> for "seconds".</li>
|
|||
|
<li><code>M</code> for "minutes".</li>
|
|||
|
<li><code>H</code> for "hours".</li>
|
|||
|
<li><code>D</code> for "days".</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>This is a very good idea which more books should steal. Do all of the <code>S</code> and
|
|||
|
<code>M</code> exercises, and try your hand at at least a few of the <code>H</code> ones. If a <code>D</code>
|
|||
|
sounds particularly interesting don't be afraid to spend some time on it
|
|||
|
— really digging into a problem is exactly what you need at this point in your
|
|||
|
Lisp journey.</p>
|
|||
|
|
|||
|
<h3 id="s18-switch-things-up"><a href="index.html#s18-switch-things-up">Switch Things Up</a></h3>
|
|||
|
|
|||
|
<p>Now that you're comfortable in Common Lisp and your programming environment,
|
|||
|
it's time to push yourself out of your comfort zone again. At the beginning I
|
|||
|
had you choose either SBCL or CCL. Now I want you to install whichever one you
|
|||
|
didn't originally choose and make sure all the code you've written so far runs
|
|||
|
in it.</p>
|
|||
|
|
|||
|
<p>This may seem a bit like running in place, but making sure your code runs in
|
|||
|
more than one implementation will keep you honest. It will force you to write
|
|||
|
portable code that doesn't rely on anything implementation-specific that might
|
|||
|
change in the next decade or two. And you might even discover that you like
|
|||
|
this other implementation better than the original — maybe CCL's super-fast
|
|||
|
compile times make you smile, or SBCL's strong type inference catches more of
|
|||
|
your bugs.</p>
|
|||
|
|
|||
|
<p>Go through all the code you've written so far and make sure it all runs in the
|
|||
|
new implementation. You might also want to take this opportunity to refactor or
|
|||
|
rewrite some of it — you've learned a lot since you first started, so your
|
|||
|
earliest Common Lisp code will probably look pretty rough to you now.</p>
|
|||
|
|
|||
|
<h3 id="s19-recipes-for-success"><a href="index.html#s19-recipes-for-success">Recipes for Success</a></h3>
|
|||
|
|
|||
|
<p>The final technical book I'll recommend to every aspiring Lisp programmer is
|
|||
|
<a href="http://weitz.de/cl-recipes/">Common Lisp Recipes</a>, sometimes abbreviated as CLR. Unlike most of
|
|||
|
the other books I've recommended so far this one is relatively recent: it was
|
|||
|
published in 2015. It's not free, but I think it's well worth the money it
|
|||
|
costs.</p>
|
|||
|
|
|||
|
<p>The book is written by the author of several very heavily used Common Lisp
|
|||
|
libraries. It's a bit of a grab bag of topics (which is why I think you need
|
|||
|
a decent amount of Lisp under your belt before you tackle it) but it's a very
|
|||
|
well-written grab bag that will teach you a lot of things you won't find in
|
|||
|
other books.</p>
|
|||
|
|
|||
|
<h3 id="s20-final-patterns"><a href="index.html#s20-final-patterns">Final Patterns</a></h3>
|
|||
|
|
|||
|
<p>If you've gotten this far you're pretty invested in Common Lisp, and I want to
|
|||
|
recommend one not-strictly-technical book that I think you'll really enjoy:
|
|||
|
Patterns of Software by Richard Gabriel. It's available as a PDF on <a href="https://www.dreamsongs.com/Books.html">the
|
|||
|
author's site</a>, and you can still find used print copies online if you
|
|||
|
prefer.</p>
|
|||
|
|
|||
|
<p>This is <em>not</em> the "Gang of Four"/"Design Patterns" book that you might have
|
|||
|
already read or heard about, but is a set of essays on a variety of
|
|||
|
loosely-related topics. It's the best book I've read so far this year. I don't
|
|||
|
want to spoil anything in it for you, so I'll just say that I think you'll find
|
|||
|
it well worth your time.</p>
|
|||
|
|
|||
|
<h2 id="s21-where-to-go-from-here"><a href="index.html#s21-where-to-go-from-here">Where to Go From Here</a></h2>
|
|||
|
|
|||
|
<p>If you made it through all the books and activities in the previous section:
|
|||
|
congratulations, you're off to a great start! Now that you've got a decent
|
|||
|
handle on the core language you can explore in many different directions,
|
|||
|
depending on your interests.</p>
|
|||
|
|
|||
|
<h3 id="s22-macros"><a href="index.html#s22-macros">Macros</a></h3>
|
|||
|
|
|||
|
<p>If you want to learn the secrets of macros, you'll probably want to read and
|
|||
|
work through <a href="http://www.paulgraham.com/onlisp.html">On Lisp</a> and <a href="https://letoverlambda.com/">Let Over Lambda</a> (in that order).</p>
|
|||
|
|
|||
|
<p>I'll say that you should take both books (<em>especially</em> the latter) with a large
|
|||
|
grain of salt. A lot of Common Lisp users don't agree with all of the arguments
|
|||
|
and style in these books, but I think they can still provide plenty of value if
|
|||
|
you read them with a critical mind.</p>
|
|||
|
|
|||
|
<h3 id="s23-object-oriented-programming-with-clos"><a href="index.html#s23-object-oriented-programming-with-clos">Object-Oriented Programming with CLOS</a></h3>
|
|||
|
|
|||
|
<p>Common Lisp has some very sophisticated support for Object-Oriented Programming
|
|||
|
through CLOS. If you're like me and have bad memories of OOP from working in
|
|||
|
a Java cube farm, I'd urge you to give CLOS a fair chance to change your mind.</p>
|
|||
|
|
|||
|
<p>Start with <a href="https://www.amazon.com/Object-Oriented-Programming-COMMON-LISP-Programmers/dp/0201175894">Object-Oriented Programming in COMMON LISP: A Programmer's Guide to
|
|||
|
CLOS</a>. It's a wonderfully-written, short and to-the-point book that will
|
|||
|
give you a good overview of how CLOS is intended to be used.</p>
|
|||
|
|
|||
|
<p>If you really want to bend your mind, try <a href="https://www.amazon.com/Art-Metaobject-Protocol-Gregor-Kiczales/dp/0262610744">The Art of the Metaobject
|
|||
|
Protocol</a> (usually abbreviated as AMOP). This book will probably take you
|
|||
|
a couple of tries to get through. Read it until you hit a mental wall, go work
|
|||
|
on other things for a couple of months, and come back and try again. Repeat
|
|||
|
that process as many times as necessary.</p>
|
|||
|
|
|||
|
<h3 id="s24-low-level-programming"><a href="index.html#s24-low-level-programming">Low-Level Programming</a></h3>
|
|||
|
|
|||
|
<p>Low-level programming can mean a lot of different things, so I'll just mention
|
|||
|
one possibility here.</p>
|
|||
|
|
|||
|
<p>If you're interested in writing emulators for old computers, I wrote <a href="../../../2016/12/chip8-cpu/index.html">a series
|
|||
|
of posts</a> on making a <a href="https://en.wikipedia.org/wiki/CHIP-8">CHIP-8</a> emulator in Common Lisp.
|
|||
|
<a href="https://github.com/kingcons/cl-6502">cl-6502</a> is an emulator for the processor used in the NES (and lots of other
|
|||
|
things) and has a really nice <a href="http://redlinernotes.com/docs/cl-6502.pdf">literate programming</a> version
|
|||
|
that's wonderful to read through.</p>
|
|||
|
|
|||
|
<h3 id="s25-web-development"><a href="index.html#s25-web-development">Web Development</a></h3>
|
|||
|
|
|||
|
<p>Unfortunately I don't have too many suggestions for web development in Common
|
|||
|
Lisp. I've made a conscious effort to avoid web development in the past five or
|
|||
|
so years, because it seems like the Hamster Wheel of Backwards Incompatibility
|
|||
|
has become more of a Hamster Centrifuge in that field.</p>
|
|||
|
|
|||
|
<p>There is a <code>#lispweb</code> channel on Freenode and a <code>#webdev</code> channel in the Lisp
|
|||
|
Discord, so if you have questions you could start by asking there. Those
|
|||
|
channels are a bit less populated than the other Lisp channels, so don't expect
|
|||
|
an answer immediately.</p>
|
|||
|
|
|||
|
<h3 id="s26-game-development"><a href="index.html#s26-game-development">Game Development</a></h3>
|
|||
|
|
|||
|
<p>Common Lisp has a small but enthusiastic community of people who like making
|
|||
|
games. There's a <code>#lispgames</code> channel on Freenode and a <code>#gamedev</code> channel on
|
|||
|
the Lisp Discord that you should join if you're interested.</p>
|
|||
|
|
|||
|
<p><a href="http://landoflisp.com/">Land of Lisp</a> is a fun book to go through. The coding
|
|||
|
style in the book has some... "eccentricities", which is why I don't recommend
|
|||
|
it as a first book on Lisp (e.g. using <code>ash</code> instead of <code>truncate</code> or <code>floor</code>
|
|||
|
for integer division), but if you know the language and just want to get started
|
|||
|
making some simple games I think you'll enjoy working through it.</p>
|
|||
|
|
|||
|
<p>If you want an excuse to make a game in Lisp in a week, the Lisp Game Jam is
|
|||
|
something you can join. It's usually held once or twice each year, so you'll
|
|||
|
have to search around (or ask in <code>#lispgames</code>) to find out when the next one is.</p>
|
|||
|
|
|||
|
<p>Lisp doesn't have any engine as full-featured as Unity, but several people are
|
|||
|
currently working on making 3D game engines. Ask around to see what people are
|
|||
|
using these days. Unfortunately a 3D game engine will generally need to
|
|||
|
interface with the OS to render images and produce audio, and so can't be
|
|||
|
written in pure Common Lisp. This means that some running on the Hamster Wheel
|
|||
|
of Backwards Incompatibility will be necessary to keep up with OS changes (e.g.
|
|||
|
<a href="https://www.macrumors.com/2018/06/05/apple-deprecates-opengl-opencl-gaming/">Apple deprecating OpenGL</a>).</p>
|
|||
|
|
|||
|
<p>If you're interested in old-school ASCII/tile-based games, I've personally done
|
|||
|
some work with using <a href="https://github.com/HiTECNOLOGYs/cl-charms">ncurses</a> and <a href="http://foo.wyrd.name/en:bearlibterminal">bearlibterminal</a> in Common Lisp.
|
|||
|
There's something really fun about making a game people can play over telnet!
|
|||
|
Feel free to get in touch with me if you're interested in that kind of stuff and
|
|||
|
want to know more.</p>
|
|||
|
|
|||
|
<h3 id="s27-window-management"><a href="index.html#s27-window-management">Window Management</a></h3>
|
|||
|
|
|||
|
<p>If you're running Linux and like tinkering with your desktop environment,
|
|||
|
<a href="https://stumpwm.github.io/">StumpWM</a> is an X window manager written in Common Lisp. I've just recently
|
|||
|
switched back to Linux so I've only been using it for about two months, but it's
|
|||
|
really pleasant to be able to customize my working environment with Common Lisp.</p>
|
|||
|
|
|||
|
<p>StumpWM has a small but friendly community — if you're looking for a non-trivial
|
|||
|
open source Common Lisp project to contribute to, StumpWM would be a great
|
|||
|
choice.</p>
|
|||
|
|
|||
|
<h3 id="s28-unit-testing"><a href="index.html#s28-unit-testing">Unit Testing</a></h3>
|
|||
|
|
|||
|
<p>If you're coming from a modern language, especially one with a lot of
|
|||
|
test-driven development advocates, you might be surprised at the lack of an
|
|||
|
emphasis on unit testing in Common Lisp. I think one reason for this is that in
|
|||
|
some languages a unit test is the simplest way to actually <em>run</em> a function,
|
|||
|
but Lisp's interactive style of development gives you an even easier
|
|||
|
alternative: just <em>run the function</em> in the REPL!</p>
|
|||
|
|
|||
|
<p>Despite the lack of heavy unit testing in the community, there are almost as
|
|||
|
many unit testing <em>frameworks</em> as there are Common Lisp programmers! This is
|
|||
|
probably because making a unit testing framework is so easy with a few macros.
|
|||
|
I love <a href="https://github.com/lmj/1am">1am</a>, but there are <em>plenty</em> more to choose from.</p>
|
|||
|
|
|||
|
<p>Whichever one you choose, please make sure to be a good citizen and create
|
|||
|
a separate ASDF system for your unit tests, so people can use your library
|
|||
|
without having to load Yet Another Testing Framework.</p>
|
|||
|
|
|||
|
<h3 id="s29-more-implementations"><a href="index.html#s29-more-implementations">More Implementations</a></h3>
|
|||
|
|
|||
|
<p>I had you use SBCL and CCL because those are the most popular free Common Lisp
|
|||
|
implementations today, but they aren't the only actively-developed ones out
|
|||
|
there. There's plenty of others you might want to explore:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="https://common-lisp.net/project/armedbear/">ABCL</a> runs on the JVM.</li>
|
|||
|
<li><a href="https://common-lisp.net/project/ecl/main.html">ECL</a> can be embedded in a C program, and can translate Common Lisp code to C code.</li>
|
|||
|
<li><a href="https://github.com/clasp-developers/clasp">CLASP</a> is still under development, but is an implementation designed to be easy to interoperate with C++.</li>
|
|||
|
<li><a href="http://www.lispworks.com/">Lispworks</a> and <a href="https://franz.com/products/allegro-common-lisp/">Allegro CL</a> are commercial implementations with a lot of extra features and support, but are not free.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>(I omitted CLISP because I'm mad at them for choosing a name that confuses the
|
|||
|
heck out of new people. Hey, I warned you this post would contain Opinions™.)</p>
|
|||
|
|
|||
|
<p>I tend to use SBCL for my own projects, but I make sure the units tests for all
|
|||
|
my libraries run in SBCL, CCL, ABCL, and ECL. This keeps me honest and gives me
|
|||
|
a reasonable degree of confidence that I'm writing portable code.</p>
|
|||
|
|
|||
|
<h2 id="s30-modern-common-lisp"><a href="index.html#s30-modern-common-lisp">Modern Common Lisp</a></h2>
|
|||
|
|
|||
|
<p>Common Lisp is old and stable, but that doesn't mean it's stagnant. The
|
|||
|
language gives you plenty of power to build on, and before I wrap this up I want
|
|||
|
go over a couple of recent developments in the Common Lisp world that the older
|
|||
|
books you've been learning from don't talk about. I also want to clarify some
|
|||
|
things that often trip up new people.</p>
|
|||
|
|
|||
|
<h3 id="s31-structure"><a href="index.html#s31-structure">Structure</a></h3>
|
|||
|
|
|||
|
<p>Common Lisp's terminology for various parts of projects is often confusing to
|
|||
|
new people because it's old and uses a lot of words that we use now (like
|
|||
|
“package”) to mean subtly different things than people mean today. Things get
|
|||
|
easier once you internalize what Common Lisp means by the terms.</p>
|
|||
|
|
|||
|
<p>(Side note: I posted a quick-and-dirty version of this section as a <a href="https://lobste.rs/s/fwhuz5/my_lisp_journey_1_getting_started_with#c_ebhvzq">comment</a>
|
|||
|
on Lobste.rs while I was waiting for a plane — this section of the post is an
|
|||
|
expanded version of that comment.)</p>
|
|||
|
|
|||
|
<h4 id="s32-packages"><a href="index.html#s32-packages">Packages</a></h4>
|
|||
|
|
|||
|
<p>We often see questions in IRC and Discord that look something like: "How do I
|
|||
|
export a class from a package"? Questions worded like this are a sign of a
|
|||
|
very common misunderstanding about what packages in Common Lisp <em>actually are</em>.</p>
|
|||
|
|
|||
|
<p><strong>A package in Common Lisp is a container for symbols</strong>. That's it. They're
|
|||
|
a way to group related names (symbols) together so you don't have to do the
|
|||
|
miserable prefixing of every name with <code>mylibrary-...</code> like you need to do in
|
|||
|
Emacs Lisp or C to avoid name clashes.</p>
|
|||
|
|
|||
|
<p>You don't export a class from a package, you export a <em>symbol</em>. You don't
|
|||
|
import a function, you import the <em>symbol</em> it's attached to. This sounds
|
|||
|
pedantic, but is important to keep clear in your head as you start using the
|
|||
|
package system. If you're not clear on what exactly a symbol <em>is</em>, I wrote
|
|||
|
a <a href="../../../2016/06/symbolic-computation/index.html">separate post</a> just about symbols which you might find helpful.</p>
|
|||
|
|
|||
|
<p>Another major tripping point for new people is the relationship between packages
|
|||
|
and files. Or, rather: the completely <em>lack</em> of any relationship in Common
|
|||
|
Lisp.</p>
|
|||
|
|
|||
|
<p>In many languages like Python, Java, or Clojure, a file's package and its
|
|||
|
location on the hard drive are tied together. For example: when you say <code>import
|
|||
|
foo.bar.baz</code> in Python, Python will look for a <code>baz.py</code> file inside the
|
|||
|
<code>foo/bar/</code> directory (it's a little more complicated than this, but that doesn't
|
|||
|
matter for this example).</p>
|
|||
|
|
|||
|
<p>In Common Lisp, this is not the case. <strong>Files and packages are completely
|
|||
|
unrelated in Common Lisp.</strong> You can have many files that all work in the same
|
|||
|
package, or one file that switches between many packages, or even create or
|
|||
|
modify packages at runtime.</p>
|
|||
|
|
|||
|
<p>This gives you the flexibility to work however you want. For example: in my
|
|||
|
procedural art library <a href="https://github.com/sjl/flax">Flax</a> most of the packages are each used in one
|
|||
|
specific file, much like you would do in modern languages. But the
|
|||
|
<code>flax.drawing</code> package contains not only a drawing protocol but also several
|
|||
|
implementations of that protocol (PNG, SVG, etc), and so I split the code into
|
|||
|
<a href="https://github.com/sjl/flax/tree/master/src/drawing">a series of separate files</a>, each one dealing with how to draw
|
|||
|
a single format (plus one for the protocol itself).</p>
|
|||
|
|
|||
|
<p>I could have created separate packages for each implementation and set up the
|
|||
|
imports/exports between them, but I didn't feel like the extra boilerplate was
|
|||
|
worth it. Common Lisp is flexible enough to let you make such choices.</p>
|
|||
|
|
|||
|
<p>So if files and packages aren't related, the next question is: how does Common
|
|||
|
Lisp know where to <em>find</em> anything on disk when it comes time to load the code?</p>
|
|||
|
|
|||
|
<h4 id="s33-systems"><a href="index.html#s33-systems">Systems</a></h4>
|
|||
|
|
|||
|
<p>A system in Common Lisp is a collection of serveral things:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>Some code.</li>
|
|||
|
<li>A description of how to load that code.</li>
|
|||
|
<li>A list of other systems this system depends on, which need to be loaded prior
|
|||
|
to loading this one.</li>
|
|||
|
<li>Some metadata like author, license, version, homepage, etc.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>The Common Lisp language itself has no knowledge of systems. If you look at
|
|||
|
<a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node120.html">section 11.9</a> of CLtL2 you'll see that it was imagined that
|
|||
|
each author would write their own custom file to load their code. But since
|
|||
|
Common Lisp gives you the power to abstract almost anything, people eventually
|
|||
|
abstracted the process of loading Common Lisp code.</p>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/asdf/">ASDF</a> is a Common Lisp library bundled with most modern implementations
|
|||
|
which handles defining and loading systems. The name ASDF stands for "Another
|
|||
|
System Definition Facility", so as you might guess there have been several
|
|||
|
other such libraries. ASDF is the one everyone uses today.</p>
|
|||
|
|
|||
|
<p>ASDF standardizes the process of defining a system into something like this:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>The system definition(s) for a project called <code>foo</code> would be in a file named <code>foo.asd</code>.</li>
|
|||
|
<li>Each system is defined with a <code>(defsystem ...)</code> form inside this file.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>We'll talk more about what a "project" is shortly. Note the extension of the
|
|||
|
file is <code>asd</code>, not <code>asdf</code>, which is a little confusing, but was probably chosen
|
|||
|
to work in environments with three-letter-extension limits.</p>
|
|||
|
|
|||
|
<p>The <a href="https://common-lisp.net/project/asdf/#documentation">ASDF manual</a> is the definitive resource for the syntax and semantics of
|
|||
|
<code>defsystem</code>, but can be a little heavy to read if you're just getting started.
|
|||
|
Another way to get started is to read some <code>.asd</code> files of some small-to-medium
|
|||
|
sized open source projects and see how they handle things.</p>
|
|||
|
|
|||
|
<p>Systems and packages are orthogonal in Common Lisp. Some systems (like small
|
|||
|
libraries) will define exactly one package. Some systems will define multiple
|
|||
|
packages. Rarely a system might not define any new packages, but will use or
|
|||
|
add to an existing one.</p>
|
|||
|
|
|||
|
<p>For example:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>My directed graph library <a href="https://github.com/sjl/cl-digraph">cl-digraph</a> contains a system called <code>cl-digraph</code>.</li>
|
|||
|
<li>That system has a description of how to load the code, which lives in the
|
|||
|
<a href="https://github.com/sjl/cl-digraph/blob/master/cl-digraph.asd"><code>cl-digraph.asd</code></a> file.</li>
|
|||
|
<li>One of the files specified for loading is <a href="https://github.com/sjl/cl-digraph/blob/master/package.lisp"><code>package.lisp</code></a>,
|
|||
|
which creates a package called <code>digraph</code>.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Even though ASDF standardizes some aspects of system definition, it still gives
|
|||
|
you plenty of flexibility. As you read projects by different authors you'll
|
|||
|
encounter different ways of organizing systems — this can be a little
|
|||
|
overwhelming at first, but it means you can organize a system in the way that
|
|||
|
works <em>best for that system</em>, which is really nice once you've got some
|
|||
|
experience under your belt.</p>
|
|||
|
|
|||
|
<p>One example of this is how people define packages for their systems. There are
|
|||
|
a couple of common ways to do this you'll see in the wild:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>A single <code>package.lisp</code> file which contains all the definitions for all the
|
|||
|
packages in the project, and gets loaded before all other files. This is the
|
|||
|
strategy I usually prefer.</li>
|
|||
|
<li>Each file defines its package at the top of the file, much like you would in
|
|||
|
Clojure or other modern languages. Care is taken in the system definition to
|
|||
|
load the files in the correct order so that each package is defined before it
|
|||
|
is ever used.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>To review: a system is a collection of code and a description of how to load it,
|
|||
|
a list of its dependencies, and some metadata. Now let's move up one level
|
|||
|
higher to the final layer of structure you need to know about.</p>
|
|||
|
|
|||
|
<h4 id="s34-projects"><a href="index.html#s34-projects">Projects</a></h4>
|
|||
|
|
|||
|
<p>A project in Common Lisp is not an official term defined anywhere that I know
|
|||
|
of, but is a word that's generally used to mean something like a library,
|
|||
|
a framework, an application, etc.</p>
|
|||
|
|
|||
|
<p>A project will usually define at least one system, because systems are where
|
|||
|
you describe how to load the code, and if a project didn't define a system how
|
|||
|
would you know how to load its code? My string-wrapping library <a href="https://github.com/sjl/bobbin">Bobbin</a> is
|
|||
|
a project that defines <em>two</em> systems:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><p>The <code>bobbin</code> system contains the actual data structure and API. It has no
|
|||
|
dependencies.</p></li>
|
|||
|
<li><p>The <code>bobbin/test</code> system contains the unit tests. It depends on the <code>bobbin</code>
|
|||
|
system (because that's the code it's going to test) and the <code>1am</code> system (a
|
|||
|
unit test framework). I made this a separate system because it allows users
|
|||
|
to load the main code without also having to load the unit testing framework
|
|||
|
if they're not going to be running the tests.</p></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>Both of these systems are defined in the <a href="https://github.com/sjl/bobbin/blob/master/bobbin.asd"><code>bobbin.asd</code> file</a>. ASDF
|
|||
|
<a href="https://common-lisp.net/project/asdf/asdf.html#index-find_002dsystem">treats systems with a forward slash in their name specially</a> and
|
|||
|
knows to look for them in the <code>asd</code> file named with the text before the slash.</p>
|
|||
|
|
|||
|
<p>We saw how Common Lisp has no concept of a system — that concept comes from
|
|||
|
ASDF. Similarly, ASDF has no concept of the internet or of reaching out to
|
|||
|
somewhere to download things. ASDF assumes you have somehow acquired the
|
|||
|
systems you want to load and stored them on your hard drive, perhaps by sending
|
|||
|
a check to an address and receiving a copy of the code on floppy disk, as many
|
|||
|
of my old Lisp books offer in their final pages.</p>
|
|||
|
|
|||
|
<p><a href="https://www.quicklisp.org/beta/">Quicklisp</a> is another library that works on top of ASDF to provide the
|
|||
|
"download projects from the internet automatically if necessary" functionality
|
|||
|
that people expect in the modern world. So when you say <code>(ql:quickload
|
|||
|
:bobbin)</code> you’re asking Quicklisp to download Bobbin (and any dependencies) if
|
|||
|
necessary, and then hand it off to ASDF to actually load the code of the
|
|||
|
<code>bobbin</code> system.</p>
|
|||
|
|
|||
|
<p>Unlike ASDF, Quicklisp is relatively new in the Common Lisp world (it's only
|
|||
|
about eight years old) and so is not bundled with any modern Lisp
|
|||
|
implementations that I know of, which is why you need to install it separately.</p>
|
|||
|
|
|||
|
<h4 id="s35-recap"><a href="index.html#s35-recap">Recap</a></h4>
|
|||
|
|
|||
|
<p>Here's a quick recap of the different layers of project structure you'll
|
|||
|
encounter in Common Lisp. Jot these down on a post it note you can refer to as
|
|||
|
you're learning.</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><strong>Files</strong> are files on your hard drive.</li>
|
|||
|
<li><strong>Packages</strong> are containers of symbols. They are orthogonal to files.</li>
|
|||
|
<li><strong>Systems</strong> are collections of code, instructions on how to load that code,
|
|||
|
dependency lists, and metadata. They are orthogonal to packages.</li>
|
|||
|
<li><strong>Projects</strong> are high-level collections of... "stuff" such as code,
|
|||
|
documentation, maybe some image assets, etc. They are (mostly) orthogonal to
|
|||
|
systems (are you seeing a trend here?).</li>
|
|||
|
<li>Common Lisp itself knows about files and packages.</li>
|
|||
|
<li>ASDF adds systems.</li>
|
|||
|
<li>Quicklisp adds the internet.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h3 id="s36-common-libraries"><a href="index.html#s36-common-libraries">Common Libraries</a></h3>
|
|||
|
|
|||
|
<p>Common Lisp doesn't have as <em>large</em> of a community as some newer languages, but
|
|||
|
it still has a lot of libraries because it's had a community for a longer time.
|
|||
|
The stability of the core language means that many libraries written in portable
|
|||
|
Common Lisp ten or fifteen years ago can still run just fine today.</p>
|
|||
|
|
|||
|
<p>In this final section I'll give you a quick overview of some of the more popular
|
|||
|
libraries you might run into as you learn the language. You don't have to use
|
|||
|
all of them, but it's helpful to have some idea of what's available.</p>
|
|||
|
|
|||
|
<h4 id="s37-alexandria"><a href="index.html#s37-alexandria">Alexandria</a></h4>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/alexandria/">Alexandria</a> is one of the most popular Common Lisp libraries (the
|
|||
|
name is a pun on the <a href="https://en.wikipedia.org/wiki/Library_of_Alexandria">Library of Alexandria</a>), and it's
|
|||
|
a collection of all kinds of useful little utility functions like
|
|||
|
<code>read-file-into-byte-vector</code> and <code>map-permutations</code>.</p>
|
|||
|
|
|||
|
<p>There are a <em>lot</em> of utility libraries for Common Lisp around — one rite of
|
|||
|
passage is building up your own personal utility library over time — but
|
|||
|
Alexandria is the most popular one. Most projects with any dependencies at all
|
|||
|
will eventually end up with Alexandria in the dependency graph somewhere.</p>
|
|||
|
|
|||
|
<h4 id="s38-bordeaux-threads"><a href="index.html#s38-bordeaux-threads">Bordeaux Threads</a></h4>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/bordeaux-threads/">Bordeaux Threads</a> was mentioned earlier. Threads aren't part of the Common
|
|||
|
Lisp standard, but most implementations provide their own custom interface for
|
|||
|
working with them. Bordeaux Threads wraps all these implementation-specific
|
|||
|
interfaces and provides an API so you can write threaded code that will work
|
|||
|
portably.</p>
|
|||
|
|
|||
|
<p>If you're looking for something like Java's <code>new Thread(() -> foo()).start()</code>,
|
|||
|
this is what you want.</p>
|
|||
|
|
|||
|
<h4 id="s39-cffi"><a href="index.html#s39-cffi">CFFI</a></h4>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/cffi/">CFFI</a> is a foreign-function interface library that lets you load
|
|||
|
C libraries (e.g. <code>foo.dylib</code> or <code>foo.so</code>) and call the functions in them. It
|
|||
|
works by wrapping implementation-specific interfaces, because this isn't part of
|
|||
|
the Common Lisp standard.</p>
|
|||
|
|
|||
|
<p>Unfortunately it has the same name as Python's FFI library, so if you're
|
|||
|
searching for documentation make sure you're looking at the right version.</p>
|
|||
|
|
|||
|
<h4 id="s40-cl-ppcre"><a href="index.html#s40-cl-ppcre">CL-PPCRE</a></h4>
|
|||
|
|
|||
|
<p><a href="https://edicl.github.io/cl-ppcre/">CL-PPCRE</a> is an implementation of Perl-compatible regular expressions.
|
|||
|
If you're looking to use regular expressions in Common Lisp, this is what you
|
|||
|
want.</p>
|
|||
|
|
|||
|
<h4 id="s41-drakma"><a href="index.html#s41-drakma">Drakma</a></h4>
|
|||
|
|
|||
|
<p><a href="https://edicl.github.io/drakma/">Drakma</a> is an HTTP client. If you need to make an HTTP request, this is what
|
|||
|
you want. There are other HTTP clients around, but Drakma is commonly used and
|
|||
|
is fine for almost anything you might need.</p>
|
|||
|
|
|||
|
<h4 id="s42-iterate"><a href="index.html#s42-iterate">Iterate</a></h4>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/iterate/">Iterate</a> is a replacement for the <code>loop</code> macro. It works similarly,
|
|||
|
but has a more Lispy syntax and a well-defined API for extending it with new
|
|||
|
iteration constructs. I really like it myself, but beware: if you get used
|
|||
|
to <code>iterate</code> going back to vanilla <code>loop</code> will feel painful.</p>
|
|||
|
|
|||
|
<h4 id="s43-local-time"><a href="index.html#s43-local-time">local-time</a></h4>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/local-time/">local-time</a> is a library for working with time and dates in Common Lisp. The
|
|||
|
standard has some basic support for times built in, but if you want to do much
|
|||
|
calculation with times (including timezones) this is probably what you want. If
|
|||
|
you're looking for something like <a href="http://www.joda.org/joda-time/">Joda Time</a> in Common Lisp, this is
|
|||
|
as close as you're going to get.</p>
|
|||
|
|
|||
|
<h4 id="s44-lparallel"><a href="index.html#s44-lparallel">lparallel</a></h4>
|
|||
|
|
|||
|
<p><a href="https://lparallel.org/">lparallel</a> is a library that builds on top of Bordeaux Threads to make common
|
|||
|
parallel processing operations much easier. Think of it as <a href="https://www.gnu.org/software/parallel/">GNU Parallel</a> for
|
|||
|
Lisp, with a few extra features (e.g. channels and tasks).</p>
|
|||
|
|
|||
|
<p>For example: if you've got a big vector you're mapping over with
|
|||
|
<code>(map 'vector #'work some-vector)</code> you can split it into chunks and
|
|||
|
run in multiple threads by changing it to
|
|||
|
<code>(lparallel:pmap 'vector #'work some-vector)</code>.</p>
|
|||
|
|
|||
|
<h4 id="s45-named-readtables"><a href="index.html#s45-named-readtables">Named Readtables</a></h4>
|
|||
|
|
|||
|
<p><a href="https://github.com/melisgl/named-readtables">Named readtables</a> is a library that adds namespaces for
|
|||
|
readtables.</p>
|
|||
|
|
|||
|
<p>One painful part of the standard is that reader macros are added and removed to
|
|||
|
the global readtable on the fly, so if you load multiple systems that define the
|
|||
|
same reader macros things can get messy. Named readtables adds some much-needed
|
|||
|
hygiene to that process. If you're working with reader macros at all you
|
|||
|
absolutely want to use this.</p>
|
|||
|
|
|||
|
<h4 id="s46-roswell"><a href="index.html#s46-roswell">Roswell</a></h4>
|
|||
|
|
|||
|
<p><a href="https://github.com/roswell/roswell">Roswell</a> is a couple of things rolled into one. It's a C program that
|
|||
|
handles installing and running multiple different Common Lisp implementations
|
|||
|
(kind of like <a href="https://github.com/creationix/nvm">NVM</a> or
|
|||
|
<a href="https://rvm.io/">rvm</a>), and it also provides a unified way to write small shell
|
|||
|
scripts in Common Lisp and compile them into binaries.</p>
|
|||
|
|
|||
|
<p>I used Roswell for a little over a year, but I eventually stopped and now
|
|||
|
I don't think it's worth the trouble, for a couple of reasons.</p>
|
|||
|
|
|||
|
<p>First: if you write portable code you generally don't need to worry running
|
|||
|
a particular version of an implementation, because Common Lisp is so stable.
|
|||
|
I usually just install the latest version of each implementation I use with
|
|||
|
a package manager or by building from source.</p>
|
|||
|
|
|||
|
<p>Second: after using it for a while I found that Roswell was always very brittle
|
|||
|
to upgrade, and whenever things broke it would spew an almost JVM-sized stack
|
|||
|
trace without a decent error message.</p>
|
|||
|
|
|||
|
<p>For me, the negatives outweighed the positives. I'd recommend simply using the
|
|||
|
latest version of the implementations you care about and writing portable code.
|
|||
|
For the compiling-into-binaries functionality I'd recommend using your
|
|||
|
implementation's built-in support for this, or using UIOP's wrapper around that,
|
|||
|
or using a separate library like <a href="https://shinmera.github.io/deploy/">Deploy</a>.</p>
|
|||
|
|
|||
|
<p>Of course your mileage might vary. If you find yourself <em>really</em> needing to run
|
|||
|
specific versions of specific Common Lisp implementations in rapid succession,
|
|||
|
you should look into Roswell.</p>
|
|||
|
|
|||
|
<h4 id="s47-series"><a href="index.html#s47-series">SERIES</a></h4>
|
|||
|
|
|||
|
<p><a href="https://www.cliki.net/Series">SERIES</a> was almost included in Common Lisp (it's in <a href="https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node347.html">Appendix A of
|
|||
|
CLtL2</a>), but didn't quite make it. It's a library for writing
|
|||
|
functional code that looks like the traditional <code>map</code> and <code>filter</code> and <code>reduce</code>
|
|||
|
operations but which compiles down to efficient loops.</p>
|
|||
|
|
|||
|
<p>If you're looking for Clojure's transducers in Common Lisp, this is what you
|
|||
|
want.</p>
|
|||
|
|
|||
|
<h4 id="s48-st-json"><a href="index.html#s48-st-json">st-json</a></h4>
|
|||
|
|
|||
|
<p>JSON support in Common Lisp is a god damn mess. There are <a href="https://sites.google.com/site/sabraonthehill/home/json-libraries">an absurd number of
|
|||
|
JSON libraries</a> and I don't really <em>like</em> any of them.</p>
|
|||
|
|
|||
|
<p>For me, the most important quality I need in a JSON library is an unambiguous,
|
|||
|
one-to-one mapping of types. For example: some libraries will deserialize JSON
|
|||
|
arrays as Lisp lists, and JSON <code>true</code>/<code>false</code> as <code>t</code>/<code>nil</code>. But this means <code>[]</code> and
|
|||
|
<code>false</code> both deserialize to <code>nil</code>, so you can't reliably round trip anything!</p>
|
|||
|
|
|||
|
<p>I've settled on using <a href="https://marijnhaverbeke.nl/st-json/">st-json</a> and wrapping it up to be a little more
|
|||
|
ergonomic with some glue code. It's not the fastest solution out there, but
|
|||
|
it works for my needs. There are plenty of other options out there, so if you
|
|||
|
have different needs than me you should look into them.</p>
|
|||
|
|
|||
|
<h4 id="s49-usocket"><a href="index.html#s49-usocket">usocket</a></h4>
|
|||
|
|
|||
|
<p><a href="https://common-lisp.net/project/usocket/">usocket</a> is a library for networking sockets. Sockets and networking aren't
|
|||
|
part of the Common Lisp standard, but most implementations provide a custom
|
|||
|
interface for working with them. usocket wraps the implementation-specific
|
|||
|
interfaces and provides an API so you can write networking code portably.</p>
|
|||
|
|
|||
|
<p>If you want to make Lisp listen on a port and read streams of bytes from
|
|||
|
clients, or want to connect to a port and send raw bytes to it, this is what you
|
|||
|
want.</p>
|
|||
|
|
|||
|
<h2 id="s50-good-luck"><a href="index.html#s50-good-luck">Good Luck!</a></h2>
|
|||
|
|
|||
|
<p>I hope this whirlwind tour was useful. Common Lisp is an old, deep language.
|
|||
|
It's not something you can learn in a month, but if you're willing to spend the
|
|||
|
time it will reward careful study.</p>
|
|||
|
|
|||
|
<p>Feel free to email me or pop into IRC or Discord if you have questions.</p>
|
|||
|
|
|||
|
<p>Good luck!</p>
|
|||
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html>
|