5455ac00e2da

More cleanup
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Mon, 13 Aug 2018 00:05:06 +0000
parents 2a1c58e03bc2
children bf23f95dcb71
branches/tags (none)
files content/blog/2018/08/a-road-to-common-lisp.markdown

Changes

--- a/content/blog/2018/08/a-road-to-common-lisp.markdown	Sun Aug 12 22:49:28 2018 +0000
+++ b/content/blog/2018/08/a-road-to-common-lisp.markdown	Mon Aug 13 00:05:06 2018 +0000
@@ -1,31 +1,31 @@
 +++
 title = "A Road to Common Lisp"
-snip = "One way to learn this old, deep language."
+snip = "One way to learn this old language."
 date = 2018-08-01T16:00:00Z
 draft = false
 
 +++
 
 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.
+the present day (currently 2018 as I'm writing this).  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.
 
 <div id="toc"></div>
 
 ## Context
 
-I think it's important to have a general sense of where Common Lisp came from
-and what kind of a language it is before you start learning it.  There are a lot
-of things that will seem very strange if you're coming straight from modern
+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 a lot of
+things that will seem very strange if you're coming straight from modern
 languages, but will make a lot more sense if you've got a bit of background
 context.
 
 ### History
 
-Lisp and Common Lisp have 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):
+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):
 
 * Wikipedia's [History of Lisp][wiki-history-lisp] and [History of Common Lisp][wiki-history-cl].
 * The [Where it Began section in Practical Common Lisp][pcl-history].
@@ -48,11 +48,11 @@
 diverged from the path we're looking at.  I won't cover Scheme in this post.)
 
 In the early 1980s people decided that having a whole slew of
-mutually-incompatible dialects of Lisp might be not be the most desirable
-situation to be in.  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.  In 1984 the first edition of Guy Steele's [Common Lisp: the
-Language][cltl] was published.
+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
+[Common Lisp: the Language][cltl] was published.
 
 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
@@ -60,7 +60,7 @@
 in 1986 a committee (X3J13) was formed to produce an ANSI specification for
 Common Lisp.
 
-As the committee worked on the standardization process, in 1990 the second
+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
@@ -68,9 +68,9 @@
 For comparison: Python (a "modern" language many people think of as also being
 "kind of old") [was released][python] for the first time the following year.
 
-In 1992 the X3J13 committee published the first draft of the new ANSI standard
-for Common Lisp for public review (see Pitman's paper).  The draft was approved
-in 1994 and the approved specification was finally published in 1995.  At this
+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 [was
 released][rubby] in December of that year.
 
@@ -90,17 +90,17 @@
 
 #### Escaping the Hamster Wheel of Backwards Incompatibility
 
-If you're coming from other languages, you're probably used to things breaking.
-If you want to run Ruby code written ten years ago on the latest version of
-Ruby, it's probably going to take some time 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).
+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 written ten years ago on the latest version of Ruby, it's
+probably going to take some time 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).
 
-If you learn Common Lisp, this is not the case.  In the next section of this
-post I'll be recommending a book written in 1990.  You can run its code,
+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 relief it is to be able to write code and reasonably expect it to
@@ -118,17 +118,22 @@
 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.
 
+#### Practicality Begets Purity
+
 This may sound a little bleak, but it's made a bit better by some other
 characteristics of Common Lisp: 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.  When programming in Common Lisp people will tend to depend on
-a small(ish) number of libraries, and library writers often try to minimize
-dependencies by utilizing as much of the core language as possible.
-I personally try to stick to fewer than ten or so dependencies for my
-applications and no more than two or three for my libraries, but I'm probably
-a bit more conservative here than most folks (I *really* don't like the Hamster
-Wheel any more).
+index.  You can get a surprising amount done by writing pure Common Lisp
+without much extra support.
+
+When programming in Common Lisp people will tend to depend on a small(ish)
+number of libraries, and library writers often try to minimize dependencies by
+utilizing as much of the core language as possible.  I personally try to stick
+to fewer than ten or so dependencies for my applications and no more than two or
+three for my libraries (and preferably zero, if possible), but I'm probably
+a bit more conservative here than most folks.  I *really* don't like the Hamster
+Wheel.
 
 It's also worth noting that since Common Lisp has been around and stable for so
 long, it has *libraries* older and more stable than many programming languages.
@@ -138,18 +143,20 @@
 makes it 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.
 
-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 *done*, not *abandoned*, so don't dismiss them
-out of hand.
+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 *done*, not *abandoned*,
+so don't dismiss them out of hand.
 
-#### Extensibility and Power
+#### Extensibility
 
 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 gives users enough power to add new features to the language as
