Книга: Practical Common Lisp
Packaging Mechanics
Packaging Mechanics
That covers the basics of how to use packages to manage namespaces in several common situations. However, another level of how to use packages is worth discussing—the raw mechanics of how to organize code that uses different packages. In this section I'll discuss a few rules of thumb about how to organize code—where to put your DEFPACKAGE
forms relative to the code that uses your packages via IN-PACKAGE
.
Because packages are used by the reader, a package must be defined before you can LOAD
or COMPILE-FILE
a file that contains an IN-PACKAGE
expression switching to that package. Packages also must be defined before other DEFPACKAGE
forms can refer to them. For instance, if you're going to :use COM.GIGAMONKEYS.TEXT-DB
in COM.GIGAMONKEYS.EMAIL-DB
, then COM.GIGAMONKEYS.TEXT-DB
's DEFPACKAGE
must be evaluated before the DEFPACKAGE
of COM.GIGAMONKEYS.EMAIL-DB
.
The best first step toward making sure packages exist when they need to is to put all your DEFPACKAGE
s in files separate from the code that needs to be read in those packages. Some folks like to create a foo-package.lisp
file for each individual package, and others create a single packages.lisp
that contains all the DEFPACKAGE
forms for a group of related packages. Either approach is reasonable, though the one-file-per-package approach also requires that you arrange to load the individual files in the right order according to the interpackage dependencies.
Either way, once all the DEFPACKAGE
forms have been separated from the code that will be read in the packages they define, you can arrange to LOAD
the files containing the DEFPACKAGE
s before you compile or load any of the other files. For simple programs you can do this by hand: simply LOAD
the file or files containing the DEFPACKAGE
forms, possibly compiling them with COMPILE-FILE
first. Then LOAD
the files that use those packages, again optionally compiling them first with COMPILE-FILE
. Note, however, that the packages don't exist until you LOAD
the package definitions, either the source or the files produced by COMPILE-FILE
. Thus, if you're compiling everything, you must still LOAD
all the package definitions before you can COMPILE-FILE
any files to be read in the packages.
Doing these steps by hand will get tedious after a while. For simple programs you can automate the steps by writing a file, load.lisp
, that contains the appropriate LOAD
and COMPILE-FILE
calls in the right order. Then you can just LOAD
that file. For more complex programs you'll want to use a system definition facility to manage loading and compiling files in the right order.[232]
The other key rule of thumb is that each file should contain exactly one IN-PACKAGE
form, and it should be the first form in the file other than comments. Files containing DEFPACKAGE
forms should start with (in-package "COMMON-LISP-USER")
, and all other files should contain an IN-PACKAGE
of one of your packages.
If you violate this rule and switch packages in the middle of a file, you'll confuse human readers who don't notice the second IN-PACKAGE
. Also, many Lisp development environments, particularly Emacs-based ones such as SLIME, look for an IN-PACKAGE
to determine the package they should use when communicating with Common Lisp. Multiple IN-PACKAGE
forms per file may confuse these tools as well.
On the other hand, it's fine to have multiple files read in the same package, each with an identical IN-PACKAGE
form. It's just a matter of how you like to organize your code.
The other bit of packaging mechanics has to do with how to name packages. Package names live in a flat namespace—package names are just strings, and different packages must have textually distinct names. Thus, you have to consider the possibility of conflicts between package names. If you're using only packages you developed yourself, then you can probably get away with using short names for your packages. But if you're planning to use third-party libraries or to publish your code for use by other programmers, then you need to follow a naming convention that will minimize the possibility of name collisions between different packages. Many Lispers these days are adopting Java-style names, like the ones used in this chapter, consisting of a reversed Internet domain name followed by a dot and a descriptive string.