How I Make Lisp Projects
You have no idea what you’re trying to achieve until you’ve achieved it.
—Gerald Jay Sussman
Table of contents
Introduction
My original intention in writing this article was to create a part 2 of the first article about scripting in Lisp. My plan was to talk about how I approach command line scripting using Lisp. Instead, what I want to talk about now is how I make Lisp projects, in a way that reduces the initial time to write boilerplate code. I do this, a lot, that I thought that it would be better to talk about it instead. Incidentally, this approach does make use of how I write Lisp scripts for the command line. I believe, I’ll be able to hit somewhat like two birds with one stone, with this one.
Motivation
Writing your own project maker is a rite-of-passage in Lisp Land. They say you can’t call yourself a lisper until you can make a half-decent tool that creates project skeletons.
My primary motivation in writing my own project maker came from not liking the ones that are already out there. I don’t like them not because the suck. I don’t like them because they don’t fit my needs. I have my own way of structuring my files and directories, and modifying those existing tools to fit my needs would essentially mean rolling out with my own tool, eventually.
Another reason why I want to roll my own is ability to specify the components that I need for the projects that we do. My engineering team has specific needs that warrant attention. We use Nix and Flakes. For that, we use the directions that are written here and here. We also do a lot of CLI work, so I need a way to parse command line options and arguments.
Installation
You may be coming to this article because you got curious, but you don’t have the basic tools, yet; or you may be coming here, to see what’s the fuss all about. Either way, I’m here to guide you.
The software that you need are the following:
- SBCL
- Quicklisp
- Marie
SBCL
The only decent open source Lisp implementation, nowadays, is SBCL. It works on x86, x86_64, and aarch64. It runs on GNU/Linux, macOS, and Windows. It produces high-quality optimized code. On the commercial side of things is LispWorks, which is the one that I use as my daily driver. The other ones are nice, but they’re not available on both GNU/Linux x86_64 and macOS aarch64, which are the systems that I care about now.
You can go directly to SBCL’s site to download it, or you can use your operating system package manager to install it. It should look something like this:
sudo apt install -y sbcl
or
brew install sbcl
But, you already know what I mean 😉
Quicklisp
Quicklisp is the de facto standard for installing Lisp libraries. There are no fancy bells and whistles. It just works. A one-liner to install it on GNU/Linux or macOS is:
curl -O https://beta.quicklisp.org/quicklisp.lisp && sbcl --load quicklisp.lisp --eval '(quicklisp-quickstart:install)' --eval '(let ((ql-util::*do-not-prompt* t)) (ql:add-to-init-file) (sb-ext:quit))'
To verify that it works, run the following:
sbcl --noinform --eval '(princ (ql:client-version))' --quit
It should say something like this:
"2021-02-13"
Marie
Marie, on the other hand, is the library that does the shenanigans of creating the project. We also need Clingon to help us with command line parsing. To fetch them, run the following:
mkdir ~/common-lisp
cd ~/common-lisp
git clone https://github.com/vedainc/marie
sbcl --noinform --eval '(ql:quickload :clingon)' --quit
Next, let’s put a stub function in your shell configuration to make things easier. Run this command, and change ~/.zshenv
to ~/.bashrc
, appropriately:
cat >> ~/.zshenv << EOF
function mk {
sbcl --noinform --eval '(ql:quickload :marie)' --eval "(marie:make-project \"\$1\")" --quit
}
EOF
. ~/.zshenv
But, you get the idea. Also, I don’t do any of that fish shell crap.
Basics
Now that we all the requirements installed, you can now create a project called foo
:
mk foo
What this does is it creates this tree:
├── flake.nix
├── foo-tests.asd
├── foo.asd
├── makefile
├── README.org
├── shells.nix
├── src
│ ├── build.lisp
│ ├── core.lisp
│ ├── driver.lisp
│ ├── main.lisp
│ ├── specials.lisp
│ ├── user.lisp
│ └── version.lisp
└── t
├── driver-tests.lisp
├── main-tests.lisp
├── user-tests.lisp
└── version.lisp
3 directories, 17 files
To test that foo
works, let’s build its command line executable:
cd ~/common-lisp/foo
make
./foo --help
It should show something like the following:
NAME:
foo - 0.0.0
USAGE:
foo [global-options] [<command>] [command-options] [arguments ...]
OPTIONS:
--help display usage information and exit
--version display version and exit
-v, --verbose verbosity [default: 0]
COMMANDS:
find, f find files
zsh-completions, zsh generate the Zsh completion script
print-doc, doc print the documentation
Closing Remarks
Marie allows me to quickly prototype an idea and have it running soon. This tool allows me to easily make executables that I can toss around. I used these tools to build Vix, a (very) thin wrapper around the Nix ecosystem.