ceb68ee2312a

Start new post, continue work on expunging JS
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Wed, 25 Jul 2018 22:32:23 +0000
parents 2fc3e28a1225
children e33111e95d15
branches/tags (none)
files content/blog/2016/02/midpoint-displacement.markdown content/blog/2016/03/recursive-midpoint-displacement.markdown content/blog/2016/06/diamond-square.markdown content/blog/2018/07/a-road-to-common-lisp.markdown static/media/images/blog/2018/07/lisp-kludge.jpeg static/media/js/print.js static/media/js/sjl.js themes/stevelosh/layouts/partials/header.html

Changes

--- a/content/blog/2016/02/midpoint-displacement.markdown	Sat Jul 21 17:42:25 2018 +0000
+++ b/content/blog/2016/02/midpoint-displacement.markdown	Wed Jul 25 22:32:23 2018 +0000
@@ -7,6 +7,7 @@
 
 +++
 
+<script defer src="/media/js/jquery.js" type="text/javascript"></script>
 <script defer src="/media/js/three.min.js"></script>
 <script defer src="/media/js/TrackballControls.js"></script>
 <script defer src="/media/js/terrain1.js"></script>
--- a/content/blog/2016/03/recursive-midpoint-displacement.markdown	Sat Jul 21 17:42:25 2018 +0000
+++ b/content/blog/2016/03/recursive-midpoint-displacement.markdown	Wed Jul 25 22:32:23 2018 +0000
@@ -6,6 +6,7 @@
 
 +++
 
+<script defer src="/media/js/jquery.js" type="text/javascript"></script>
 <script defer src="/media/js/three.min.js"></script>
 <script defer src="/media/js/TrackballControls.js"></script>
 
--- a/content/blog/2016/06/diamond-square.markdown	Sat Jul 21 17:42:25 2018 +0000
+++ b/content/blog/2016/06/diamond-square.markdown	Wed Jul 25 22:32:23 2018 +0000
@@ -6,6 +6,7 @@
 
 +++
 
+<script defer src="/media/js/jquery.js" type="text/javascript"></script>
 <script defer src="/media/js/three.min.js"></script>
 <script defer src="/media/js/TrackballControls.js"></script>
 <script defer src="/media/js/terrain3.js"></script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/content/blog/2018/07/a-road-to-common-lisp.markdown	Wed Jul 25 22:32:23 2018 +0000
