[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

writeup on name conflicts



@section[Name Conflicts]

A fundamental invariant of the package system is that within one package
any particular name can only refer to one symbol.  A @i[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, then additional programs or data are read, 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 different mapping from names to symbols than was expected by
the user, causing programs to execute incorrectly.  Therefore, any time
a name conflict occurs, an error is signalled.  The user may continue
from the error and tell the package system how to resolve the conflict.

Note that if the same symbol is accessible to a package through more than
one path, for instance as an external of more than one package, or both
through inheritance and through direct presence in the package, there is
no name conflict.  Name conflicts only occur 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 @i[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 signalling an error
(except for one exception involving @b[import] described below).  The
functions @b[shadow] and @b[shadowing-import] may be used to declare
shadowing symbols.

Name conflicts are detected when they become possible, i.e. when the
package structure is altered.  There is no need to check for name
conflicts during every name lookup.

The functions @b[use-package], @b[import], and @b[export] check for name
conflicts.  @b[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.
@b[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.  @b[import] signals an error even if there
is a name conflict with a shadowing symbol, because two explicit
directives from the user are inconsistent.  @b[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:  @b[(export @i[s] @i[p])] does
@b[(find-symbol (symbol-name @i[s]) @i[q])] for each package @i[q]
in @b[(package-used-by-list @i[p])].  Note that in the usual case of
an @b[export] during the initial definition of a package, the
@b[package-used-by-list] will be @b[nil] and the name conflict checking
will take no time.

@b[intern] 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.

@b[shadow] and @b[shadowing-import] never signal a name-conflict error,
because by calling these functions the user has specified how any
possible conflict is to be resolved.  @b[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.  @b[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 @b[unintern]ed if it was
directly present in the package.

@b[unuse-package], @b[unexport], and @b[unintern] (when the symbol being
@b[unintern]ed 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.

@b[unintern] of a shadowing symbol 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 @b[x], and B and C each contain external
symbols named @b[x], then @b[unintern]ing @b[x] from A will reveal a name
conflict between @b[b:x] and @b[c:x] if those two symbols are distinct.
In this case @b[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.  Note: when multiple changes are to be
made, for example when @b[export] is given a list of symbols, it is legal
for each change to be processed 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 @b[export] of a single symbol will not leave that symbol accessible
to some packages and inaccessible to others; @b[export]
appears as 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 @b[shadowing-import], @b[unintern], or @b[unexport].

A name conflict in @b[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 @b[unintern]ing the first symbol from the using
package.  The latter resolution is dangerous if the symbol to be
@b[unintern]ed is an external symbol of the using package since it
will cease to be an external symbol.

A name conflict in @b[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 @b[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 @b[unintern]ing the other one, or in favor of the already-present
symbol by making it a shadowing symbol.

A name conflict in @b[export] or @b[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
@b[use-package].

A name conflict in @b[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 @b[import].  A
name conflict in @b[import] with a symbol already present in the
package may be resolved by @b[unintern]ing that symbol, or by not
doing the @b[import].

Good user-interface style dictates that @b[use-package] and @b[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, requiring a lot of interaction but permitting
different conflicts to be resolved different ways.

Some implementations will have other ways of resolving name conflicts to
offer.  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.  On the Lisp machine one can
also @i[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, i.e. the fact that there are two non-@b[eq]
symbols with the same name doesn't affect the correct operation of
his 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 and start over, after correcting the package-definition forms
that caused the error.