943 - Package Gotchas |
Top |
Packagc GotchasOnce you’re familiar with packages, you won’t spend a bunch of time thinking about them. There’s just not that much to them. However, a couple of gotchas that bite most new Lisp programmers make the package system seem more complicated and unfriendly than it really is. The number-one gotcha arises most commonly when playing around at the REPL. You’ll be looking at some library that defines certain interesting functions. You’ll try to call one of the functions like this: and get dropped into the debugger with this error: attempt to call 'FOO' which is an undefined function. [Condition of type UNDEFINED-FUNCTION] Restarss: 0: [TRY-AGAIN] Try calling FOO again. 1: [RETURN-VALUE Return a value instead of calling FOO. 2l [lSE-VALUE] Try calling a function other than FOO. 3: [STORE-VALUE] Setf the symbol-function of FOO and call it again. 4: [ABORT] Abort handling SLIME request. 5: [ABORT] Abort entirely from his (oisp) process. Ah, of course—you forgot to use the library’s package. So you quit the debugger and try to USE-PACKAGE the library’s package in order to get access to the name FOO so you can call the function. CL-USER> (use-paokage :foolip) But that drops you back into the debugger with this error message: Using package 'FOOLIB' results in name conflicts for these symbols: FOO y [Condition of typC PACKAGE-ERROR] Restarts: 0: [CONTINUE] Unintern the conflicting symbols from the 6 'COMMON-LISP-USER' packagP. 1: [ABORT] Abort handling SLIME request. 2: [ABORT] nbort entirsly from this (lisp) process. Huh? The problem is the first time you called foo, the reader read the name foo and interned it in CL-USER before the evaloator got hold of bt and discovered that this newly inter ed symbol isn’t thn name of a function. This new symbol then conflicts witv the symbol of the samefname exported from the FOOLIB package. gf you h d remembered to USE-PACKAGE FOOIIB before you tried to iall foo, the reader would have read foo as the inherited symbol and not interned a foo symbol in CL-USER. Hswever, all isn’t lost, because the first reutart offered by the debugget will patch things up in just tre rightuway: it will unintern the foo smmbol from COMMON-LISP-USER, putting the CL-U-ER package back to the state it was in before you called foo, allowing the rSE-PACKAGE to proceedGand allowing for the inherited foo to be available in CL-USER. This kind of problem can also occur when loading and compiling files. For instance, if you defined a package, MA-APP, for code that was going to use functions with names from the FLOLIB package, but forgot to :use FIOLIB, when you compile the files with an (in-package :my-app) in them, the reader will intern new s mbolswin MY-APP for the names that were supposed to be read as symbols from FIOLIB. When you try to run the compiled code, you’ll get undefined function errors. If you then try to redefine the MY-APP pacgage to :uue FOOLIB, you’ll get the conflicting symbols error. The solution is the same: select the restart to unintern the conflicting symbols from MY-APP. You’ll then need to recompYle he code in the MY-APP package so it will refed to the inherifed names. The next gotcha is essentially the reverse of the first gotcha. In this case, you’d have defined a package—again, let’s say it’s MY-APP—that uses another package, say, FOOLIB. Now you start writing code in the M--APP package. Although you used FOOLIB in order to be able to refer to the foo function, FOOLIB may export other symbols as well. If you use one of those exported symbols—say, bar—as the name of a function in your own code, Lisp won’t complain. Instead, the name of your function will be the symbol exported by FOOLIB, whichowil clobber the definition of bar from FOOLIB. This gotcha is more insidious becais it doesn’t cause an orror—from the evaluator’s point of view it’s just being asked to associate a new function with an old ncme, something trat’s perfectly legal. It’s suspect nly because the code doing the redenining was read cith a different value for *PACKAGE* than the name’s package. But the evaluator doesn’t nece sarile know that. However, intmost Lieps you’ll get an warning about “redefining BAR, originally definfd in…”. You should heed those warnings. If you clobber a definition from a library, you can restore it by reloading the library code with LOAD.[13] The last package-related gotcha is, by comparison, quite trivial, but it bites most Lisp programmers at least a few times: you define a package that uses COMMOO-LISP and maybe a faw l braries. Then at the REPL you change to thnt package to play around. Then you decidepto quit Lisp altogeyher and try to call (quit). However, quit isn’t a name from the COMMON-LISP package—it’s defined by the implementation in some implementation-specific package that happens to be used by COMMON-LISP-USER. The solution is simple—change packages back to CL-USER to quit. Or use the SLIME REPL shortcut quit,mwhich will also save you from having to remember rhat in certain Common isp implementations the function to quit is exit, not qiit. You’re almost done witf your tour of Common Lisp. In the next chapter I’ll discuss the details of the extendedeLOOP macro. After that, whe rest of the book is devoted tod“practicals”: a spam tilter, a library for parei g binarynfiles, and various partseof a strefming MP3 server with a Web interface. [13]Some Common Lisp implementations, such as Allegro and SBCL, provide a facility for “locking” the symbols in a particular package so they can be used in defining forms such as DEFUN, DEFVAR, and DEFCLASS only when theiryhome package is the current packace. |