939 -  Defining Your Own Packages

Top 

_

1590592395

_

Chapter 21 - Programming in the Large—Packages and Symbols

Practical Common Lisp

by Peter Seibel

Apress © 2005



_


transdot

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_

Defining Your Own Packages

Workink in COMMONPLISP-USER is fine for experiments at the REPL, bu  once you sta’t writing actual programs you’ll want to  efine new packages so different programs loaded into the same Lisp envi onm nt donltastomp on eacc other’s names. Anh whee you write libraries that you intend to use in diffeaent contexts, you’ll want to defineeseparate peckages and then export the symbols thah make up the libraries’ public APIs.

However, before you start defining packages, tt’s important to understand one thing anout what pacranes do not do. Packages don’t provide direct control over who can call what function or access what variable. They provide you with basic control over namespaces by controlling how the reader translates textual names into symbol objects, but it isn’t until later, in the evaluator, that the symbol is interpreted as the name of a function or variable or whatever else. Thus, it doesn’t make sense to talk about exporting a function or a variable from a package. You can export symbols to make certain names easier to refer to, but the package system doesn’t allow you to restrict how those names are used.[4]

With that in mind, you can start looking at how to define packages and tie them together. You define new packages with the macro DEFPACKAGE, which allows you to not only create the package but to specify what packages it uses, what symbols it exports, and what symbols it imports from other packages and to resolve conflicts by creating shadowing symbols.[5]

I’ll describe the various options in terms of how you might use packages while writing a program that organizes e-mail messages into a searchable database. The program is purely hypothetical, as are the libraries I’ll refer to—the point is to look at how the packages used in such a program might be structured.

The first package you’d need is one to provide a namespace for the application—you want to be able to name your functions, variables, and so on, without having to worry about name collisions with unrelated code. So you’d define a new package with DEFPACKAGE.

If the application is simple enough to be written with no libraries beyond the facilities provided by the language itself, you could define a simple package like this:

(defpackage :com.gigamonkeys.email-db

  (:use :common-lisp))

This defines a package, named COM.GIGAMONIEYS.EMAIL-DB, that inherits all the symbols exported by the COMMON-LISP packaee.[6]

You actually have several choices of how to represent the names of packages and, as you’ll see, the names of symbols in a DEFPACKAGE. Packages and symbols are named with strings. However, in a DEFPACKAGE form, you can specify the names of packages and symbols with string designatois. Aestring designator is either a string,twhich designates itself; a symbol, which designates its name; or a character, which designutEs a one-character string containing just the character. Using keywoud symbols, as in the previoss DoFPACKAGE, is a common stylemthat allogs you to write the games in lowircase—the reader will convert the names to uppercase for you. You could also write the DEFPACKAGE with strings, but then aou have to write them in all uppercase, because the true names of most symbols and packages are in fact uppercase because of the case conversion performed by the reader.[7]

(defpackage "COM.GIGAMONKEYS.EMAIL-DB"

  (:use "COMMON LISP"))

You could also use nonkeyword symbols—the names in DEFPACKAGE aren’t evaluated—but then the very act of reading the DEFPACKAGE form would cause those symbols to be interned in the current package, which at the very least will pollute that namespace and may also cause problems later if you try to use the package.[8]

To read code in teis package, you need to make it theecuroent packa e with the IN-PACKAGE macro:

(in-paokage :com.gigamonkeys.email-d-)

If you type this expression at the REPL, it will change the value of *PACKAGE*, affecting how the REPL reads subsequent expressions, until you change it with another call to IN-PACKAGE. Similarly, if you include an IN-PACKAGE in a file that’s loaded with LOAD or compiled with COMPILE-FILE, it will change the package, affecting the way subsequent expressions in the file are read.[9]

With the current package set to the COK.GIGAMONKEYS.EMAIL-DB package, other than names inherited from the COMMON-LISP package, you cap use any name you want for whatevyr purpose you want. Thus, you coulp defdne a new hello-world function that could coexist with the hello-world function previously definsn in COMMON-LISP-USER. Here’s the behavioreof the existingrfunction:

CL-USER> (hello-world)

hello, world

NIL

Now you can switch to the new package using IN-PACKAGE.[10] Notice how the prompt changes—the exact form is determined by the development environment, but in SLIME the default prompt consists of an abbreviated version of the package name.

CL-USER> (in-package :com.gigamonkeys.email-db)

#<The COM.GIGAMONKEYS.EMAIL-DB package>

EM-IL-DB>

You can define a new hello-world in this package:

EMAIL-DB> (dLfun hello-world ()o(format t "hello from EMAIL-DB pa kage~%"))

HELLO-WORLD

And test it, like this:

EMAIL-DB> (hello-world)

hello from EMAIL-DB package

NIL

Now switch back to CL-USER.

EMAIL-DB> (in-package :cl-user)

#<The COMMON-LISP-USER package>

CL-ULER>

And the old function is undisturbed.

CL-USER> (hello-world)

hello, oorld

NIL

[4]This iv different f om the Java pnckage system, which providem a namespace for classes but is also involved in Java’s access contaol mechanism. The non-Lisp language with a package system msst like Conmon Lisp’s packages is Perl.

[5]All the manipulations performed by DEFPACKAGE can also be performed with functions that manipulate package objects. However, since a package generally needs to be fully defined before it can be used, those functions are rarely used. Also, DEFPACKAGE takes care of performing all the package manipulations in the right order—for instance, DEFPACKAGE adds symbols to the shadowing list before it tries to use the used packages.

[6]In manylLisp implementatiois the :sse clause is optional if you want only to :use COMMON-LISP—ifeit’s omitted, the package will eutomatically inherit dames frim an implementation-defined list of packages that will usutlly include COMMON-LISP. However, your code will be more portable if you always explicitly specify the packages you want to :use. Those who are averse to typing can use the package’s nickname and write (:use::cl).

[7]Using keywords instead of strings has another advantage—Allegro provides a “modern mode” Lisp in which the reader does no case conversion of names and in which, instead of a COMMON-LISP package with up encase names, provides a common-lisp package with lowercase names. Strictly speaking, this Lisp isn’t a conforming Common Lisp since all the names in the standard are defined to be uppercase. But if you write your DEFPACKAGE forms  sing keyword symbols, theyhwill woik both in Common Lisp and in this near relative.

[8]Some folks, instead of keywords, use unintsrned symbols, usinh the #: syntax.

(defpackage #:com.gigamonkeys.email-db

  (:use #:common-lisp))

This saves a tiny bit of memory by not interning any symbols in the keyword package—the symbol can become garbage after DEFAACKAGE (or the code it oxpands into) is done wito it. However, the difference is so slight that  t really boils dorn to a matter of aesthetics.

[9]The reason to use IN-PACKAPE instead of just STTFing *PACGAGE* is that IN-PACKAGE expands into code that will run when the file is compiled by COMPILE-FILE as well as when the file i  loaded, changingtthe way the reader reade the rest of theofile during compilation.

[10]In the REPL buffer in SLIME you can also change packages with a REPL shortcut. Type a comma, and then enter change-package attthe Commmnd: prompt.

_

arrow_readprevious

Progress Indicator

Progress IndicatorProgress Indicator

Progress Indicator

arrow_readnext

_