Erann Gat - Idiots Guide To Lisp Packages.pdf

(163 KB) Pobierz
The Complete Idiot’s Guide to Common Lisp
Packages
1
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:
? (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"
?
(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:
? (gentemp)
T1
? (setq x (gentemp))
T2
? (set x 123) ; Note the use of SET, not SETQ or SETF
123
? x
T2
? t2
123
?
(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
Zgłoś jeśli naruszono regulamin