Lisp is so powerful, that it encourages individual independence to the point of bloody-mindedness.

Rudolf Winestock1

Because Lisp, as a tool, is to the mind as the lever is to the arm. It amplifies your power and enables you to embark on projects beyond the scope of lesser languages […] It opens out whole kingdoms shut to other programmers.

Dr. Mark Tarver2

I believe that LISP has special properties which makes it especially well suited to small startups. It is fast to write, easy to debug, and more powerful than just about anything else that you’ve encountered before. It enables individuals to create programs which requires scores of developers using other languages.

LISP is a super power. There is no other way of describing it.

This article is not a complete guide to LISP. Think of it more as a taster of why you should fall down the rabbit hole.

Stupidly small, almost offensive, introduction to LISP

First thing’s first. If you’re new to LISP. You want to focus on Common Lisp, specifically SBCL. This has the highest mindshare out of all LISPs3 and could be considered the defacto standard. If/when you run into issues, you will have a higher chance of getting assistance.

If you disagree with this, we can meet up to have a fight about it. Bring a team.

Here is a small recursive function to calculate factorial in Javascript:

function factorial (n) {
	if (n === 0) {
		return 1;
	} else {
		return n * factorial(n - 1);

Here is that same function, with the same logic, in LISP:

(defun factorial (n)
	(if (= n 0) 1
		(* n (factorial (- n 1)))))

It looks like it makes sense, but also it looks like there is something wrong. Something fundamentally wrong.

The first thing you’ll notice when you look at LISP code is that there are lots of parentheses. Lots. It initially looks hard to read but, believe me, you become accustomed to them very quickly and eventually stop seeing them. You’ll be that guy from the Matrix looking at the falling code. That bald guy who eats steak with the robot. You’ll be him, not seeing the code anymore but what it represents.

LISP is derived from LISt Processor. A list in LISP is simply (list 1 2 3)4. That looks shockingly similar to the code above, right? That’s because it is. LISP is homoiconic, this means that the code is data. All LISP code is, in fact, a list where the first element is the function which the rest of the elements get passed to. This is prefix notation.

One noteworthy thing about lists in LISP is that they are actually linked lists, where the first position is the value, and the second is a pointer to the rest of the list. The first position of (list 1 2 3) is 1, and the second is (2 3).

That’s all there is to LISP. There is no other syntax to learn. No hidden gotchas. When you understand the building blocks of the language, you stop writing code to satisfy a compiler and you start writing code to solve problems.

I’m going to go build my own language! With blackjack and hookers!

One really interesting implication of homoiconicity, is that you can very quickly modify the language to add features and constructs. You don’t need to wait on some governing body to 1) agree that the language requires this feature and 2) implement it. You have all of the tools to create everything that you need, with the power of macros.

As everything in LISP is a list, you can think about macros in terms of creating lists for LISP to evaluate. A great example of the power of this is the LOOP macro:

(loop for i from 1 upto 10
	  collect (* i i)
	  finally (print "finished!"))

This code reads like English and does exactly what you imagine it will. It counts from 1 upto 10, collecting the square of each number as a list, then finally it writes “finished!” to the screen. Here is a good reference for what is possible with LOOP.

While LOOP is a behemoth in terms of code size and complexity, macros are incredibly simple to define.

Lets say we’re building a website. We can create a special function that defines middleware:

