A Bit of Package and Symbol Vocabulary
As I mentioned previously, the mapping from names to symbols implemented by a package is slightly more flexible than a simple lookup table. At its core, every package contains a name-to-symbol lookup table, but a symbol can be made accessible via an unqualified name in a given package in other ways. To talk sensibly about these other mechanisms, you’ll need a little bit of vocabulary.
To start with, all the symbols that can be found in a given package using **FIND-SYMBOL**
are said to be accessible in that package. In other words, the accessible symbols in a package are those that can be referred to with unqualified names when the package is current.
A symbol can be accessible in two ways. The first is for the package’s name-to-symbol table to contain an entry for the symbol, in which case the symbol is said to be present in the package. When the reader interns a new symbol in a package, it’s added to the package’s name-to-symbol table. The package in which a symbol is first interned is called the symbol’s home package.
The other way a symbol can be accessible in a package is if the package inherits it. A package inherits symbols from other packages by using the other packages. Only external symbols in the used packages are inherited. A symbol is made external in a package by exporting it. In addition to causing it to be inherited by using packages, exporting a symbol also—as you saw in the previous section—makes it possible to refer to the symbol using a single-colon qualified name.
To keep the mappings from names to symbols deterministic, the package system allows only one symbol to be accessible in a given package for each name. That is, a package can’t have a present symbol and an inherited symbol with the same name or inherit two different symbols, from different packages, with the same name. However, you can resolve conflicts by making one of the accessible symbols a shadowing symbol, which makes the other symbols of the same name inaccessible. In addition to its name-to-symbol table, each package maintains a list of shadowing symbols.
An existing symbol can be imported into another package by adding it to the package’s name-to-symbol table. Thus, the same symbol can be present in multiple packages. Sometimes you’ll import symbols simply because you want them to be accessible in the importing package without using their home package. Other times you’ll import a symbol because only present symbols can be exported or be shadowing symbols. For instance, if a package needs to use two packages that have external symbols of the same name, one of the symbols must be imported into the using package in order to be added to its shadowing list and make the other symbol inaccessible.
Finally, a present symbol can be uninterned from a package, which causes it to be removed from the name-to-symbol table and, if it’s a shadowing symbol, from the shadowing list. You might unintern a symbol from a package to resolve a conflict between the symbol and an external symbol from a package you want to use. A symbol that isn’t present in any package is called an uninterned symbol, can no longer be read by the reader, and will be printed using the #:foo
syntax.