# HG changeset patch # User Steve Losh # Date 1534308636 0 # Node ID 61af4332b8026f18385cb1a811a5e053208256a0 # Parent bf23f95dcb71161a2d2a6c061dd7458b16692135 More todos diff -r bf23f95dcb71 -r 61af4332b802 content/blog/2018/08/a-road-to-common-lisp.markdown --- a/content/blog/2018/08/a-road-to-common-lisp.markdown Mon Aug 13 06:36:09 2018 +0000 +++ b/content/blog/2018/08/a-road-to-common-lisp.markdown Wed Aug 15 04:50:36 2018 +0000 @@ -1,7 +1,7 @@ +++ title = "A Road to Common Lisp" snip = "One way to learn this old language." -date = 2018-08-01T16:00:00Z +date = 2018-08-15T16:00:00Z draft = false +++ @@ -492,8 +492,6 @@ ### Lisp as a System -TODO gabriel paper - 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. @@ -570,7 +568,7 @@ breathing programming [*system*][gabriel-system] goes beyond just the short feedback loop and interactive REPL, too. -[gabriel-system]: +[gabriel-system]: https://www.dreamsongs.com/Files/Incommensurability.pdf 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 @@ -609,11 +607,13 @@ 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][mickens TODO] on +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! +[blood stains]: https://www.usenix.org/system/files/1311_05-08_mickens.pdf + 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 are debugging a request that fails somewhere between two calls to the API, e.g. @@ -636,8 +636,13 @@ 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]. +* Emacs with [SLIME][] or [Sly][]. +* Vim (or Neovim) with [Vlime][] or [Slimv][]. + +[SLIME]: https://common-lisp.net/project/slime/ +[Sly]: https://github.com/joaotavora/sly +[Vlime]: https://github.com/l04m33/vlime +[Slimv]: https://github.com/kovisoft/slimv 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 @@ -654,17 +659,19 @@ 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] +On a side note: if anyone is interested in making a Common Lisp [LSP][] 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 +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. +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. + +[LSP]: https://langserver.org/ ### Learning Paradigms @@ -674,15 +681,17 @@ practice using your fancy new environment. I think the perfect book for both of these is [Paradigms of Artificial -Intelligence Programming][TODO], often abbreviated as PAIP. The book was +Intelligence Programming][PAIP], often abbreviated as PAIP. The book was recently made available for free as a PDF, or you can buy a paper copy from if you prefer. -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 in this kind of AI, the book is a great example of how -to write Common Lisp code. +[PAIP]: https://github.com/norvig/paip-lisp + +This book was written in 1992 so it's not about the hyped up AI fields you've +been hearing about in the news like machine learning or deep learning — instead +it's a tour of [Good Old-Fashioned AI][gofai]. Even if you're not particularly +interested in this kind of AI, the book is a great example of how to write +Common Lisp code. 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 @@ -705,14 +714,14 @@ — really digging into a problem is exactly what you need at this point in your Lisp journey. -[gofai]: TODO +[gofai]: https://en.wikipedia.org/wiki/Symbolic_artificial_intelligence ### Recipes for Success The final technical book I'll recommend to every aspiring Lisp programmer is -[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 +[Common Lisp Recipes][recipes], sometimes abbreviated as CLR. Unlike most of +the other books I've recommended so far this one is relatively recent: it was +published in 2015. It's not free, but I think it's well worth the money it costs. The book is written by the author of several very heavily used Common Lisp @@ -721,6 +730,8 @@ well-written grab bag that will teach you a lot of things you won't find in other books. +[recipes]: http://weitz.de/cl-recipes/ + ### Final Patterns If you've gotten this far you're pretty invested in Common Lisp, and I want to @@ -896,9 +907,9 @@ #### Packages -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*. +We often see questions in IRC and Discord that look something like: "How do I +export a class from a package"? Questions worded like this are a sign of a +very common misunderstanding about what packages in Common Lisp *actually are*. **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 @@ -926,20 +937,24 @@ package, or one file that switches between many packages, or even create or modify packages at runtime. -This gives you maximum flexibility to work however you want. For example: in my -[Prolog VM][temperance] most of the packages are each defined in their own file, -much like you would do in modern languages. But the `temperance.compiler` TODO -package is pretty large (the compiler is the most complicated part of the code) -and so I split it into [a series of separate files][temperance-compiler], each -one dealing with a single pass of the compiler, which all work in the same -package. +This gives you the flexibility to work however you want. For example: in my +procedural art library [Flax][] most of the packages are each used in one +specific file, much like you would do in modern languages. But the +`flax.drawing` package contains not only a drawing protocol but also several +implementations of it (PNG, SVG, etc), and so I split the code into [a series +of separate files][flax-drawing], each one dealing with how to draw a single +format (plus one for the protocol itself). + +I could have created separate packages for each implementation and set up the +imports/exports between them, but I didn't feel like the extra boilerplate was +worth it. Common Lisp is flexible enough to let you make such choices. So if files and packages aren't related, the next question is: how does Common Lisp know where to *find* anything on disk when it comes time to load the code? -[symbols]: TODO -[temperance]: TODO -[temperance-compiler]: TODO +[symbols]: http://stevelosh.com/blog/2016/06/symbolic-computation/ +[Flax]: https://github.com/sjl/flax +[flax-drawing]: https://github.com/sjl/flax/tree/master/src/drawing #### Systems @@ -952,15 +967,19 @@ * Some metadata like author, license, version, homepage, etc. 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. And since Common Lisp -gives you the power to abstract almost anything, people eventually abstracted -the process of loading Common Lisp code. +[section 11.9][cltl2-packages] of CLtL2 you'll see that it was imagined that +each author would write their own custom file to load their code. But since +Common Lisp gives you the power to abstract almost anything, people eventually +abstracted the process of loading Common Lisp code. + +[cltl2-packages]: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node120.html -[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][] is a Common Lisp library bundled with most modern implementations +which handles defining and loading systems. The name ASDF stands for "Another +System Definition Facility", so as you might guess there have been several +other such libraries. ASDF is the one everyone uses today. + +[ASDF]: https://common-lisp.net/project/asdf/ ASDF standardizes the process of defining a system into something like this: @@ -971,10 +990,12 @@ file is `asd`, not `asdf`, which is a little confusing, but was probably chosen to work in environments with three-letter-extension limits. -The [ASDF manual][TODO] is the definitive resource for the syntax and semantics -of `defproject`, but can be a little heavy to read if you're just getting -started. Another way to get started is to read some `.asd` files of some -small-to-medium sized open source projects and see how they handle things. +The [ASDF manual][] is the definitive resource for the syntax and semantics of +`defproject`, but can be a little heavy to read if you're just getting started. +Another way to get started is to read some `.asd` files of some small-to-medium +sized open source projects and see how they handle things. + +[ASDF manual]: https://common-lisp.net/project/asdf/#documentation Systems and packages are orthogonal in Common Lisp. Some systems (like small libraries) will define exactly one package. Some systems will define multiple @@ -984,8 +1005,10 @@ For example: * 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. -* One of the files specified for loading is [`packages.lisp`][cl-digraph-packages], which creates a package called `digraph`. +* That system has a description of how to load the code, which lives in the + [`cl-digraph.asd`][cl-digraph-asd] file. +* One of the files specified for loading is [`package.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 @@ -997,7 +1020,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 `packages.lisp` file which contains all the definitions for all the +* A single `package.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 @@ -1009,9 +1032,9 @@ it, a list of its dependencies, and some metadata. Now let's move up one level higher to the final layer of structure you need to know about. -[cl-digraph]: TODO -[cl-digraph-asd]: TODO -[cl-digraph-packages]: TODO +[cl-digraph]: https://github.com/sjl/cl-digraph +[cl-digraph-asd]: https://github.com/sjl/cl-digraph/blob/master/cl-digraph.asd +[cl-digraph-packages]: https://github.com/sjl/cl-digraph/blob/master/package.lisp #### Projects @@ -1019,30 +1042,30 @@ of, but is a word that's generally used to mean something like a library, a framework, an application, etc. -A project will usually define at least one system, because systems are where you -describe how to load the code, and if a project didn't define a system how would -you know how to load its code? My cl-digraph library mentioned above is -a project that defines *three* systems: +A project will usually define at least one system, because systems are where +you describe how to load the code, and if a project didn't define a system how +would you know how to load its code? My string-wrapping library [Bobbin][] is +a project that defines *two* systems: -The `cl-digraph` system contains the actual data structure and API. It has no +[Bobbin]: https://github.com/sjl/bobbin + +The `bobbin` system contains the actual data structure and API. It has no dependencies. -The `cl-digraph.test` system contains the unit tests. It depends on the -`cl-digraph` system (because that's the code it's going to test) and the `1am` +The `bobbin/test` system contains the unit tests. It depends on the +`bobbin` system (because that's the code it's going to test) and the `1am` system (a unit test framework). I made this a separate system because it allows users to load the main code without also having to load the unit testing framework if they're not going to be running the tests. -The `cl-digraph.dot` system contains code for drawing the directed graphs to -image files with [Graphviz][TODO]. It depends on the `cl-digraph` system and -the `cl-dot` system (the Graphviz bindings). I made this a separate system -because it allows users load the main code without also having to load the -Graphviz bindings if they don't care about drawing. +[Graphviz]: https://www.graphviz.org/ -If I were writing this project today I'd use a forward slash in the system names -instead of a period (e.g. `cl-digraph/test`), because ASDF has some nice [extra -support][TODO] for that. I just didn't know about it at the time, and don't -want to break backwards compatibility now. +Both of these systems are defined in the [`bobbin.asd` file][bobbin-asd]. ASDF +[treats systems with a forward slash in their name specially][asdf-slash] and +knows to look for them in the `asd` file named with the text before the slash. + +[bobbin-asd]: https://github.com/sjl/bobbin/blob/master/bobbin.asd +[asdf-slash]: https://common-lisp.net/project/asdf/asdf.html#index-find_002dsystem We saw how Common Lisp has no concept of a system — that concept comes from ASDF. Similarly, ASDF has no concept of the internet or of reaching out to @@ -1051,14 +1074,17 @@ a check to an address and receiving a copy of the code on floppy disk, as many of my old Lisp books offer in their final pages. -Quicklisp is another library that works on top of ASDF to provide the "download -projects from the internet automatically if necessary" functionality that people -expect in the modern world. So when you say `(ql:quickload :cl-digraph)` you’re -asking Quicklisp to download cl-digraph (and any dependencies) if necessary, and -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, which is why you need to install it separately. +[Quicklisp][] is another library that works on top of ASDF to provide the +"download projects from the internet automatically if necessary" functionality +that people expect in the modern world. So when you say `(ql:quickload +:bobbin)` you’re asking Quicklisp to download Bobbin (and any dependencies) if +necessary, and then hand it off to ASDF to actually load the code of the +`bobbin` 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, which is why you need to install it +separately. + +[Quicklisp]: https://www.quicklisp.org/beta/ #### Recap