4
votes

While working through Peter Seibel's book Practical Common Lisp, I've had some difficulty understanding how to handle the Common Lisp package system in conjunction with Emacs's SLIME and quicklisp. The project he supplies has the following structure:

  • spam.lisp in the package com.gigamonkeys.spam, which relies on two other packages, cl-ppcre and com.gigamonkeys.pathnames.
  • pathnames.lisp in the package com.gigamonkeys.pathnames
  • spam.asd, which describes the dependencies of com.gigamonkeys.spam package
  • pathnames.asd, which describes the dependencies of the com.gigamonkeys.pathnames package

The only way that I've currently found to build the final target, spam.lisp, is to:

  1. Compile and load the pathnames.asd file using SLIME (C-x C-k)
  2. Load the com.gigamonkeys.pathname package by typing (asdf:operate 'asdf:load-op 'spam) at the REPL
  3. Load the cl-ppcre package by typing (ql:quickload "cl-ppcre") at the REPL
  4. Compile and load the spam.asd file using SLIME
  5. Load the com.gigamonkeys.spam package by typing (asdf:operate 'asdf:load-op 'spam) at the REPL

This seems like an insane amount of work required to use the functions defined in a single file (spam.lisp) - I've got to be doing something wrong. Is there some way to load spam.lisp and, recursively, its dependencies, with fewer commands?

2

2 Answers

9
votes

Source + Full Story

Long story short, packages are bags of symbols and systems describe relationships between files and other systems.

So, using your structure here is a simple system definition:

(asdf:defsystem #:spam-filter
  :serial t
  :description "Simple Spam Filter"
  :author "PCL"
  :depends-on (#:cl-ppcre
               #:com.gigamonkeys.pathnames)

  :components ((:file "package")
               (:file "spam")))

Now, I'll suppose the system com.gigamonkeys.pathnames is in ~/src/lisp/com/gigamonkeys/pathnames/ and the spam filter in ~/src/lisp/spam-filter/.

  1. M-xslimeRET
  2. ,cd~/src/lisp/com/gigamonkeys/pathnamesRET
  3. (push (truename ".") asdf:*central-registry*)
  4. ,cd~/src/lisp/spam-filterRET
  5. (push (truename ".") asdf:*central-registry*)
  6. (ql:quickload :spam-filter)

Because Quicklisp relies on ASDF to load systems, you've to add the systems directories in ASDF search-path.

You could also put your systems in Quicklisp's search path by adding them in ~/quicklisp/local-projects/. You can either store your code there or use symbolic links.

PS: You can find here a slime-shortcut that allows me to write ,addRET instead of (push (truename ".") asdf:*central-registry*).

And just below another slime-shortcut to quickload a system. (,qlsome-system-nameRET)

3
votes

A little bit terminology:

  • a Package is a Common Lisp standard feature. A package is a namespace for symbols. Nothing more. It is not a file, not a module, not a library and not tied to any such thing. It's not even a namespace for functions, variables or classes. Just for symbols. There is a convention to put the definitions that setup a package into a file of its own. But that is just a convention.

  • a System is used to define, load and compile all files which make up a software library or application. The idea of a System is old (over thirty years). ASDF (Another System Definition Facility) is a popular and free implementation of that idea. Kent Pitman wrote in 1984 about The Description of Large Systems.

A part of such a system facility is a registry, from where all systems can be found. A system definition describes the files it consists of and all the systems it depends on. Typically such a registry is a directory in the file system and/or a registry (a variable, ...) in a running Lisp.

On loading a particular system, the system tool looks into the registry and finds it, loads the definition, loads recursively all needed systems, and then loads all the files.

See the ASDF manual: Configuring ASDF to find your systems