emacs.d/cl-packages.org

937 lines
24 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

* The Complete Idiot's Guide to Common Lisp Packages
Erann Gat
Copyright © 2003 by the author. Permission is hereby granted for non-commercial
use provided this notice is retained.
** 1. Introduction
When coding a large project with multiple programmers two different programmers
will often want to use the same name for two different purposes. It is possible
to solve this problem using a naming convention, e.g. Bob prefixes all his
names with “BOB-“ and Jane prefixes all her names with “JANE-“. This is in fact
how Scheme addresses this problem (or fails to address it as the case may be).
Common Lisp provides a language mechanism called packages for segregating
namespaces. Here's an example of how packages work:
#+begin_example
? (make-package :bob)
#<Package "BOB">
? (make-package :jane)
#<Package "JANE">
? (in-package bob)
#<Package "BOB">
? (defun foo () "This is Bob's foo")
FOO
? (in-package jane)
#<Package "JANE">
? (defun foo () "This is Jane's foo")
FOO
? (foo)
"This is Jane's foo"
? (in-package bob)
#<Package "BOB">
? (foo)
"This is Bob's foo"
#+end_example
(NOTE: Code examples are cut-and-pasted from Macintosh Common Lisp (MCL). The
command prompt in MCL is a question mark.)
Bob and Jane each have a function named FOO that does something different, and
they don't conflict with each other.
1 Version 1.2
What if Bob wants to use a function written by Jane? There are several ways he
can do it. One is to use a special syntax to indicate that a different package
is to be used:
? (in-package bob)\\
#<Package "BOB">
? (jane::foo)
"This is Jane's foo"
?
Another is to import what he wants to use into his own package. Of course, he
won't want to import Jane's FOO function because then it would conflict with
his own, but if Jane had a BAZ function that he wanted to use by simply typing
(BAZ) instead of (JANE::BAZ) he could do it like this:
? (in-package jane)
#<Package "JANE">
? (defun baz () "This is Jane's baz")\\
BAZ
? (in-package bob)
#<Package "BOB">
? (import 'jane::baz)
T
? (baz)
"This is Jane's baz"
?
Alas, things don't always go quite so smoothly:
? (in-package jane)
#<Package "JANE">
? (defun bar () "This is Jane's bar")
BAR
? (in-package bob)
#<Package "BOB">
? (bar)
> Error: Undefined function BAR called with arguments () .
> While executing: "Unknown"
> Type Command-/ to continue, Command-. to abort.
> If continued: Retry applying BAR to NIL.
See the Restarts... menu item for further choices.
1 >
; Oops! Forgot to import.
Aborted
? (import 'jane::bar)
> Error: Importing JANE::BAR to #<Package "BOB"> would conflict with symbol BAR
.
> While executing: CCL::IMPORT-1
> Type Command-/ to continue, Command-. to abort.
> If continued: Ignore attempt to import JANE::BAR to #<Package "BOB">.\\
See the Restarts... menu item for further choices.
1 >
; Huh?
To understand why this happened, what to do about it, and many of the other
subtleties and surprises of packages, it is important to understand what
packages actually do and how they work. For example, it is important to
understand that when you type (import jane::foo) you are importing the symbol
JANE::FOO, not the function associated with that symbol. It is important to
understand the difference, and so we have to start with a review of some basic
Lisp concepts.
** 2. Symbols, Values, and the READ-EVAL-PRINT Loop
Lisp operates in a READ-EVAL-PRINT loop. Most of the interesting stuff happens
in the EVAL phase, but when it comes to packages interesting stuff happens in
all three phases, and it's important to understand what happens when. In
particular, some of the processing related to packages can change the state of
the Lisp system at READ time, which can in turn result in some surprising (and
often annoying) behavior, like the last example in the previous section.
A package is a collection of Lisp symbols, so to understand packages you first
have to understand symbols. A symbol is a perfectly ordinary Lisp data
structure, just as lists, numbers, strings, etc. are. There are built-in Lisp
functions for creating and manipulating symbols. For example, there is a
function called GENTEMP that creates new symbols:
#+begin_example
? (gentemp)
T1
? (setq x (gentemp))
T2
? (set x 123) ; Note the use of SET, not SETQ or SETF\\
123
? x
T2
? t2
123
#+end_example
(If you are not familiar with the SET function now would be a good time to look
it up because if you don't you will be lost in short order.)
The symbols created by GENTEMP behave in all respects like symbols that you get
just by typing in their names.
You have only limited control over the name of a symbol created by GENTEMP. You
can pass it an optional prefix string that, but the system will add a suffix
and you have to take whatever it decides to give you. If you want to make a
symbol with a particular name you have to use a different function,
MAKE-SYMBOL:
? (make-symbol "MY-SYMBOL")\\
#:MY-SYMBOL
?
Hm, that's odd. What's that funny-looking “#:” doing there?
To understand this we have to dig a little deeper into the guts of symbols.
? (setq symbol1 (make-symbol "MY-SYMBOL"))\\
#:MY-SYMBOL
? (setq symbol2 (make-symbol "MY-SYMBOL"))\\
#:MY-SYMBOL
? (setq symbol3 'my-symbol)
MY-SYMBOL
? (setq symbol4 'my-symbol)
MY-SYMBOL
? (eq symbol1 symbol2)
NIL
? (eq symbol3 symbol4)
T
?
As you see, MAKE-SYMBOL can make multiple distinct symbols that have the same
name, whereas symbols that the reader gives you by typing the same name on two
different occasions are the same symbol.
This property of symbol identity is very important. It is what insures that the
FOO you type in one place is the same FOO as the FOO you type someplace else.
If this were not so you could end up with some very weird results:
? (set symbol1 123)
123
? (set symbol2 456)
456
? (setq code-fragment-1 (list 'print symbol1))\\
(PRINT #:MY-SYMBOL)
? (setq code-fragment-2 (list 'print symbol2))\\
(PRINT #:MY-SYMBOL)
? (eval code-fragment-1)
123
123
? (eval code-fragment-2)
456
456
?
Contrast this with:
? (set symbol3 123)
123
? (set symbol4 456)
456
? (setq code-fragment-3 (list 'print symbol3)) (PRINT MY-SYMBOL)
? (setq code-fragment-4 (list 'print symbol4)) (PRINT MY-SYMBOL)
? (eval code-fragment-3)
456
456
? (eval code-fragment-4)
456 456
?
Symbols 1-4 all have the name “MY-SYMBOL” but symbols 1 and 2 are different
symbols, while symbols 3 and 4 are the same symbol. How did this happen? Well,
one obvious difference is that we called the function MAKE-SYMBOL to make
symbols 1 and 2, while symbols 3 and 4 were made for us by the Lisp reader.
Maybe the Lisp reader has a different way of making symbols than calling MAKE-
SYMBOL. We can test this hypothesis:
? (trace make-symbol)
NIL
? 'foobaz
Calling (MAKE-SYMBOL "FOOBAZ")\\
MAKE-SYMBOL returned #:FOOBAZ\\
FOOBAZ
Nope, the reader apparently makes symbols the same way we did, by calling
MAKE-SYMBOL. But wait, the symbol returned by MAKE-SYMBOL had that funny #:
thing in front of it, but by the time the reader was done the #: prefix had
vanished. What gives?
We can find the answer by trying the same experiment a second time with MAKE-
SYMBOL still traced:
? 'foobaz\\
FOOBAZ
Aha! The second time we type FOOBAZ the reader doesn't call MAKE-SYMBOL. So the
reader is apparently keeping a collection of all the symbols it has made, and
before it makes a new one it checks that collection to see if there is already
a symbol there by the same name. If there is, then it simply returns that same
symbol instead of making a new one. And a symbol that is a member of such a
collection loses its mysterious #: prefix.
That collection of symbols is called a package.
A package is a collection of Lisp symbols with the property that no two symbols
in the collection have the same name.
Unfortunately, that is more or less the last aspect of packages that is simple
and straightforward. From here on out things get rather hairier.
** 3. Interning
The act of putting a symbol into a package is called interning a symbol. A
symbol that is a member of a package is said to be interned in that package.
Symbols that are not members of any package are said to be uninterned. When an
uninterned symbol is printed, it gets a #: prefix^{2} to distinguish it from an
interned symbol, and to warn you that just because it looks just like another
symbol that you saw before it might not in fact be the same symbol.
Now, here's where things start to get a tad confusing.
There is a Lisp function called INTERN, which you might expect to add a symbol
to a package, but it doesn't. That function is performed by a function called
IMPORT.
#+begin_example
? symbol1
#:MY-SYMBOL
? (import symbol1)
T
? symbol1
MY-SYMBOL
? (eq symbol1 'my-symbol)
T
#+end_example
As you can see, symbol1 has gone from being an uninterned symbol to an interned
symbol. It has lost its #: prefix, and it is now EQ to the symbol MY- SYMBOL as
produced by the Lisp reader.
Now, you might expect that to undo the effect of IMPORT you would call
UNIMPORT, but there is no such function. (I warned you that things would not be
straightforward.) To remove a symbol from a package you call UNINTERN:
#+begin_example
? (unintern symbol1)
T
? symbol1
#:MY-SYMBOL
? (eq symbol1 'my-symbol)\\
NIL
#+end_example
Things are back to the way they were. Symbol 1 is now uninterned, and it is now
a different symbol than the one the reader gives you. Let's put symbol1 back
into our package:
? (import symbol1)
> Error: Importing #:MY-SYMBOL to #<Package "COMMON-LISP-USER"> would conflict
with symbol MY-SYMBOL .
> While executing: CCL::IMPORT-1
> Type Command-/ to continue, Command-. to abort.
> If continued: Ignore attempt to import #:MY-SYMBOL to #<Package
"COMMON-LISP-USER">.
See the Restarts... menu item for further choices.
2 This is not quite accurate, but a good enough approximation for now. The real
truth is that a #: prefix just means that a symbol has no home package, not
necessarily that it is not interned in any package.
1 >
Whoa! What happened? (It is a good exercise to try to figure out what's going
on here before reading any further. You have all the information you need.
Hint: try the experiment yourself with MAKE-SYMBOL traced.)
Here's what happened:
? (unintern 'my-symbol)
T
? (eq symbol1 'my-symbol
Calling (MAKE-SYMBOL "MY-SYMBOL")\\
MAKE-SYMBOL returned #:MY-SYMBOL
)
NIL
?
When we typed (eq symbol1 'my-symbol) the reader interned the symbol MY-
SYMBOL. So when we tried to import symbol1 there was already a symbol with the
same name in the package. Remember, packages maintain the invariant that there
can only be one symbol with the same name in the package at any one time
(that's the whole point) so when we tried to import symbol1 (whose name is also
MY-SYMBOL) there was already a symbol there with the same name (having been
quietly interned by the reader). This situation is called a symbol conflict,
and it is, alas, very common.
Note, by the way, that the output from the trace of MAKE-SYMBOL above appears
inside the S-expression (eq symbol1 my-symbol). This is because MCL rearranges
the text on the screen to reflect the true order of events. Because MAKE-SYMBOL
was called by the reader while processing the string “my-symbol” but before
processing the close-paren, the output appears at that point. If we type: (list
'x1 'x2 'x3 'x4) the resulting output looks like this:
? (list 'x1
Calling (MAKE-SYMBOL "X1")\\
MAKE-SYMBOL returned #:X1\\
'x2
Calling (MAKE-SYMBOL "X2")\\
MAKE-SYMBOL returned #:X2\\
'x3
Calling (MAKE-SYMBOL "X3")\\
MAKE-SYMBOL returned #:X3\\
'x4
Calling (MAKE-SYMBOL "X4")\\
MAKE-SYMBOL returned #:X4
)
Here, the text formatting has been preserved to help illustrate what is
happening. The text typed by the user is in bold type. (Note that most Lisp
systems don't do this reformatting.)
There are a few more useful functions that we can go ahead and mention at this
point. SYMBOL-NAME returns the name of a symbol as a string. FIND-SYMBOL takes
a string and tells you if there is a symbol with that name already interned.
And, finally, INTERN, which can be defined as follows:
#+begin_src lisp
(defun intern (name)
(or (find-symbol name)
(let ((s (make-symbol name)))
(import s)
s)))
#+end_src
In other words, INTERN is sort of the opposite of SYMBOL-NAME. SYMBOL-NAME
takes a symbol and returns the string that is that symbol's name. INTERN takes
a string and returns the symbol whose name is that string. INTERN is the
function that READ uses to make symbols.
** 4. Which Package?
The functions that operate on packages like FIND-SYMBOL, IMPORT and INTERN by
default operate on a package that is the value of the global variable
*PACKAGE*, also known as the current package. Like symbols, packages have
names, and no two packages can have the same name. Packages, like symbols, are
also ordinary Lisp data structures. There are functions PACKAGE-NAME and
FIND-PACKAGE which operate analogously to SYMBOL-NAME and FIND-SYMBOL, except
of course, that FIND-PACKAGE doesn't take a package argument. (It's as if there
is one global meta-package for package names.)
As we've already seen, we can make new packages by calling MAKE-PACKAGE, and
set the current package by calling IN-PACKAGE. (Note that IN-PACKAGE is not a
function but a macro that does not evaluate its argument.)
You can access symbols that are not in the current package by using a special
syntax: the package name followed by two colons followed by the symbol name.
This is handy if you want to use a symbol without importing it. For example, if
Bob wanted to use Jane's FOO function he could type JANE::FOO.
*** 4.1 Home packages
One of the invariants that Common Lisp tries to maintain is a property called
print-read consistency. This property says that if you print a symbol, and then
read the resulting printed representation of that symbol, the result is the
same symbol, with two caveats: 1) this does not apply to uninterned symbols,
and 2) it applies only if you refrain from certain “dangerous” actions. We'll
cover what those are in a moment.
To maintain print-read consistency, some symbols need to be printed with their
package qualifier. For example:
? (in-package jane)\\
#<Package "JANE">
? 'foo
FOO
? 'jane::foo
FOO
? (in-package bob) #<Package "BOB">
? 'foo
FOO
? 'jane::foo\\
JANE::FOO
? 'bob::foo
FOO
?
Obviously one of the “dangerous actions” that allows print-read consistency to
be violated is calling IN-PACKAGE.
Now, consider the following situation:
? (in-package bob)\\
#<Package "BOB">
? (unintern 'foo)
T
? (import 'jane::foo)
T
? (make-package :charlie)\\
#<Package "CHARLIE">
? (in-package charlie)\\
#<Package "CHARLIE">
? 'bob::foo
JANE::FOO
?
Here we have a symbol FOO which is interned in both the JANE and the BOB
packages. That one symbol is therefore accessible as both JANE::FOO and
BOB::FOO. How does the system decide which printed representation to use when
the symbol is printed from the CHARLIE package, in which the symbol is not
interned?
It turns out that every symbol keeps track of a single package called its home
package, which is usually the first package in which that symbol was interned
(but there are exceptions). When a symbol needs to be printed with a package
qualifier, it uses the qualifier from its home package. You can query a symbol
for its home package using the function SYMBOL-PACKAGE.
? (symbol-package 'bob::foo)\\
#<Package "JANE">
?
Note that it is possible to make symbols without home packages. Uninterned
symbols, for examples, have no home packages. But it is also possible to make
interned symbols with no home packages. For example:
? (in-package jane)
#<Package "JANE">
? 'weird-symbol
WEIRD-SYMBOL
? (in-package bob)
#<Package "BOB">
? (import 'jane::weird-symbol)
T
? (in-package jane)\\
#<Package "JANE">
? (unintern 'weird-symbol)
T
? (in-package bob)
#<Package "BOB">
? 'weird-symbol
WEIRD-SYMBOL
? (symbol-package 'weird-symbol) NIL
? (in-package jane)
#<Package "JANE">
? 'bob::weird-symbol\\
#:WEIRD-SYMBOL
Such things are best avoided.
** 5. Export and Use-package
Suppose Jane and Bob are collaborating on a software development project, each
working in their own package to avoid conflicts. Jane writes:
? (in-package jane)
#<Package "JANE">
? (defclass jane-class () (slot1 slot2 slot3))\\
#<STANDARD-CLASS JANE-CLASS>
Now, imagine that Bob wants to use JANE-CLASS. He writes:
? (in-package bob)
#<Package "BOB">
? (import 'jane::jane-class)
T
? (make-instance 'jane-class)\\
#<JANE-CLASS #x130565E>
?
So far so good. Now he tries:
? (setq jc1 (make-instance 'jane-class))
#<JANE-CLASS #x130BA96>
? (slot-value jc1 'slot1)
> Error: #<JANE-CLASS #x130BA96> has no slot named SLOT1.
> While executing: #<CCL::STANDARD-KERNEL-METHOD SLOT-MISSING (T T T T)>
What happened? Well, JANE-CLASS was defined in the JANE package, so the slot
names are symbols interned in that package. But Bob tried to access an instance
of that class using a symbol interned in the BOB package. In other words, JANE-
CLASS has a slot named JANE::SLOT1, and Bob tried to access a slot name
BOB::SLOT1, and there is no such slot.
What Bob would really like to do is import all the symbols that are
“associated” with JANE-CLASS, that is, all the slot names, method names, etc.
etc. How can he know what all those symbols are? He could examine Jane's code
and try to figure it out for himself, but that would lead to many problems, not
least of which is that he might decide to import a symbol that Jane didn't want
Bob to mess with (remember, separating Jane's symbols from the effects of Bob's
meddling is the whole point of having packages to being with).
A better solution would be for Jane to assemble a list of symbols that Bob
should import in order to use her software. Then Jane could do something like:
? (defvar *published-symbols* '(jane-class slot1 slot2 slot3))\\
*PUBLISHED-SYMBOLS*
?
And Bob could then do (import jane::*published-symbols*).
Common Lisp provides a standard mechanism for doing this. Every package
maintains a list of symbols that are intended to be used by other packages.
This list is called the exported symbol list of that package, and to add a
symbol to that package you use the function EXPORT. To remove a symbol from a
package's exported symbol list you use (as you might expect this time)
UNEXPORT. To import all of the exported symbols in a package you call
USE-PACKAGE. To unimport them all you call UNUSE-PACKAGE.
There are two things to note about exporting symbols.
First, a symbol can be exported from any package in which that symbol is
interned, not just its home package. A symbol also does not have to be exported
from its home package in order to be exported from some other package in which
that symbol is interned.
Second, a symbol that is exported from its home package uses only one colon
instead of two when printed with its package qualifier. This is to alert you to
the fact that the symbol is exported from its home package (and so you might
want to USE-PACKAGE the symbol's package instead of IMPORTing the symbol), and
also to discourage you from using unexported symbols by forcing you to type an
extra colon in order to access them. (No, I am not kidding.)
** 6. Shadowing
There is one last gotcha: using USE-PACKAGE is slightly different from
IMPORTing all the exported symbols in the package. When you IMPORT a symbol you
can undo the effect of the IMPORT using UNINTERN. You cannot use UNINTERN to
(partially) undo the effect of a USE-PACKAGE. For example:
? (in-package jane)
#<Package "JANE">
? (export '(slot1 slot2 slot3))
T
? (in-package bob)\\
#<Package "BOB">
? (use-package 'jane)
T
? (symbol-package 'slot1)\\
#<Package "JANE">
? (unintern 'slot1)
NIL
? (symbol-package 'slot1)\\
#<Package "JANE">
?
This is a problem, because it would seem to make it impossible to use two
different packages that export a symbol with the same name. For example,
suppose you wanted to use two packages P1 and P2 in a third package named
MYPACKAGE, and both P1 and P2 exported a symbol named X. If you just tried to
USE-PACKAGE both P1 and P2 you would get a name conflict because Lisp would
have no way of knowing whether X in MYPACKAGE should now be P1:X or P2:X.
To resolve such name conflicts every package maintains what is called a
shadowing symbols list. The shadowing symbols list is a list of symbols that
shadow or override any symbols would normally become visible in that package as
a result of calling USE-PACKAGE.
There are two ways to add symbols to a package's shadowing symbols list, SHADOW
and SHADOWING-IMPORT. SHADOW is used to add symbols that are in the package to
the shadowing symbols list. SHADOWING-IMPORT is used to add symbols that are in
some other package.
For example:
? (in-package p1)\\
#<Package "P1">
? (export '(x y z))
T
? (in-package p2)\\
#<Package "P2">
? (export '(x y z))
T
? (in-package bob)
T
? (use-package 'p1)
T
? (use-package 'p2)
> Error: Using #<Package "P2"> in #<Package "BOB">
> would cause name conflicts with symbols inherited\\
> by that package:
> Z P2:Z
> Y P2:Y\\
> X P2:X\\
. . .
1 >
Aborted
? (unuse-package 'p1)
T
? (shadow 'x)
T
? (shadowing-import 'p1:y)
T
? (shadowing-import 'p2:z)
T
? (use-package 'p1)
T
? (use-package 'p2)
T
? (symbol-package 'x)\\
#<Package "BOB">
? (symbol-package 'y)\\
#<Package "P1">
? (symbol-package 'z)\\
#<Package "P2">
?
To undo the effect of SHADOW or SHADOWING-IMPORT use UNINTERN.
Note that UNINTERN (and many, many other surprising things) can result in name
conflicts unexpectedly appearing. The most common cause of an unexpected name
conflict is usually inadvertently interning a symbol in a package by typing it
at the reader without paying close attention to which package you were in at
the time.
** 7. DEFPACKAGE
Now that you've learned all about the myriad functions and macros that can be
used to manipulate packages you shouldn't really be using any of them. Instead,
all of the functionality of IMPORT, EXPORT, SHADOW, etc. is all rolled up in a
single macro called DEFPACKAGE, which is what you should use for real (non-
prototype) code.
I'm not going to try to explain DEFPACKAGE here because now that you understand
the basic concepts of packages you should just be able to read the
documentation in the hyperspec and understand it. (There are also a lot of
other goodies, like DO-SYMBOLS and WITH-PACKAGE-ITERATOR, in the hyperspec that
you should now be able to understand on your own.)
One caveat about using DEFPACKAGE though: note that most of the arguments to
DEFPACKAGE are string designators, not symbols. This means that they can be
symbols, but if you choose to use symbols then those symbols will get interned
in the current package, whatever it may be, at the time that the DEFPACKAGE
form is
read. This often leads to undesirable consequences, so it's a good idea to get
into the habit of using keywords or strings in your DEFPACKAGE forms.
** 8. Final Thoughts
The most important thing to understand about packages is that they are
fundamentally a part of the Lisp reader and not the evaluator. Once you wrap
your brain around that idea, everything else will fall into place. Packages
control how the reader maps strings onto symbols (and how PRINT maps symbols
onto strings), nothing else. In particular, packages have nothing to do with
the functions, values, property lists, etc. that might or might not be
associated with any particular symbol.
Note in particular (this has nothing to do with packages but newcomers often
get confused by this anyway) that both symbols and function objects can be used
as functions, but they behave slightly differently. For example:
? (defun foo () "Original foo")
FOO
? (setf f1 'foo)
FOO
? (setf f2 #'foo)
#<Compiled-function FOO #xEB3446>
? (defun demo ()
(list (funcall f1) (funcall f2)
(funcall #'foo) (funcall #.#'foo)))
;Compiler warnings :
; Undeclared free variable F1, in DEMO.
; Undeclared free variable F2, in DEMO.
DEMO
? (demo)
("Original foo" "Original foo" "Original foo" "Original foo")\\
? (defun foo () "New foo")
FOO
? (demo)
("New foo" "Original foo" "New foo" "Original foo")
?
In this example we have two variables, F1 and F2. The value of F1 is the symbol
FOO. The value of F2 is the function object that was in the symbol-function
slot of the symbol FOO at the time F2 was assigned.
Note that when FOO is redefined, the effect of calling the symbol FOO is to get
the new behavior, whereas calling the function object produces the old
behavior.
Explaining the second and third result in the list is left as an exercise for
the reader (no pun intended).