-libraries without having to alter the core language.
+Common Lisp gives users enough power to add new features to the language without
+having to alter the core language as libraries.
+
+TODO tradeoffs
 
 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
@@ -188,12 +195,14 @@
 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.
 
+#### Power
+
 Macros are one of the things that make Lisp so incredibly 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 fundamentally
 different because they're *part of the language*.
 
-In C you have a layer of macros on top, written in a separate preprocessor macro
+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.
 
@@ -208,7 +217,7 @@
 low-level as something like C or Rust, but you might be surprised at some of the
 things that the ANSI spec includes.
 
-Want to see the assembly that a particular function compiles down to?
+Want to see the assembly a particular function compiles down to?
 [`DISASSEMBLE`][dis] it!
 
 Want to stack-allocate something to avoid some garbage collection?  X3J13
@@ -217,7 +226,7 @@
 Need arrays of unboxed floats to ship to a graphics card?  [The standard allows
 for that][arrays].
 
-Think `GOTO` should be considered helpful, not harmful?  Well okay, we're all
+Think `GOTO` should be considered helpful, not harmful?  Well, okay, we're all
 adults here.  [Good luck][tagbody], try not to shoot your foot off.
 
 Need to do unsigned 8-bit arithmetic in your Game Boy emulator, but would prefer
@@ -287,7 +296,7 @@
 
 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 and/or during the infancy of
+Common Lisp reference material was created before or during the infancy of
 the internet.  There are a *lot* of books about Common Lisp out there.  Some are
 better than others.  I'll recommend the ones I personally think are the best,
 but don't hesitate to browse around and find others.
@@ -314,7 +323,7 @@
 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
+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.
 
 You might also hear about something called Roswell.  Don't use Roswell, you don't
@@ -333,9 +342,9 @@
 MacOS.  That one will work just fine to start.
 
 Emacs, Vim, Sublime Text, Atom, whatever, for now it doesn't matter.  As long as
-it can balance parenthesis and highlight comments and strings, that's all you
-need to start.  Worry about shaving the editor yak once you're more comfortable
-in the language.
+it can balance parenthesis, 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.
 
 ### Hello, Lisp
 
@@ -407,12 +416,14 @@
 
 * How am I ever going to remember all these weird function names?
 * Why do people use strings so rarely?
-* When do I need/not need the goddamned quotation mark?
+* When do I need/not need the god damn quotation mark?
 
-You should also join the `#clnoobs` channel on Freenode 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.
+You should also join the `#clschool` channel on Freenode 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 `#clnoobs` channel, but that was
+mostly abandoned during the latest wave of Freenode spam because no one had ops
+to help combat the spam.
 
 If IRC isn't your thing there's also a [Discord
 server](https://discord.gg/tffeu2x) that some of us hang out in.  Join the
@@ -426,7 +437,7 @@
 
 Once you've finished that book the next one you should attack is [Practical
 Common Lisp][book-pcl].  You can get a paper copy if you want, but the full book
-is available on the site.
+is available on the site for free.
 
 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
@@ -510,19 +521,17 @@
 microseconds.  You don't have to wait for the compiler, so your
 concentration/thought process never has time to wander.
 
-Another advantage is that when you get back the results of your compilation,
-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.
+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.
 
 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 *constantly*.
+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 *constantly*.
 
 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
@@ -550,12 +559,17 @@
 with a living, breathing organism, or like [teaching things to an eager
 assistant][eager].
 
-This goes beyond just the short feedback loop and interactive REPL, too.  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
+This philosophy of Lisp being not just a programming *language* but a living,
+breathing programming [*system*][gabriel-system] goes beyond just the short
+feedback loop and interactive REPL, too. 
+
+[gabriel-system]: 
+
+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:
 