(defmacro defmiddleware (name &body body)
  `(defun ,name (*request*)

As we can see, we’re creating a macro (defmacro) which accepts a name and the function body, and then creates a defun which accepts a *request*. We can use this macro to create middleware which verifies that there is a logged in user:

(defmiddleware verify-loggedin
  (unless (session-value :user-id)
      (add-error "Please login again")
      (redirect "/user/log-in"))))

This is contrived and you may be wondering the usefulness of this but extrapolate this into your own project. What parts of your codebase do you wish were more explicit in its functionality? What about parts which you wish read more like English? You get to create a programming language perfectly tailored for your needs at next to no cost.

Move Slow and Fix Things

When you see that there is a new version of your favourite programming language, do you get excited or worried? Maybe both? Maybe the version that you’re currently on is so old that it’s no longer receiving security updates. Or maybe you upgraded to the newest version and now everything is broken as it’s not compatible anymore. I’ve been there. We all have.

Most LISPs are standardized. What this means is that the language is fully defined and others can implement it themselves. I mentioned up top about using SBCL. Well, you don’t have to. Want to program microcontrollers? Go use µLisp. What about LISP on the JVM? You want ABCL. .Net Core, perhaps? Bike’s ya boy.

Another benefit of this is that LISP is almost entirely backwards compatible. People have their own LISP programs and libraries that they have been using since we thought that the year 2000 would cause the robots to rise up. Luckily, the robots were not programmed in LISP, otherwise they would’ve won. Or wouldn’t have risen up in the first place. This is a complicated point that I’m trying to make. LISP good, okay?

It also means that a LISP program can be, well, finished. If you see a Javascript project on Github that hasn’t had a commit in the last 30 minutes, that’s a sure sign that it has been abandoned, right? Well, LISP projects that haven’t been touched in years still work.

You can rest assured that the code you write now will still be usable years, maybe decades from now.

The Transcredible Exploits of LISPs REPL

REPL stands for Read Evaluate Print Loop. Many languages have one, but I imagine you’re not using it. No wonder. They’re typically terrible. Good for chucking in a little function here or there to test it but not much more, right?

What makes a LISP REPL special is because a LISP program is a living image that you can connect to and manipulate in place. You can view state, you can inspect classes, you can add, remove, and redefine functions. All without restarting the program. Nothing I could write can compare to one story that every LISP developer knows by heart.

The Remote Agent software, running on a custom port of Harlequin Common Lisp, flew aboard Deep Space 1 (DS1), the first mission of NASA’s New Millennium program. Remote Agent controlled DS1 for two days in May of 1999. During that time we were able to debug and fix a race condition that had not shown up during ground testing. (Debugging a program running on a $100M piece of hardware that is 100 million miles away is an interesting experience. Having a read-eval-print loop running on the spacecraft proved invaluable in finding and fixing the problem.

Ron Garret5

Most LISP developers have a REPL open beside their code. Developing becomes a conversation between you and LISP. There is no context switch between writing code and executing code. You define a function then immediately start plugging data into it. You can profile it, get stack traces, handle errors, disassemble the compiled code, all at a moments notice. And, same as above, you get to connect to programs running on other servers (or planets) with all of the exact same functionality.

All of this adds up to an incredibly fast development cycle. And it won’t be long until you wonder how you ever lived without it.

The SLY6 documentation is a really good reference which shows what is possible with a LISP REPL.

Wrapping Up

I started this article off with two quotes talking about how great LISP is. If you read the associated articles, you’ll know that it’s slightly more than just “LISP = good”; these articles are talking about the downsides of LISP. The idea that LISP is so powerful that it prevents communities from forming as everyone is able to be independent.

However, I believe that this is a benefit for an indie hacker. The language is fun and easy, and it gets out of your way to let you write the code that you need to write, regardless of how complex it may be. You’re already working by yourself, why not use the tool best suited to that lifestyle?

You can always rewrite the codebase when you’re profitable but if you are focused on shipping products quickly, you can’t really beat LISP.

The following are resources and references to learn LISP in depth.

Useful Packages

As the quotes at the start referenced, a lot of LISP developers like to reinvent the wheel to ensure that it is exactly what they require, but there is a vibrant and smart community which produces and maintains some really top quality open source code.

These are packages that I have personally used in production and have been really happy with:

Job Package
Package Management Quicklisp
Project Skeleton Quickproject
JSON Encoding/Decoding Jonathan
Web Server Hunchentoot
HTML Generation Spinneret
PostgresQL Postmodern
Time local-time
Web Requests Dexador
HTML Parsing Plump
Cryptography Ironclad
Multi-threading Bordeaux-threads
Pattern Matching Trivia
Standard Library++ Alexandria
Testing FiveAM
Cron cl-cron

  1. The Lisp Curse ↩︎

  2. The Bipolar Lisp Programmer ↩︎

  3. Yes, Clojure is a LISP. No, I’m not going to talk about it. ↩︎

  4. This could also be written as '(1 2 3) but reader macros are outside the scope of this article. ↩︎

  5. Lisping at JPL ↩︎

  6. SLY is an Emacs package. Emacs is a LISP program designed to edit text, but it can do anything you want. LISP can be developed using any text editor that you prefer. ↩︎