937 lines
24 KiB
Org Mode
937 lines
24 KiB
Org Mode
* 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).
|