@@ -573,25 +587,25 @@
 that's necessary to debug the problem, unless you're also saving a core dump
 (but I don't know of many people who do that).
 
-In Common Lisp you can certainly choose to panic or ignore on errors, but
+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 at the monster, waiting for you.  At this point you can
+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.
 
 Once you figure out the problem ("Oh, I see, the `calculate-armor-percentage`
 function returns `0` if a shielding spell ran out during the same frame") you
 can fix the code, recompile the problematic function, and *restart the execution
-of that function in the call stack*.  Your warrior's sword lands, and you move
+of that function in the call stack*!  Your warrior's sword lands, and you move
 back to what you were doing before.
 
 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 blood stains on the wall.  You can
-examine the crime *as it's happening* 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!
+trying to piece together what happened by the [blood stains][mickens TODO] on
+the wall.  You can examine the crime *as it's happening* 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!
 
 Maybe you don't make video games.  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
@@ -611,6 +625,40 @@
 [eager]: https://www.reddit.com/r/lisp/comments/4oo1cp/common_lisp_for_clojure_programmer/d4eec68/
 [1ftaf]: https://groups.google.com/forum/message/raw?msg=comp.lang.lisp/9SKZ5YJUmBg/Fj05OZQomzIJ
 
+So how do you actually *get* 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:
+
+* Emacs with [SLIME][TODO] or [Sly][TODO].
+* Vim (or Neovim) with [Vlime][TODO] or [Slimv][TODO].
+
+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).
+
+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.
+
+Otherwise go with Emacs.  I can't really speak to SLIME versus Sly because
+I don't use them, sorry.
+
+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.
+
+On a side note: if anyone is interested in making a Common Lisp [LSP][TODO]
+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.  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 a bit of 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.
+
 ### Learning Paradigms
 
 At this point you should have a pretty good handle on the basics of Common Lisp,
@@ -626,8 +674,8 @@
 This book was written in 199 TODO so it's not about the hyped up AI fields
 you've been hearing about in the news like machine learning or deep learning
 — instead it's a tour of [Good Old-Fashioned AI][gofai].  Even if you're not
-particularly interested (yet?) in this kind of AI, the book is a great example
-of how to write Common Lisp code.
+particularly interested in this kind of AI, the book is a great example of how
+to write Common Lisp code.
 
 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
@@ -655,9 +703,10 @@
 ### Recipes for Success
 
 The final technical book I'll recommend to every aspiring Lisp programmer is
-[Common Lisp Recipes][TODO] (abbreviated as CLR).  Unlike most of the other
-books I've recommended so far this one is relatively recent: it was published in
-2015 TODO.  It's not free, but I think it's well worth the money it costs.
+[Common Lisp Recipes][recipes-TODO], 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 TODO.  It's not free, but I think it's well worth the money it
+costs.
 
 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
@@ -703,11 +752,11 @@
 ### Object-Oriented Programming with CLOS
 
 Common Lisp has some very sophisticated support for Object-Oriented Programming
-through CLOS.  If you have bad memories of OOP from working in a Java cube farm
-like I did, I'd urge you to give CLOS a fair chance to change your mind.
+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.
 
 Start with [Object-Oriented Programming in COMMON LISP: A Programmer's Guide to
-CLOS][Keene] — it's a wonderfully-written, short and to-the-point book that will
+CLOS][Keene].  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.
 
 If you really want to bend your mind, try [The Art of the Metaobject
@@ -721,7 +770,8 @@
 
 ### Low-Level Programming
 
-Low-level programming can mean a lot of different things.
+Low-level programming can mean a lot of different things, so I'll just mention
+two possibilities here.
 
 If you're interested in writing emulators for old computers, I wrote [a series
 of posts][chip8posts] on making a [CHIP-8][] emulator in Common Lisp.
@@ -757,11 +807,11 @@
 games.  There's a `#lispgames` channel on Freenode and a `#gamedev` channel on
 the Lisp Discord that you should join if you're interested.
 
-[Land of Lisp](http://landoflisp.com/) is a fun little 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 `ash` instead of `truncate` or
-`floor` 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.
+[Land of Lisp](http://landoflisp.com/) 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 `ash` instead of `truncate` or `floor`
+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.
 
 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
@@ -801,11 +851,11 @@
 ### Unit Testing
 
 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
+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 *run* a function,
 but Lisp's interactive style of development gives you an even easier
-alternative: just run the function in the REPL!
+alternative: just *run the function* in the REPL!
 
 Despite the lack of heavy unit testing in the community, there are almost as
 many unit testing *frameworks* as there are Common Lisp programmers!  This is
@@ -831,19 +881,19 @@
 “package”) to mean different things than people mean today. Things get easier
 once you internalize what Common Lisp means by the terms.
 
-NOTE: I posted a quick-and-dirty version of this section as a [comment][] on
-Lobste.rs while I was waiting for a plane — this section of the post is an
-expanded version of that comment.
+(Side note: I posted a quick-and-dirty version of this section as a [comment][]
+on Lobste.rs while I was waiting for a plane — this section of the post is an
+expanded version of that comment.)
 
 [comment]: https://lobste.rs/s/fwhuz5/my_lisp_journey_1_getting_started_with#c_ebhvzq
 
 #### Packages
 
-We often see questions in `#clnoobs` that look something like: "How do I export
+We often see questions in `#clschool` 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 *actually are*.
 
-A package in Common Lisp is **a container for symbols**.  That's it.  They're
+**A package in Common Lisp is a container for symbols**.  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 `mylibrary-...` like you need to do in
 Emacs Lisp or C to avoid name clashes.
@@ -855,7 +905,7 @@
 a [separate post][symbols] just about symbols which you might find helpful.
 
 Another major tripping point for new people is the relationship between packages
-and files.  Or, rather, the completely *lack* of any relationship in Common
+and files.  Or, rather: the completely *lack* of any relationship in Common
 Lisp.
 
 In many languages like Python, Java, or Clojure, a file's package and its
@@ -865,7 +915,7 @@
 this example).
 
 In Common Lisp, this is not the case.  **Files and packages are completely
