# HG changeset patch # User Steve Losh # Date 1532557943 0 # Node ID ceb68ee2312ab42c42a1edbec3739980ea14604a # Parent 2fc3e28a12252a6b917166f462b256c8599019a8 Start new post, continue work on expunging JS diff -r 2fc3e28a1225 -r ceb68ee2312a content/blog/2016/02/midpoint-displacement.markdown --- 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 @@ +++ + diff -r 2fc3e28a1225 -r ceb68ee2312a content/blog/2016/03/recursive-midpoint-displacement.markdown --- 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 @@ +++ + diff -r 2fc3e28a1225 -r ceb68ee2312a content/blog/2016/06/diamond-square.markdown --- 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 @@ +++ + diff -r 2fc3e28a1225 -r ceb68ee2312a content/blog/2018/07/a-road-to-common-lisp.markdown --- /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. + +
+ +## 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 + + diff -r 2fc3e28a1225 -r ceb68ee2312a static/media/images/blog/2018/07/lisp-kludge.jpeg Binary file static/media/images/blog/2018/07/lisp-kludge.jpeg has changed diff -r 2fc3e28a1225 -r ceb68ee2312a static/media/js/print.js --- 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('
  • ') - .parent() - .wrapAll(''); - - printing_links.each(function() { - var href = $(this).attr('href'); - if (href.match("^/")) { - href = 'http://stevelosh.com' + href; - } - $(this).after(': ' + href); - }); - }); -}); diff -r 2fc3e28a1225 -r ceb68ee2312a static/media/js/sjl.js --- 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(/ /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('
    '); - 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; } }); + + diff -r 2fc3e28a1225 -r ceb68ee2312a themes/stevelosh/layouts/partials/header.html --- 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 @@ - - {{ if .Params.lightbox }} + @@ -38,13 +37,11 @@ {{ end }} {{ if .Params.mathjax }} - + + {{ end }} - - -