@@ -0,0 +1,664 @@
++++
+title = "A Road to Common Lisp"
+snip = "How I learned Lisp, and you can too."
+date = 2018-07-03T16: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.
+
+<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
+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):
+
+* 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].
+* The [History: Where did Lisp come from?][cll-history] section of the comp.lang.lisp FAQ.
+* [Common Lisp: the Untold Story][untold] by Kent Pitman.
+* [The Evolution of Lisp][evolution] by Guy Steele and Richard Gabriel.
+
+I realize that you probably won't want to read all of the links above
+immediately, so here's a whirlwind tour of sixty years of Lisp.
+
+Lisp began in the late 1950's.  It was invented by John McCarthy at MIT.
+
+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 *lot* of different implementations, all growing,
+changing, and trying out different things.
+
+(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.)
+
+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.
+
+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.
+
+As 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 30 years of experience and history to draw upon.
+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 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 point Lisp is
+over thirty-five years old.  The first version of Ruby [was released][rubby] in
+December of that year.
+
+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.
+
+### Consequences
+
+So why am I telling you all this?  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.
+
+#### 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 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,
+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
+still work in twenty years.
+
+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 the Common Lisp
+community seems overall fairly good about maintaining backwards compatibility.
+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/or 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.
+
+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 (large) 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).
+
+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.
+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 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.
+
+#### Power
+
+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.
+
+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.
+
+Common Lisp doesn't include string interpolation.  You want it?  No problem, you
+don't have to wait for [Scala
+2.10](https://docs.scala-lang.org/overviews/core/string-interpolation.html) or
+[Python 3.6](https://www.python.org/dev/peps/pep-0498/), just [use
+a library][cl-interpol].
+
+Want to try some nondeterministic programming without any boilerplate?  [Grab
+a library][screamer].
+
+Pattern matching syntax can make for some really beautiful, readable code.
+Common Lisp doesn't include it, but of course [there's a library][trivia].
+
+Enjoying algebraic data types in Haskell or Scala?  Here's your [library][cl-adt].
+
+All of these libraries rely on macros to make using them feel seamless.  Of
+course you could *do* all of that without macros, but you've have to add a layer
+of boilerplate to manage evaluation.  This:
+
+    (match foo
+      '(list x y z) (lambda (x y z) (+ x y z))
+      '(vector x y) (lambda (x y) (- x y)))
+
+just doesn't flow off the fingers like:
+
+    (match foo
+      ((list x y z) (+ x y z))
+      ((vector x y) (- x y)))
+
+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 95% 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.
+
+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
+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.
+
+In Common Lisp, you write macros *in Common Lisp itself*.  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 *feedback loop* of abstractive power.
+
+But macros aren't the only thing about Common Lisp that make it so practical and
+extensible.  Something that 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 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?
+[`DISASSEMBLE`][dis] it!
+
+Want to stack-allocate something to avoid some garbage collection?  X3J13
+[thought of that][dynext].
+
+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
+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
+it to compile down to just a machine instruction or two?  [It's
+possible][arith].
+
+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
+*do* support these kinds of things will take advantage of the optimization
+opportunities.
+
+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.
+
+#### Ugliness
+
+It's also important to realize that while Common Lisp might be incredibly
+practical, the need to accommodate existing users and dialects means that there
+are 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:
+
+[![Photo of a page of CLtL2's Index, listing "kludges" as pages 1 to 971](/media/images/blog/2018/07/lisp-kludge.jpeg)](/media/images/blog/2018/07/lisp-kludge.jpeg)
+
+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 `RPLACD` written
+on the side sitting off in a corner where no one's touched it for twenty years.
+
+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 the language probably would have been ignored as too
+different to port implementations and code to, instead of being adopted and
+embraced.
+
+[wiki-history-cl]: https://en.wikipedia.org/wiki/Common_Lisp#History
+[wiki-history-lisp]: https://en.wikipedia.org/wiki/Lisp_(programming_language)#History
+[cll-history]: https://www.cs.cmu.edu/Groups//AI/lang/lisp/faq/lisp_2.faq
+[pcl-history]: http://www.gigamonkeys.com/book/introduction-why-lisp.html#where-it-began
+[untold]: http://www.nhplace.com/kent/Papers/cl-untold-story.html
+[evolution]: https://www.dreamsongs.com/Files/HOPL2-Uncut.pdf
+[cltl]: https://www.cs.cmu.edu/Groups/AI/html/cltl/cltl2.html
+[python]: https://en.wikipedia.org/wiki/History_of_Python#Early_history
+[rubby]: https://en.wikipedia.org/wiki/Ruby_(programming_language)#First_publication
+[bt-release]: https://web.archive.org/web/20040831185622/http://ww.telent.net/diary/2004/7/#9.57181
+[cl-interpol]: https://edicl.github.io/cl-interpol/
+[screamer]: https://nikodemus.github.io/screamer/
+[trivia]: https://github.com/guicho271828/trivia/wiki/What-is-pattern-matching%3F-Benefits%3F
+[cl-adt]: https://github.com/tarballs-are-good/cl-algebraic-data-type
+[dis]: http://clhs.lisp.se/Body/f_disass.htm
+[dynext]: http://clhs.lisp.se/Body/d_dynami.htm
+[arrays]: http://clhs.lisp.se/Body/15_ab.htm
+[arith]: https://pdfs.semanticscholar.org/7089/0eff0e93aba49174a9346731f4bf9225706d.pdf
+[tagbody]: http://clhs.lisp.se/Body/s_tagbod.htm
+
+## A Road to Learning Common Lisp
+
+If all of this hasn't scared you away from the language, let's talk about how
+you can learn it in 2018.
+
+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
+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.
+
+One final disclaimer: this is *a* road to Common Lisp, not *the* road to Common
+Lisp.  It's what I followed (without some of the dead ends) and has a lot of my
+personal opinions baked in, but is by no means the only way to learn the
+language.  Ask around and get some more opinions — more options won't hurt!
+
+### Get a Lisp
+
+To get started with Common Lisp you'll need to install a Common Lisp
+implementation.  Remember: 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:
+
+* If you're comfortable with the command line, installing packages with a package manager, and already have a text editor you like, choose [SBCL][].
+* Otherwise, choose [ClozureCL][CCL] (often called "CCL").
+
+That's Clozure with a Z.  Clojure is something entirely different that just
+happens to have a confusingly similar name.
+
+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.
+
+You might also hear about something called Roswell.  Don't use Roswell, you don't
+need it (yet (or at all)).
+
+Just install SBCL or CCL for now, you can explore the other options once you've
+got your bearings a bit better.
+
+### Pick an Editor
+
+You might hear people tell you that you *must* 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.
+
+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.
+
+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.
+
+### Hello, Lisp
+
+To check that you've got everything set up properly, make a `hello.lisp` file
+with the following contents:
+
+    :::lisp
+    (defun hello ()
+      (write-line "What is your name?")
+      (let ((name (read-line)))
+        (format t "Hello, ~A.~%" name)))
+
+Don't worry about what this means yet, it's just a check that everything's
+working properly.
+
+Open an SBCL or CCL REPL and load the file by entering `(load "hello.lisp")`,
+then call the function and make sure it works.  It should look something like
+this if you picked SBCL:
+
+    $ sbcl
+    * (load "hello.lisp")
+
+    T
+    * (hello)
+    What is your name?
+    Steve
+    Hello, Steve.
+    NIL
+    *
+
+Or this if you chose CCL (the program might be annoyingly named `ccl64` if
+you're on a 64-bit system):
+
+    $ ccl64
+    Clozure Common Lisp Version ...
+
+    ? (load "hello.lisp")
+    #P"/home/sjl/Desktop/hello.lisp"
+    ? (hello)
+    What is your name?
+    Steve
+    Hello, Steve.
+    NIL
+    ?
+
+If your arrow keys and backspace don't work in the REPL, use [`rlwrap`][rlwrap]
+to fix that.  `rlwrap sbcl` will give you a non-miserable REPL.  `rlwrap` is
+a handy tool to have in your toolbox anyway.
+
+[rlwrap]: https://github.com/hanslub42/rlwrap
+
+### A Gentle Introduction
+
+The best book I've found for getting started in Common Lisp is [Common Lisp:
+A Gentle Introduction to Symbolic Computation][book-gentle].  This book really
+does strive to be gentle.  Even if you've programmed before I'd recommend
+starting here because it eases you into the language.  If you find it's moving
+too slow just skim forward a bit.
+
+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.
+
+Go through the book and *do all the exercises*.  This will take a while.
+
+This is mainly meant to get you started overcoming some of the main obstacles to
+being comfortable in Common Lisp, like:
+
+* 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?
+
+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.
+
+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
+`#common-lisp` channel there and we'll be happy to help you.
+
+[book-gentle]: https://www.cs.cmu.edu/~dst/LispBook/
+[SBCL]: http://www.sbcl.org/on
+[CCL]: https://ccl.clozure.com/
+
+### Getting Practical
+
+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.
+
+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.
+
+Unfortunately the book doesn't include exercises.  If you *really* 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.
+
+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.
+
+You should also begin to get comfortable looking up things in [the Common Lisp
+language specification][clhs] itself.  It's the ultimate manual for Common Lisp.
+It can be pretty dense at points, but can answer many questions you might have.
+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.
+
+(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.)
+
+[book-pcl]: http://www.gigamonkeys.com/book/
+[clhs]: http://www.lispworks.com/documentation/lw70/CLHS/Front/Contents.htm
+
+### Make Something
+
+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.
+
+If you need some ideas:
+
+* Do some [Project Euler](https://projecteuler.net/) problems.
+* Do some [Advent of Code](https://adventofcode.com/) exercises.
+* Make a [stupid Twitter bot](https://twitter.com/git_commands).
+* Make a personal calendar program that records your appointments, checks the
+  weather forecast the day of, etc.
+
+It doesn't really matter what you make, just make *something* on your own.
+
+### Lisp as a System
+
+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 move on.
+
+In most languages the development process looks something like this:
+
+1. Edit some code in the project with an editor.
+2. Compile the project (some languages skip this step).
+3. Run the project (or the tests).
+4. Observe the output (in the console, a browser, etc).
+5. Go to 1.
+
+This is not how most Common Lisp users interact with the language.  In Common
+Lisp, the development cycle looks more like this:
+
+1. Start a Lisp process.
+2. Load the project.
+3. Edit some code with your editor.
+4. Tell the running process to compile *only the code you edited*.
+5. Interact with the changed code in the process via the REPL, an HTTP request, etc.
+6. Observe the output (either in the console, a browser, etc).
+7. Go to 3.
+
+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 just that function, 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.
+
+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.
+
+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.
+
+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.
+
+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
+*least* a second or two (or, sometimes *minutes* 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 run the project or tests once I know they have
+a chance of working.
+
+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.
+
+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 *interact* with it immediately
+like I can in Common Lisp.
+
+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 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 [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
+quest, you hit the damage bug!  In tradition languages, one of two things might
+happen:
+
+1. The game crashes, and you get a stack trace and maybe a core dump.
+2. You've wrapped a `try` block around the main game loop that logs a stack
+   trace and ignores errors and allows the game to continue.
+
+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.
+
+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 do that).
+
+In Common Lisp you can certainly choose to panic or ignore on 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
+communicate with the running process at the REPL to see what's going on.  You
+can examine variables in the stack.
+
+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 call the 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.
+
+Maybe you don't make video games.  But this process can be useful in call kinds
+of contexts.  Maybe you're writing a web app that talks to an API somewhere, and
+are debugging a requests that fails somewhere between two calls to the API,
+maybe "create widget `foo`" and "add `foo` to widget list `bar`".  Instead of
+just aborting the request, logging a stack trace, and now leaving things in
+a possibly weird state (`foo` having been created without being in the expected `bar`
+list), you can fix the problem and allow the request to finish properly.
+
+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 ([one
+function to a function][1ftaf]) this case is pretty rare.  And even when it does
+happen, it just means you're back in the same situation you're in *by default*
+with other languages!
+
+[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
+
+### Learning Paradigms
+
+### Recipes for Success
+
+## Where to Go From Here
+
+### Macros
+### Object-Oriented Programming with CLOS
+### Low-Level Programming
+### Web Development
+### Game Development
+### Window Management
+### Unit Testing
+
+## Modern Common Lisp
+
+### Structure and Building
+
+#### Packages
+#### Systems
+#### Projects
+#### Naming Conventions
+
+### Common/Important Libraries
+
+[Alexandria][alexandria]
+[Drakma][]
+[Bordeaux Threads][bt]
+[Flexi Streams][flexi]
+[Gray Streams][gray]
+[Iterate][iterate] 
+[local-time][]
+[lparallel][]
+[named-readtables][]
+[CL-PPCRE][ppcre]
+[Roswell][]
+[SERIES][series]
+Trivial-whatever
+[uiop][]
+[usocket][]
+
+[alexandria]: https://common-lisp.net/project/alexandria/
+[iterate]: https://common-lisp.net/project/iterate/
+[series]: https://www.cliki.net/Series
+[bt]: https://common-lisp.net/project/bordeaux-threads/
+[gray]: https://www.cliki.net/Gray%20streams
+[flexi]: https://edicl.github.io/flexi-streams/
+[ppcre]: https://edicl.github.io/cl-ppcre/
+[uiop]: https://www.cliki.net/UIOP
+[usocket]: https://common-lisp.net/project/usocket/
+[drakma]: https://edicl.github.io/drakma/
+[lparallel]: https://lparallel.org/
+[local-time]: https://common-lisp.net/project/local-time/
+[named-readtables]: https://github.com/melisgl/named-readtables
+[Roswell]: https://github.com/roswell/roswell
+
+
Binary file static/media/images/blog/2018/07/lisp-kludge.jpeg has changed
--- a/static/media/js/print.js	Sat Jul 21 17:42:25 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-$(function() {
-    $('#leaf-content p:has(a)').add('#leaf-content ol:has(a)').add('#leaf-content ul:has(a)').each(function() {
-        var printing_links = $(this).find('a')
-                                    .not("[href^='#']")
-                                    .not(":has(img)")
-                                    .clone();
-        $(this).after(printing_links);
-
-        printing_links.wrap('<li></li>')
-                      .parent()
-                      .wrapAll('<ul class="print-links"></ul>');
-
-        printing_links.each(function() {
-            var href = $(this).attr('href');
-            if (href.match("^/")) {
-                href = 'http://stevelosh.com' + href;
-            }
-            $(this).after(': ' + href);
-        });
-    });
-});
--- a/static/media/js/sjl.js	Sat Jul 21 17:42:25 2018 +0000
+++ b/static/media/js/sjl.js	Wed Jul 25 22:32:23 2018 +0000
@@ -1,61 +1,20 @@
-var h2s = null;
-
-function place_scrolly_header () {
-    var soff = 75;
-
-    var y = $(window).scrollTop();
-
-    var target_content = null;
-    var opacity = null;
-    var header_y = null;
-    h2s.each(function() {
-        var pre_header_y = $(this).position()['top'] - soff;
-        if (y < pre_header_y) {
-            return false;
-        };
-        header_y = pre_header_y;
-
-        target_content = $(this).html().replace(/&nbsp;/g, ' ');
-
-        var opacity_y = y - (header_y + soff);
-        opacity = opacity_y / soff;
-        if (opacity > 1.0) {
-            opacity = 1.0;
-        }
-
-        if (opacity > 0.99) {
-            var next_headers = $(this).nextAll('h2');
-            if (next_headers.length) {
-                var next_header_y = next_headers.first().position()['top'];
-                var next_header_distance = next_header_y - y;
-                if (next_header_distance <= soff*2) {
-                    opacity = 1.0 / (soff - next_header_distance/2);
-                };
-            }
-        }
-    });
-    $('#scrolling-header').css({ opacity: opacity })
-                          .css('left', h2s.first().position()['left'] - 180 - 35)
-                          .html(target_content);
+function ready(fn) {
+  if (document.attachEvent ? document.readyState === "complete" : document.readyState !== "loading"){
+    fn();
+  } else {
+    document.addEventListener('DOMContentLoaded', fn);
+  }
 }
 
-$(function() {
-    if ($('#leaf-stats').length) {
-        $('body').append('<div id="scrolling-header"></div>');
-        h2s = $('#leaf-content h2');
+ready(function() {
+    hiddenToc = document.querySelectorAll('#leaf-toc');
+    actualToc = document.querySelectorAll('#toc');
 
-        $(window).scroll(function () {
-            place_scrolly_header();
-        });
-        $(window).resize(function () {
-            place_scrolly_header();
-        });
-    }
-    if ($('#leaf-toc').length) {
+    if (hiddenToc.length && actualToc.length) {
         // hugo's toc support is fucked so we need to move shit around by hand
         // computers are garbage
-        if ($('#toc').length) {
-            $('#toc').html($('#leaf-toc').html());
-        }
+        actualToc[0].innerHTML = hiddenToc[0].innerHTML;
     }
 });
+
+
--- a/themes/stevelosh/layouts/partials/header.html	Sat Jul 21 17:42:25 2018 +0000
+++ b/themes/stevelosh/layouts/partials/header.html	Wed Jul 25 22:32:23 2018 +0000
@@ -26,11 +26,10 @@
         <link rel="stylesheet" href="/media/css/print.css" type="text/css" media="print" charset="utf-8" />
         <link rel="stylesheet" href="/media/css/pygments-clean.css" type="text/css" media="screen" charset="utf-8" />
 
-        <script defer src="/media/js/jquery.js" type="text/javascript"></script>
         <script defer src="/media/js/sjl.js" type="text/javascript"></script>
-        <script defer src="/media/js/print.js" type="text/javascript"></script>
 
         {{ if .Params.lightbox }}
+            <script defer src="/media/js/jquery.js" type="text/javascript"></script>
             <script defer src="/media/js/featherlight.min.js" type="text/javascript" charset="utf-8"></script>
             <script defer src="/media/js/featherlight.gallery.min.js" type="text/javascript" charset="utf-8"></script>
             <link href="/media/css/featherlight.min.css" type="text/css" rel="stylesheet" />
@@ -38,13 +37,11 @@
         {{ end }}
 
         {{ if .Params.mathjax }}
-        <script type="text/javascript" async
-            src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
-        </script>
+            <script defer src="/media/js/jquery.js" type="text/javascript"></script>
+            <script type="text/javascript" async
+                src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
+            </script>
         {{ end }}
-
-        <link rel="openid.server" href="http://www.myopenid.com/server" />
-        <link rel="openid.delegate" href="http://stevelosh.myopenid.com/" />
     </head>
 
     <body>