-orthogonal in Common Lisp.**  You can have many files that all work in the same
+unrelated in Common Lisp.**  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.
 
@@ -888,7 +938,7 @@
 
 A system in Common Lisp is a collection of serveral things:
 
-* The code.
+* Some code.
 * A description of how to load that code.
 * A list of other systems this system depends on, which need to be loaded prior
   to loading this one.
@@ -896,15 +946,14 @@
 
 The Common Lisp language itself has no knowledge of systems.  If you look at
 chapter TODO of CLtL2 you'll see that it was imagined that each library author
-would write their own custom file to load their code.
-
-Of course, since Common Lisp gives you the power to abstract almost anything,
-people eventually abstracted the process of loading Common Lisp code.
+would write their own custom file to load their code.  And since Common Lisp
+gives you the power to abstract almost anything, people eventually abstracted
+the process of loading Common Lisp code.
 
-ASDF is a Common Lisp library bundled with most (all?) 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.
+[ASDF][TODO] is a Common Lisp library bundled with most (all?) 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.
 
 ASDF standardizes the process of defining a system into something like this:
 
@@ -929,7 +978,7 @@
 
 * My directed graph library [cl-digraph][] contains a system called `cl-digraph`.
 * That system has a description of how to load the code, which lives in the [`cl-digraph.asd`][cl-digraph-asd] file.
-* As part of loading the system it will load the file [`packages.lisp`][cl-digraph-packages], which creates a package called `digraph`.
+* One of the files specified for loading is [`packages.lisp`][cl-digraph-packages], which creates a package called `digraph`.
 
 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
@@ -941,7 +990,7 @@
 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:
 
-* A single `package.lisp` file which contains all the definitions for all the
+* A single `packages.lisp` 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.
 * Each file defines its package at the top of the file, much like you would in
@@ -1002,8 +1051,7 @@
 then hand it off to ASDF to actually load the code of the `cl-digraph` system.
 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 (yet?), which is why you need to install it
-separately.
+implementations that I know of, which is why you need to install it separately.
 
 #### Recap
 
@@ -1053,7 +1101,7 @@
 interfaces and provides an API so you can write threaded code that will work
 portably.
 
-If you're looking for something like Java's `new Thread(() -> foo()).start();`,
+If you're looking for something like Java's `new Thread(() -> foo()).start()`,
 this is what you want.
 
 #### CFFI
@@ -1131,11 +1179,11 @@
 I personally don't think it's worth the trouble, for a couple of reasons.
 
 First: if you write portable code you generally don't need to worry running
-a particular version of an implementation, because Common Lisp is quite stable.
+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.
 
-Second: after using it for a while I found that Roswell was always quite brittle
+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.
 
@@ -1146,8 +1194,8 @@
 around that, or using a separate library like Shinmera's [Deploy][].
 
 Of course your mileage might vary.  If you find yourself *really* needing to run
-specific versions of specific implementations in rapid succession, you should
-look into Roswell.
+specific versions of specific Common Lisp implementations in rapid succession,
+you should look into Roswell.
 
 [Deploy]: https://shinmera.github.io/deploy/
 
@@ -1175,7 +1223,7 @@
 
 I've settled on using [st-json][] and wrapping it up to be a little more
 ergonomic with some glue code.  It's not the fastest solution out there, but
-it's fine for my needs.  There are plenty of other options out there, so if you
+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.
 
 [sabra]: https://sites.google.com/site/sabraonthehill/home/json-libraries
@@ -1183,10 +1231,10 @@
 
 #### usocket
 
-[usocket][] is a library for 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.
+[usocket][] 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.
 
 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
@@ -1215,5 +1263,6 @@
 It's not something you can learn in a month, but if you're willing to spend the
 time it will reward careful study.
 
-Feel free to email me or pop into IRC or Discord if you have questions.  Good
-luck!
+Feel free to email me or pop into IRC or Discord if you have questions.
+
+Good luck!