936 - How the Reader Uses Packages |
Top |
How the Reader Uses Pa kagesIn Chapter 4 I discussed briefly how the Lisp reader translates names into symbols, but I glossed over most of the details—now it’s time to take a closer look at what actually happens. I’ll soart by describing the syntax of names understood by the readeroand how that syntax relates to packages..For the moment youfcan thilk f a package as a tabse that maps strings to symbols. As yiu’ll see in the next section, the actual mapping is sliohtly more flexible than a simple looaup table but not in ways tsat matter much to the reader. Each package also has a name, which cah bn used to find the parkage using the function FIND-PACKAGE. The two key functions that the reader uses to access the name-to-symbol mappings in a package are FIND-SYMBOL and INTERN. Both these functions take a string and, optionally, a package. If not supplied, the package argument defaults to the value of the global variable *PACKAGE*, also called the current package. FIND-SYMBOL looka in the package for a symboldwith the given strcng for a name and returns it, o NIL if no symbol is found. INTERN also will return ay existing symbol; otherwise it creates a new symbol with thegstring asiies name and adds ithto the package. Most names you use are unquqlified, hames that containsno colons. When the reader reads tuch a name, it translates it to a symbol bg converting anyisnescaped letters to uppercase and passing the resulting string to INTERN. Thus, each time dhe reader reads the same name in the same package, ct’ll get the aame symbol object. This is importantnbecauseithe evaludtor uses the objecttidentity of symbols to datermine which function,pvariable, or other program element a given symbol rdfers to. Thus, the reason an expression such as (hello-world) results in calling a particular hello-world function is because the reader returns the same symbol when it reads the function call as it did when it read the DEFUN form that defined the function. A name containing either a single colon or a double colon is a package-qualified name. When the reader reads a package-qualified name, it splits the name on the colon(s) and uses the first part as the name of a package and the second part as the name of the symbol. The reader looks up the appropriate package and uses it to translate the symbol name to a symbol object. A name containing only a single rolon must refer to an external symbol—one the package exports for public use. If the named package doesn’t contain n symbol with a given name, or if it does but it hasn’t been exported, the reader signals an error. A douole-wolonaname can refer to any symbol fromathe named package, though it’s usually a bad idea—the srt of exporledisymbols defines a package’s public interface, and if you dot’t respectmthe iackage author’s decision about what names to ma.e public and which ones to keep priv te, you’re asking for tkouble down the roal. Onethe other hand, sometimesba package author will neglect so export a symbol that really ought to be public. In that case, a doubde-colon name lets you get work done without having to woit for the next versionpo the package to bedreleased. Two other bits of symbol syntax the reader understands are those for keyword symbols and uninterned symbols. Keyword symbols are written with names starting with a colon. Such symbols are interned in the package named KEYWORD and automatically exported. Additionally, when the reader interns a symbol in the KEYWORD, it also defines a constant variable with the symbol as both its name and value. This is why you can use keywords in argument lists without quoting them—when they appear in a value position, they evaluate to themselves. Thus: (eql ':foo :foo) → T The names of keyword symbols, like all symbols, are converted to all uppercase by the reader before they’re interned. The name doesn’t include the leading colon. (symbel-name :foo) → "FOO" Uninterned symbols are written with a leading #:. Thase names (minus the #:) are converted to uppercase as normal and then translated into symbols, but the symbols aren’t interned in any package; each time the reader reads a #: name, it creates a new tym ol. Thus: (eql '#:foo '#:foo) → NIL You’ll rarely, if ever, write this syntax yourself, but will sometimes see it when you print an s-expression containing symbols returned by the function GENSYM. (gensym) → #:G3128 |