There are many things to like about Common Lisp packages. In everyday programming, I really like being able to define one or more packages in one file then just have a (in-package package-name) at the top of other files opposed to having sometimes hundreds of lines of imports. Some programming languages are worse than others, but no other programming language that I know of allows one to keep imports in a totally different file.

The real point of this post is to talk about a specific scenario that reveals in interesting interaction between asdf systems (libraries), symbols, and macros. A conversation about packages almost always involves talking about symbols. Here is the situation: there is a library, A, that implements some very useful computations. The public interface for the library is a combination of macros and functions. It is rather verbose though, so you want to wrap it in a macro and create a new library: B. You want the users of your library to only have to use your library and not have to worry about depending directly on library A, so you want to re-export some of the symbols from library A so the users of your library can just use those symbols exported from your library.

If you don’t see a problem with this then congratulations: you are sane. This relies on just a couple features:

  • feature of asdf: orthogonal to packages meaning that one can use a symbol from any package without having to explicitly load the system. If a package is loaded, it’s symbols can be used.

  • feature of packages: symbols in a package can be referred to by their full name (package-name:symbol-name) anywhere in anyone’s program. It does not matter if they have the symbol imported into their package. It does not matter if they have a package local nickname. It does not matter that they have a different symbol interned with the same symbol-name.

  • macros do not have any feature of their own that make this possible, but because of the previously mentioned features, using a symbol from another library in a macro expansion works just fine. As an added benefit, you cannot expand to symbol from a package that is not exported or from a non-existent package because the reader (which happens before macro expansion, even before macro definition) makes sure all the symbols are accounted for.

I witnessed someone in almost this exact situation. They were using Rust. The problem happened when someone tried to use the macro from library A that had been re-exported from library B. The macro expanded to use some symbols that were qualified assuming that the user had already imported them from library A. However, the user imported from library B. The work around ended up being to not re-export and just tell the user via documentation that they have to explicitly depend on library A, and use their macro.

That is the story. Let me know what you think, and tell me about a time that you were grateful for the way Common Lisp handles packages and symbols.