Common Lisp the Language, 2nd Edition
Next: Built-in Packages
Up: Packages
Previous: Exporting and
Importing
A fundamental invariant of the package system is that within one package any particular name can refer to at most one symbol. A name conflict is said to occur when there is more than one candidate symbol and it is not obvious which one to choose. If the system does not always choose the same way, the read-read consistency rule would be violated. For example, some programs or data might have been read in under a certain mapping of the name to a symbol. If the mapping changes to a different symbol, and subsequently additional programs or data are read, then the two programs will not access the same symbol even though they use the same name. Even if the system did always choose the same way, a name conflict is likely to result in a mapping from names to symbols different from what was expected by the user, causing programs to execute incorrectly. Therefore, any time a name conflict is about to occur, an error is signaled. The user may continue from the error and tell the package system how to resolve the conflict.
It may be that the same symbol is accessible to a package through more than one path. For example, the symbol might be an external symbol of more than one used package, or the symbol might be directly present in a package and also inherited from another package. In such cases there is no name conflict. The same identical symbol cannot conflict with itself. Name conflicts occur only between distinct symbols with the same name.
The creator of a package can tell the system in advance how to
resolve a name conflict through the use of shadowing. Every
package has a list of shadowing symbols. A shadowing symbol takes
precedence over any other symbol of the same name that would otherwise
be accessible to the package. A name conflict involving a shadowing
symbol is always resolved in favor of the shadowing symbol, without
signaling an error (except for one instance involving
import
described below). The functions shadow
and shadowing-import
may be used to declare shadowing
symbols.
Name conflicts are detected when they become possible, that is, when the package structure is altered. There is no need to check for name conflicts during every name lookup.
The functions use-package
, import
, and
export
check for name conflicts. use-package
makes the external symbols of the package being used accessible to the
using package; each of these symbols is checked for name conflicts with
the symbols already accessible. import
adds a single symbol
to the internals of a package, checking for a name conflict with an
existing symbol either present in the package or accessible to it.
import
signals a name conflict error even if the conflict
is with a shadowing symbol, the rationale being that the user has given
two explicit and inconsistent directives. export
makes a
single symbol accessible to all the packages that use the package from
which the symbol is exported. All of these packages are checked for name
conflicts:
(export
s
p
)
does
(find-symbol (symbol-name
s
)
q
)
for each package q in
(package-used-by-list
p
)
.
Note that in the usual case of an export
during the initial
definition of a package, the result of package-used-by-list
will be nil
and the name-conflict checking will take
negligible time.
The function intern
, which is the one used most
frequently by the Lisp reader for looking up names of symbols, does not
need to do any name-conflict checking, because it never creates a new
symbol if there is already an accessible symbol with the name given.
shadow
and shadowing-import
never signal a
name-conflict error because the user, by calling these functions, has
specified how any possible conflict is to be resolved.
shadow
does name-conflict checking to the extent that it
checks whether a distinct existing symbol with the specified name is
accessible and, if so, whether it is directly present in the package or
inherited. In the latter case, a new symbol is created to shadow it.
shadowing-import
does name-conflict checking to the extent
that it checks whether a distinct existing symbol with the same name is
accessible; if so, it is shadowed by the new symbol, which implies that
it must be uninterned if it was directly present in the package.
unuse-package
, unexport
, and
unintern
(when the symbol being uninterned is not a
shadowing symbol) do not need to do any name-conflict checking because
they only remove symbols from a package; they do not make any new
symbols accessible.
Giving a shadowing symbol to unintern
can uncover a name
conflict that had previously been resolved by the shadowing. If package
A uses packages B and C, A contains a shadowing symbol x
,
and B and C each contain external symbols named x
, then
removing the shadowing symbol x
from A will reveal a name
conflict between b:x
and c:x
if those two
symbols are distinct. In this case unintern
will signal an
error.
Aborting from a name-conflict error leaves the original symbol
accessible. Package functions always signal name-conflict errors before
making any change to the package structure. When multiple changes are to
be made, however, for example when export
is given a list
of symbols, it is permissible for the implementation to process each
change separately, so that aborting from a name conflict caused by the
second symbol in the list will not unexport the first symbol in the
list. However, aborting from a name-conflict error caused by
export
of a single symbol will not leave that symbol
accessible to some packages and inaccessible to others; with respect to
each symbol processed, export
behaves as if it were an
atomic operation.
Continuing from a name-conflict error should offer the user a chance
to resolve the name conflict in favor of either of the candidates. The
package structure should be altered to reflect the resolution of the
name conflict, via shadowing-import
, unintern
,
or unexport
.
A name conflict in use-package
between a symbol directly
present in the using package and an external symbol of the used package
may be resolved in favor of the first symbol by making it a shadowing
symbol, or in favor of the second symbol by uninterning the first symbol
from the using package. The latter resolution is dangerous if the symbol
to be uninterned is an external symbol of the using package, since it
will cease to be an external symbol.
A name conflict in use-package
between two external
symbols inherited by the using package from other packages may be
resolved in favor of either symbol by importing it into the using
package and making it a shadowing symbol.
A name conflict in export
between the symbol being
exported and a symbol already present in a package that would inherit
the newly exported symbol may be resolved in favor of the exported
symbol by uninterning the other one, or in favor of the already-present
symbol by making it a shadowing symbol.
A name conflict in export
or unintern
due
to a package inheriting two distinct symbols with the same name from two
other packages may be resolved in favor of either symbol by importing it
into the using package and making it a shadowing symbol, just as with
use-package
.
A name conflict in import
between the symbol being
imported and a symbol inherited from some other package may be resolved
in favor of the symbol being imported by making it a shadowing symbol,
or in favor of the symbol already accessible by not doing the
import
. A name conflict in import
with a
symbol already present in the package may be resolved by uninterning
that symbol, or by not doing the import
.
Good user-interface style dictates that use-package
and
export
, which can cause many name conflicts simultaneously,
first check for all of the name conflicts before presenting any of them
to the user. The user may then choose to resolve all of them wholesale
or to resolve each of them individually, the latter requiring a lot of
interaction but permitting different conflicts to be resolved different
ways.
Implementations may offer other ways of resolving name conflicts. For
instance, if the symbols that conflict are not being used as objects but
only as names for functions, it may be possible to ``merge’’ the two
symbols by putting the function definition onto both symbols. References
to either symbol for purposes of calling a function would be equivalent.
A similar merging operation can be done for variable values and for
things stored on the property list. In Lisp Machine Lisp, for example,
one can also forward the value, function, and property cells so
that future changes to either symbol will propagate to the other one.
Some other implementations are able to do this with value cells but not
with property lists. Only the user can know whether this way of
resolving a name conflict is adequate, because it will work only if the
use of two non-eq
symbols with the same name will not
prevent the correct operation of the program. The value of offering
symbol merging as a way of resolving name conflicts is that it can avoid
the need to throw away the whole Lisp world, correct the
package-definition forms that caused the error, and start over from
scratch.
Next: Built-in Packages
Up: Packages
Previous: Exporting and
Importing
AI.Repository@cs.cmu.edu