Ask HN: I want to start learning Lisp. Where do I begin?
There are multiple versions of Lisp. Multiple compilers. What is a good one for someone new to pick?
I use VS Code and Vim. Are they suitable for learning Lisp?
I use VS Code and Vim. Are they suitable for learning Lisp?
210 comments
[ 2.4 ms ] story [ 213 ms ] threadFor Common Lisp, SBCL [1] is probably the best free option. If you want to learn Scheme instead, Chicken Scheme [2] and Racket [3] are popular and good choices.
[1]: http://www.sbcl.org/
[2]: https://call-cc.org/
[3]: https://racket-lang.org/
Once you have the basics down then look into setting up slime and jumping down the emacs rabbit hole.
With that, you're ready to go. Take just one book and work through it, you don't need the best book, but a book will be better structured and more complete than multiple blog posts from different people. Practical Common Lisp (http://www.gigamonkeys.com/book/) is good and freely available. I used ANSI Common Lisp (http://www.paulgraham.com/acl.html), from Paul Graham (https://news.ycombinator.com/user?id=pg), long time ago and it's also good.
If you are open to non-common-lisp lisps, I'd say pick up clojure instead of common lisp, it's more modern and thus you will find more community and up-to-date resources. Clojure for the Brave and True is a book that has been praised, but I did not read it. It has some jokes and humorous examples that might or might not suit you.
https://marketplace.visualstudio.com/items?itemName=rheller....
I meant any lisp. Common lisp, clojure, ccl. I am new to this so did not have a choice. But in this thread I get the feeling that common lisp is the most suggested.
Make sure your editor has parinfer available. Not parindent, parinfer. People swear by parindent, but you don't need it on day one. Just try it later on. Parinfer is your friend.
https://stevelosh.com/blog/2018/08/a-road-to-common-lisp/
That's the best article I know on the matter.
Common Lisp is multi-paradigm language that is pretty bendy with regard to absorbing new ways of doing programming. It gets actual work done as well.
In Clojure, how do you usually approach problems that are shaped in an object-oriented way? In object-oriented languages, this usually includes either some sort of method calls (and therefore mutable state) or message passing (which usually involves actors, like Erlang processes). Passing all state around as function arguments is one way, but it becomes somewhat tedious as state grows large.
But obviously all this is not very idiomatic; in clojure you would keep those functions first-class through defn instead of tying them to the object / map, and would pass state as an argument. Something like:
If obj has structure (e.g. it is a map such as {:type :my.personal/type :val 12}) you can identify its type through the type keyval and can check conformance to a spec, etc.As it was said in the previous post, it's equivalent. It's a matter of how to organize code.
Thanks, TIL! That's equivalent to the "mostly functional" style that's doable in Common Lisp.
(and also obviously functions that map that state to HTML/CSS/SVG/etc.).
Clojure does have constructs to manage state. You don't need to shove it all into your function arguments.
Then you would need to have ways to mutate variables, so you'd need to stop using Clojure's persistent collections and instead use the ones provided by the JVM.
Overall, you can do it if you want to, it is just somewhat painful to do so and the language will fight you. This is like my who tried to write functional Python which is possible but then it is just a much worse Clojure.
(nota bene: I understand these things are hard to solve and I don't mean to throw the stone without putting my own time on the line to fix it, but that's one reason I'm not using VS code for clojure. Doom emacs with cider is still better, even if not perfect). If people know of someone I could throw money at to solve the integration for VS code, I'd be happy to do that.
If you find a good/great experience for clojure with vim keybindings, I'm all ears.
[1] https://calva.io/ [2] https://github.com/borkdude/clj-kondo/blob/master/doc/editor...
Another issue has to do with startup time. The Clojure application bootstrap process is relatively slow, i.e. start-up might take 1 second, so it's great for server applications, but less great for Android apps or other CLI scripts that expect require instant startup. It has been approached in different ways, e.g. compiling Clojure with Graalvm native image or using ClojureScript instead of JVM Clojure. The latest solution is Babashka which provides a variant of Clojure specifically for writing CLI scripts.
Otherwise, Clojure is an excellent language which both modernises Lisp syntax significantly and implements a very well thought out standard library for doing functional programming. There are many great features, but the standout ones are probably the persistent data structures (which syntactically act both as data structures and functions) and parallelism/concurrency support. It's also very natural to do interop with the host platforms (Java, JS, .NET) and the data-oriented style of programming makes communication between backend (Clojure) and frontend (ClojureScript) extremely simple. So it's pretty much the perfect full-stack language for information systems. I would take a look at the rationale: https://clojure.org/about/rationale
Absolutely everything mentioned above in this quote is available in Common Lisp as well by just loading the needed libraries.
>Another issue has to do with startup time. The Clojure application bootstrap process is relatively slow, i.e. start-up might take 1 second,
>The main issue with Clojure is probably its tight integration with the various host platforms. When you get a stack trace in Clojure/ClojureScript, you basically get a Java or JavaScript stack trace, so there is some expectation of familiarity with the host platform.
Correct. And there are no such problems in Common Lisp. Except if you want to execute CL in a JVM, in which case the available implementation, ABCL, does take a slow time to start. Otherwise it's a great implementation.
https://insideclojure.org/2018/05/04/add-lib/
Some of Clojure's other runtimes perform a bit better, Babashka, CLJS, CLR, GraalVM for different trade offs if you need something like a scripting language or native images etc
I'm sure CL is still quicker but there are options
I use Common Lisp and my main argument will be that Clojure isn't an "interactive programming" language like Common Lisp, Smalltalk (and Pharo, Squeak, Scratch) are. And, for me, this is removing one of the main, core advantages of Lisp.
Dispensing with the interactive programming features is, IMO, a step backwards in the state-of-the-art. Common Lisp ADDED all the improvements in the state of the art: Interactive programming from Smalltalk, lexical scoping from Scheme, various high performance/low level features from StarLisp and ZetaLisp, a very powerful OOP system, etc. And then, thanks to it being highly extensive, almost any feature can be added to it.
Clojure features like threading macros, immutable seqences, software transactional memory, and others, are already available in Common Lisp by just importing (loading) the respective library.
One of Clojure's main advantage is to be able to call Java libraries. But, surprise, you can do this in Common Lisp too, easily, by using the Armed Bear Common Lisp (ABCL) implementation. Which runs on the JVM too and makes the process of calling Java libs really, really easy. I have made a working example here, calling all the Swing UI library (java) from lisp:
https://github.com/defunkydrummer/abcl-jazz
It's true that Clojure has more widespread adoption in the industry and more libraries. However the library ecosystem on Common Lisp is decent.
>and you can get actual work done in it which is an advantage over some of the other options
This implies you can't do "actual work" in, for example, Common lisp. Which is not true, since there are companies that, in this very moment, are doing well paid, critical commercial work using Common Lisp.
But Common Lisp features like the condition system are already available in Clojure by just requiring (loading) the respective library. :)
As an example, see https://github.com/clojureman/special . Nice thing about this repo is that it points to other options re. conditions with pros and cons of each one.
Now that I think of it, you might also be referring to the fact that Common Lisp can load libraries through the REPL. Clojure can also do this through an alpha feature, add-lib, that you can already require in any project.
- Able to easily modify (and recompile) a function while your code is running
- Able to redefine a class and then change existing instances so they use the new class definition
- Able to sabe the complete running state of the system (the "image" of the system, including state of all variables, data, loaded libraries, compiled functions, etc) into a file so it can be restarted later, just like Smalltalk does.
- Able to inspect any stack frame at will and to restart execution from any chosen stack frame
These are just a few of the feature that Common Lisp has and that are part of what an "interactive language" is. Common Lisp brings all these features, they work seamlessly, without any sweat, working reliably and efficiently.
I do this all the time with Clojure at work. I will have my application running (web app) with two repls in emacs. One is connected to the ClojureScript repl and one to the Clojure repl. I am able to make changes to both front end code and back end code on the fly by changing a function and then evaluating the function into the repl. I can start the application when I get in to work in the morning and have it going all day a while I'm doing my work.
> Able to sabe the complete running state of the system (the "image" of the system, including state of all variables, data, loaded libraries, compiled functions, etc) into a file so it can be restarted later, just like Smalltalk does.
> - Able to inspect any stack frame at will and to restart execution from any chosen stack frame
Those are really cool features, are they in the base language or are they libraries that you would need to include?
If I may ask, are you using Common Lisp professionally? If so, what kinds of applications do you use it for? I've had this notion, as many others seem to have in this thread, that you don't really do anything professional with CL but I realize it is just ignorance on my part.
But since you can't arbitrarily inspect and restart any start frame, you don't really get the interactive programming experience.
On CL, for example, when you hit a runtime bug that is uncaught, you get the debugger window which shows not just the "stack trace" but the complete stack FRAMES that you can inspect. So let's say deep down you find where the error originated and the states of the variables.
You can then jump to the definition of the offending function, edit the function, recompile it, (optionally) change any variable on that stackframe, and then continue the execution by restarting specifically at that stack frame.
In this way, the feature of "modify a function while the code is running" becomes way more meaningful. The program evolves as it runs, as if it were a lifeform.
Edit: I meant ANSI Common Lisp. Thanks everyone!
(I wouldn't even recommend On Lisp as a second text, too much macrology).
The documentation for Racket is excellent, see e.g.
https://docs.racket-lang.org/quick/index.html
... and many people have created online resources to adapt SICP to Racket, as well as other learning texts. I like Beautiful Racket, e.g.
https://beautifulracket.com/explainer/lists.html
You can use #lang sicp and start playing around with the free online reformatted SICP text here:
http://sarabander.github.io/sicp/
I recommend getting a print copy of SICP, though, and working through the examples in a real DrRacket environment on your computer.
IMO, if you end up going deep in the "lispy" direction after playing with Racket, you'll probably be drawn to Clojure as it is the Lisp with the biggest "production use" community at the moment. So long as you can put up with some JVM warts, it, too, will be a good experience.
[0]:https://people.eecs.berkeley.edu/~bh/ssch1/showing.html
Clojure is Lisp!!! with some nice out of the box semantics like immutability.
(There's also trampoline for mutual recursion cases)
Even a little thing like (recur ...) could be a tiny amount of friction. But good to read, that the usual cases are apparently handled well in the language.
The trampoline pattern is an old trick that can also be imlemented in other languages to avoid stack consumption in mutually recursive calls.
That's unfortunate, that it is an additional function call, which needs to be explicitly written out. I'd guess such is necessary ultimately, because of limitations of the JVM and its limitations regarding recursion.
it comes with its own editor (Dr. Racket) that does some really helpful things for beginners, like hovering over a variable and seeing lines drawn to where it's being used.
It's not the fastest lisp, but that's ok. It's spectacular for learning and has a huge ecosystem too. My "daily driver" is Chicken Scheme, but i wouldn't recommend it to a beginner. The docs just aren't helpful to newbs (and frequently frustrating to me even though it has one of the better set of docs)
Vim is meh for Lisp & Scheme, and I'm a Vim fan. Emacs is great. If you're a Vim user check out Doom Emacs. It's great for us geeks who love vim but want more power and better lisp support. But... start with Dr. Racket for now.
Others have mentioned the SICP book, and it is good but i wouldn't suggest it as as "how to start with lisp" book. Also, watch the free lectures of the course from MIT. Very good, and make it way easier to work through the book.
https://github.com/Olical/conjure
It's written in a lisp, runs as lua and supports: Clojure, Fennel, Racket and Janet with a bunch more to come.
There is always spacemacs: https://www.spacemacs.org/
ClojureScript is great if you're drawn towards the "production use" side as pixelmonkey said and already have knowledge of the Javascript / Node ecosystem.
[1] https://mitpress.mit.edu/sites/default/files/sicp/index.html
[2] https://htdp.org
VSCode will be fine for editing Lisp, there's a good syntax highlighter extension too.
Like there are many Lisps, there are many Vims. Spacemacs is a Vim for Emacs or inside Emacs: https://www.spacemacs.org/
It's quite powerful. And it can be argued that Emacs is highly extensible - because it's written in a Lisp. Looking under the hood and hacking on the editor is a lot of fun and very informative. And also horrible and bad. But in a good way!
And almost completely unrelated: I very much enjoy the Structure and Interpretation of Computer Programs book and online lectures: https://ocw.mit.edu/courses/electrical-engineering-and-compu...
The video lectures were recorded in 1986!: https://ocw.mit.edu/courses/electrical-engineering-and-compu...
And they're still amazing.
(But people all think very differently and what works for me may not be your cup of tea!)
Emacs seems the best choice for Lisp, since it is scripted in Lisp and it's best adapted to it probably. However when I started, I was already so used to vim that I kept going with it. Maybe not the best option, but it's ok, maybe just a little bit more effort to automatize some stuff.
If you choose Common Lisp, this is probably the best book for start: http://www.gigamonkeys.com/book/ . Anyway be prepared, Lisp is not easy to grasp at first, but as soon you do, you will probably love it and appreciate way it is. :)
Note that getting started with Emacs, Lisp, SBCL, Quicklisp and Git is possible in 3 clics thanks to Portacle (http://portacle.github.io/)
just to point out i believe 'functional' here means 'has lots of functionality' not 'more suited to functional programming' - in that regard scheme (and racket) are more 'functional' than CL
If you would like to learn on line (maybe using pdf book) you can try my Scheme REPL bookmarklet https://lips.js.org/#bookmark so you don't need to install anything on your computer, you can do that later.
https://ia800700.us.archive.org/33/items/sketchy-lisp/sketch...
- Choose Common Lisp because it has been the most popular dialect of Lisp in the overall history of Lisp. It is more convenient than Scheme if one decides to develop serious software in Lisp. Clojure appears to be more popular in the technology industry than Common Lisp among organizations (my workplace included) that use Lisp. I still recommend Common Lisp because I believe that it is more likely that one would work on an independent open source or hobby Lisp project than one would encounter one of the rare organizations who use Clojure at work.
- Choose SBCL (Steel Bank Common Lisp) as the implementation.[1][2] It is the most popular[3] Common Lisp implementation and is recommended in many online discussions. CCL (Clozure CL, not to be confused with Clojure which is a separate dialect) is also another very good implementation.
- Work through this book: Practical Common Lisp: http://www.gigamonkeys.com/book/ (available in print too if you search online). Just use a modern alternative to Lisp in a box when you reach Chapter 2. In fact, I encourage using vanilla Emacs and setting it up with SLIME and Paredit yourself. See Emacs4CL for example.[4]
- A Vim user may consider installing Slimv or Vlime[5]. Slimv and Vlime offer an environment similar to Emacs/SLIME. They display the REPL in a Vim buffer. Slimv also comes with Paredit mode that makes typing and executing S-expressions quite convenient.
- In case you want to postpone setting up Emacs + SLIME or Vim + Slimv/Vlime until a time you have become more familiar with the language, you can also execute your Lisp source code files from the shell.[6]
- Optionally, keep another implementation of Common Lisp. Common Lisp is a standard that is implemented by various implementations. Each implementation may have its own extensions or implementation-specific behaviour regarding error handling, command line options, FASL format, unspecified behaviour, etc. Experimenting with concepts with another implementation of Lisp occasionally may offer some perspective about how some things could be different in different implementations. I keep CLISP around for this purpose.[7][8]
[1]: Install SBCL on macOS: brew install sbcl
[2]: Install SBCL on Debian-based distro: apt-get install sbcl
[3]: State of Common Lisp Survey 2020: https://docs.google.com/forms/d/e/1FAIpQLSfg7UJRKrkI3OjOHWL4...
[4]: Emacs4CL: https://github.com/susam/emacs4cl
[5]: Lisp in Vim with Slimv or Vlime: https://susam.in/blog/lisp-in-vim-with-slimv-or-vlime/
[6]: Load (execute) code in a file and exit: sbcl --script foo.lisp
[7]: Install CLISP on macOS: brew install clisp
[8]: Install CLISP on Debian-based distro: apt-get install clisp
Disclosure: I am the author of [4] and [5].
That being said you might have no desire to leap at all - Clojure is a powerful language with a great ecosystem (especially if you rope in Java interop)
pgloader was rewritten from Python to Lisp: https://tapoueh.org/blog/2014/05/why-is-pgloader-so-much-fas... because of Python's poor performances and threading capacities.
A theorem prover used in the industry: https://github.com/acl2/acl2
A Lisp vendor selling semantic graph solutions: https://franz.com/ (see also its success stories for industry examples)
Here's a sadder story: a team switched from CL to Go for production, but still use CL for prototyping: https://lisp-journey.gitlab.io/blog/pro-mailing-list-on-comm...
Some use CL in bioinformatics (and claim it's the best tool for the job). For example, they create a new Lisp implementation: https://github.com/clasp-developers/clasp
More success stories: https://lisp-lang.org/success/
Does anyone know if there's a mirror somewhere? I'd gladly host this in a droplet and make it public if someone has the source.
[0]http://reed.cs.depaul.edu/peterh/Tools/lisptutor.html
For me, I decided to start with Common Lisp. I installed SBCL, Slime for Emacs, and started working through the book Practical Common Lisp. By and large this seems to be a workable approach, but I will offer up this caveat. The PCL book is very much "project based" in that the author walks you through building a couple of actual applications. This is a Good Thing™ for the most part, but it does mean that you don't necessarily get things in the order you might expect.
There's a part of me that almost wishes I'd started with a book that takes more of an approach of "programming is ultimately sequence, selection and iteration. Here's how you do sequence in Lisp. Here's how you do selection. Here's how you do iteration. Now here's all the Fancy Lisp Stuff™".
The reason I say that, is because if you have at least the primitives for sequence, selection, and iteration (and maybe some I/O) in your mental toolbox, you can start building more or less arbitrary programs. With the PCL book structure you don't get to, for example, iteration, until moderately deep into the book. You do get there of course, but the early parts left me with an uneasy feeling like "geez, I don't even know how to write a loop in this language yet, when am I ever going to be able to just start coding in Lisp on my own?" if that makes sense.
Possibly one could complement PCL with another book, or online materials, but so far I've mostly just been grinding through PCL and haven't looked at any other Lisp books.
HTH.
> The PCL book is very much "project based" in that the author walks you through building a couple of actual applications. This is a Good Thing™ for the most part, but it does mean that you don't necessarily get things in the order you might expect. > > There's a part of me that almost wishes I'd started with a book that takes more of an approach of "programming is ultimately sequence, selection and iteration. Here's how you do sequence in Lisp. Here's how you do selection. Here's how you do iteration. Now here's all the Fancy Lisp Stuff™".
I feel like I'm in the minority in that I prefer to NOT learn things by just "diving in". I am much more successful when I start from the absolute fundamentals. There are objective pros and cons to either approach, but I suspect the success factor is based on one's personality (or some such).
If you start with the fundamentals, it's hard to know if you have any mastery until you try to do something "real". And then you might get frustrated because nobody taught you, e.g., how to do a network call in this programming language. It's also less rewarding because you can spend hours/days/weeks before actually "doing" anything.
If you start by diving in to a project, it's hard to know if you're doing things "the right way" or if you're just porting over bad habits from some other language you're familiar with. Sure, I got it to work, but is it good, or did I just cement in some bad habits?
The first time I drove a standard transmission car, I didn't stall it a single time. Because I read on the internet for a week about how car transmissions work and the difference between a manual and automatic gearbox. I do the same thing with programming languages. The first thing I want to know is what is the memory model approximately like (everything is a reference, values + references, are allocations cheap or expensive), what is the "philosophy" of the language (everything is an object, function composition is the blessed approach, mutability is bad, types are most/least important), etc. Then I want basic mechanisms: loops, iterators, lists or arrays, threads/futures/coroutines. Etc, etc.
That being said, SICP is kind of nice in that it finds an interesting middle ground where you're definitely learning details of the scheme language (applicative order vs normal order, recursion, etc) while it also feels like the details of the language is not the entire point.
No real point here. Just inspired by your comment and thought I'd share my own experience.
A bit more off-topic, but this resonated with me. I too feel more comfortable when I start with the absolute fundamentals. When something does not work, knowing the fundamentals helps me to reason about why it does not work from the first principles. On similar lines, I did a relevant "Show HN" post here today:
Emacs4CL: https://github.com/susam/emacs4cl (35 lines of ~/.emacs to turn Emacs into Common Lisp dev environment)
It bothers me a little when someone new to Common Lisp or Emacs is encouraged to start off with Portacle or Spacemacs directly without appreciating the extensibility of Emacs and how SLIME fits in the environment. The Show HN post on Emacs4CL I have linked to above is an attempt to promote a more DIY approach to setting up one's development environment for Common Lisp without having to spend a lot of time.
Though, to be fair, if someone says they want to learn LISP and then you tell them to learn Emacs... well, now they have TWO problems! xD
I love and use Emacs. And I also, unsurprisingly, don't like or recommend that people start off with all of these "starter packs".
But at the same time, if you don't already know Emacs and you want to learn LISP, I don't know what I'd recommend.
Here is my attempt to make it easier to get started with vanilla Emacs + SLIME + SBCL without hiding the underlying details like a starter pack does: https://news.ycombinator.com/item?id=25440690
Also, if you are used to the Jetbrains tools like IntelliJ, try the Cursive IDE (paid).
The instructions in your link were fantastic and easy. Thanks a lot for this !!
Indeed the intention behind the .emacs and detailed documentation for it was to make it easy for beginners to set up a Common Lisp development environment with Emacs and SLIME quickly while understanding every step of the setup process. I am glad you like it!
I definitely find value in the project based approach, but I guess I'd say that I tend to want just a little bit more "here's the fundamentals" first, before transitioning into that mode. As a generalization. But Programming Common Lisp isn't bad for this reason, it's just a hair short of what I personally might consider "the perfect Lisp book" if I could imagine up such a thing.
With Lisp you can better think in terms of abstractions. In particular how do you do data abstraction, how do you do functional abstraction (that's the control flow: sequencing selection and iteration) and how do you do syntactic abstraction?
That last one is where Lisp (any Lisp) really comes into its own. It's where you go beyond the expressiveness of those other programming languages. Whether you go with defmacro or define-syntax, it opens up a whole other world.
And the expressiveness here isn't meant to suggest that you can write programs that you can't in other languages (they're all Turing complete) but that you can write new kinds of control flow and scoping constructs so you're not limited to the sequencing selection and iteration that the language provides.
That's all great... once you already know how to use the language. But, at least relative to the way I think and learn, I really want to quickly be able to do three or four things when learning a new language: sequence, selection, iteration, and console I/O. Once I can do that, I can write essentially any arbitrary program. And then I can start adding onto my knowledge and learning the more interesting ways of doing things.
Maybe I'd have a different perspective if I'd learned Lisp first, instead of having 20+ years of programming in Algol derived languages under my belt first. But I do feel uncomfortable spending days and weeks with a language and not even learning how do use (their version of) a for loop or whatever.
I found a very good complement was Edi Weitz' "Common Lisp Recipes" from same the publisher.
http://weitz.de/cl-recipes/
This is exactly the approach I took (~15 years ago) when I went all-in on Lisp for a few years. Don't know if it is still the best today, but it was a solid approach at the time.
The whole issue of "flavors" largely comes down to one concept: Do you like the idea of functions and variables being in the same namespace (that's called a Lisp 1 like Scheme) or different (a Lisp 2 like CL). If you don't care, it doesn't matter which you pick.
If you're going to extend your Lisp app into production, you might want some of the many libraries that are available for CL. These cover just about every known topic and algorithm in comp sci and they're old and proven and just work everywhere, so that can be a benefit as you move to a non-trial app.
If you're going to distribute something written in Lisp (like an executable), then the flavor and implementation you pick will need to work all the way down your distribution path, and that can be challenging to get setup. One proven path is SBCL + Node.js + Electron to wrap your app for all three platforms.
Hope you love Lisp and get great things out of it!
[0]http://nebula.mimix.io
[1]http://home.mimix.io/en/dev/nebula
ClojureScript (cljs) (and Clojure) I would recommend as practical, but simple and approachable dialects.
Scheme is a good, simple dialect to learn if you’re just looking to grok lisp in “maxwell’s equations” elegance (but not work in it). Not nearly as many libraries written for it as Common Lisp has, or Clojure with JVM interop, or CLJS with javascript+node interop).
Whether you use Scheme or cljs (or another~), I recommend SICP[2] “Structure and Interpretation of Computer Programs”, free online from MIT press. There’s even an interactive, editable version online [3] (though it doesn’t support saving reader code)
[1]: http://www.paulgraham.com/lisp.html
[2]: https://mitpress.mit.edu/sites/default/files/sicp/index.html
[3]: https://xuanji.appspot.com/isicp/
-ANSI Common Lisp by Paul Graham -On Lisp by Paul Graham -Simply Scheme -The Little Schemer -Structure And Interpretation Of Computer Programs -Paradigms of Artificial Intelligence Programming
Implementations
Common Lisp - any should be fine, I’ve used sbcl and clisp
Scheme - I like gambit scheme. It has a macro system that can be used like described in “On Lisp”
He wrote two books on Common Lisp and has his own Lisp reading list: http://www.paulgraham.com/booklinks.html. There's also, one level higher, a set of lisp links: http://www.paulgraham.com/lisplinks.html