1-1. Understanding the Role of Packages and the Symbol Nomenclature
Problem
Symbols can be accessible , inherited , internal , present , and so on. They have a home package but can live in several different packages at the same time or in none at all. This can all be pretty confusing at first, but it is vital to develop a good understanding of how packages and symbols interact in order to get the finer points.
How It Works
We now have a situation that can be depicted graphically, like so:
Each of the four large boxes above is a package; the smaller rectangles denote symbols. We see the three packages, P1 through P3 , which we explicitly created, and the CL (or COMMON-LISP ) package, which is already there when we start our Lisp image and of whose many symbols we only show a sample of four.
A package is a container of symbols (thats why they are drawn as boxes holding the symbol rectangles); and it is probably fair to say that the main job of packages is to help us find symbols and to organize them into groups. This can also be put differently: packages can help us name and find symbols, but symbols can have an existence on their own; that is, without packages. (As well see in Recipes .)
Symbols are usually created automatically (by the Lisp reader; see ) because it creates a new internal symbol of the package.
Of course, most of the time symbols are not created because, when it reads them, the Lisp system firsts checks if they are already there; that is, whether they are accessible using the name and (implicit or explicit) package prefix you provided. So, in the following situation (assuming you just started your Lisp image)
when your Lisp reads foo it is as if it had read cl-user::foo because (as the prompt shows) the CL-USER package is the current package. Because no symbol with the name "FOO" is accessible via the package CL-USER , a new symbol (with that name and in that package) is created (viz. interned into the package).
When your Lisp now reads foo again , a symbol described by this sequence of characters is available (the one that was just created) and is returned.
In our example code from page 2 we didnt create any symbols this way but we created them implicitly or explicitly through the DEFPACKAGE forms. Lets see what we got this way:
Symbols are either present in a package or not. In our picture, the symbols present in a package are those that are contained within the surrounding box.
Symbols become present in a package either if a new symbol is interned (see above) or if an existing symbol is imported .
The DEFPACKAGE form for P3 , for example, explicitly interned a symbol named ALPHA and explicitly imported the symbol named DELTA from the P2 package.
When a symbol is imported into a package, the effect is that the same symbol is now present in (at least) two different packages. In the picture, we used dashed lines to show identity; that is, symbols connected with dashed lines are actually the same symbol and symbols not connected are really different symbols.
Once a symbol is present in a package, it can only be removed using the function UNINTERN (see Recipe ).
If a symbol is present in a package, it can be exported from that package. This means that it can now be accessed using only one colon between the packages and the symbols name. Exported symbols are shown with a gray background in our picture.
For example, the package P1 exports the symbol CHARLIE , but not the symbol ALPHA . We didnt explicitly intern CHARLIE , but because we instructed the system to export it, it had to be present and was thus created (and interned) automatically.
Exported symbols are called external and the other ones are called internal .
A package can also have inherited symbols. This happens if a package uses another package. All external symbols of the used package become inherited symbols of the package using this package. Inherited symbols are not shown directly in our picture but rather by means of the arrows on the right that show which packages use which other packages.
For example, package P3 uses package P2 and thus P2:ECHO is an inherited symbol of P3 . Likewise, P3 also uses CL and thus all of the 978 exported symbols of CL (see footnote on page 19) become inherited symbols of P3 . You can thus refer to P2:ECHO as P3::ECHO and to CL:ABS as P3::ABS .
Inheritance is not transitive. The fact that P3 uses P2 and P2 in turn uses P1 does not mean that the symbol P1:CHARLIE is an inherited symbol of P3 .
P3 has its own symbol named CHARLIE , which was implicitly created because it was exported, but this symbol is different from P1:CHARLIE .
A symbol is accessible in a package (in other words, it can be found through that package) if it is either present or inherited in that package.
As accessibility means that a symbol can be found in a package by name, it implies that no two different accessible symbols can have the same name. This would be called a name conflict . To provoke such a conflict, try something like this: