Lisp's Superpower: Saving Time Writing Code

By Henry Steere on July 27, 2018

A lot of people have heard about Lisp, but don't know what's so special about it. I decided to explore its potential by extending a json library using Lisp macros and saved a whole lot of time writing code. Here’s how.

I was writing a game-playing AI that needed information about the game from a json file, and I needed a way to read data from the json. The json library I was using turned json into lists and hash tables, but I wanted to map it to classes for two reasons:

To give the data meaning in the context of the game and

To give it structure and make it easy to manipulate.

Initially, I was doing this manually but quickly found that I was just writing the same code over and over. That’s when I realised that this was a perfect opportunity to use Lisp's macros. Lisp is one of the oldest and most revolutionary programming languages.

In other languages, there would be a sharp division between writing code and running it. Lisp's macros are useful because you can use all of Lisp’s runtime functionality as it’s generating and manipulating your code. With Lisp:

You can easily convert from text to symbols and back to text. This makes it easy to generate the symbols you use in code.

Lisp provides a templating syntax for generating lists. That means you can insert elements at arbitrary positions and also splice lists into other lists conveniently. This allows you to structure the list that represents your code in an easy-to-read way whereas function calls tend to make the structure you want less obvious.

Code is just a list of symbols. You can loop over code, append it and rearrange it with Lisp’s list functions. This is useful when you want to manipulate and generate code in more complicated ways. The templating syntax usually doesn’t support this all too well.

In order to understand why this was useful for my game-playing AI, we first have to have a quick look at how I set up my programme in Lisp.

Implementation in Lisp

Mapping classes from Json

I first defined the player class from the json because it’s a nice and simple starting point:

Now, for someone who doesn’t know much about Lisp, this might be a bit dense. So let’s look at all these parts in a little more detail.

Generating hash keys and symbols

For convenience reasons, you want to make the macro as brief as possible. I used Lisp's easy conversion between text and symbols to turn symbols into the literal hash keys I wanted and generate the other symbols I needed. This works by passing in just a few symbols and generating extra symbols and strings based on a pattern that uses those symbols.

Here’s how I did that: I got the hash table keys by converting the slot symbols (properties in a class) to text with the symbol-name function and then camel casing the text to match how it appears in the json:

Keywords that start with a colon like :energy to specify the arguments to the class constructor and

Ordinary symbols like the name of the function map-player-from-hash-table.

Ordinary symbols are created with (intern "YOUR-SYMBOL-NAME-HERE") and keywords are created with (intern "A-KEYWORD-SYMBOL" "KEYWORD").

Constructing a list of symbols

The next step was to create a template for what the function definition would look like. Lisp's list templating DSL let me easily construct a list of symbols with the code I wanted. The backtick operator tells Lisp that a templated list is about to begin. The comma operator tells Lisp to substitute the value of a variable at the next position in the list. The list splicing operator inserts a list into another list. Again, this is super valuable because it makes it a whole lot easier to structure your code in whatever way you choose.

In other languages code isn’t a value. That means, it’s either impossible to generate code or it’s incredibly clumsy and done in a context that’s separate from ordinary programming in the language. A good example for this is the preprocessor in C.

Because code in Lisp is made up of lists, an ordinary value, I could flexibly manipulate it and rearrange it.

I collected symbols into lists and appended them together to get the body of the class constructor:

(apply #'append
(loop for key in keys
for init-arg in init-args
for slot in slots
collect `(,init-arg (gethash ,key ,hash))))

All of this is just the beginning. After tweaking the macro some more, I generated code that:

Defines the class,

Defines a function that maps json to a class and also

Handles nested classes.

The final version can be found here and this is what it looks like to use it:

I could easily convert from symbols to text and back again. This let me use text manipulation functions to generate symbols and hash keys based on the symbols I passed to the macro.

The list templating syntax let me easily put symbols where they needed to be and splice pieces of code into other pieces of code.

Because Lisp code is a list, it’s a value in the language and can be iterated over and manipulated like any other data structure. This let me map functions over the arguments passed to the macro and append the lists of symbols I needed to make the body of a function call.

Resources

If you’re interested in Lisp there are a lot of good books to take a look at. Many of them are freely available.

On Lisp by Paul Graham (founder of Hacker News) is a classic book on Lisp focussing on Macros

Let Over Lambda by Doug Hoyte is a very entertaining and interesting exploration of Lisp and Macros