Книга: Practical Common Lisp

Package Gotchas

Package Gotchas

Once 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:

CL-USER> (foo)

and get dropped into the debugger with this error:

attempt to call `FOO' which is an undefined function.
[Condition of type UNDEFINED-FUNCTION]
Restarts:
0: [TRY-AGAIN] Try calling FOO again.
1: [RETURN-VALUE] Return a value instead of calling FOO.
2: [USE-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 this (lisp) 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-package :foolib)

But that drops you back into the debugger with this error message:

Using package `FOOLIB' results in name conflicts for these symbols: FOO
[Condition of type PACKAGE-ERROR]
Restarts:
0: [CONTINUE] Unintern the conflicting symbols from the `COMMON-LISP-USER' package.
1: [ABORT] Abort handling SLIME request.
2: [ABORT] Abort entirely 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 evaluator got hold of it and discovered that this newly interned symbol isn't the name of a function. This new symbol then conflicts with the symbol of the same name exported from the FOOLIB package. If you had remembered to USE-PACKAGE FOOLIB before you tried to call foo, the reader would have read foo as the inherited symbol and not interned a foo symbol in CL-USER.

However, all isn't lost, because the first restart offered by the debugger will patch things up in just the right way: it will unintern the foo symbol from COMMON-LISP-USER, putting the CL-USER package back to the state it was in before you called foo, allowing the USE-PACKAGE to proceed and 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, MY-APP, for code that was going to use functions with names from the FOOLIB package, but forgot to :use FOOLIB, when you compile the files with an (in-package :my-app) in them, the reader will intern new symbols in MY-APP for the names that were supposed to be read as symbols from FOOLIB. When you try to run the compiled code, you'll get undefined function errors. If you then try to redefine the MY-APP package to :use 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 recompile the code in the MY-APP package so it will refer to the inherited 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 MY-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, which will clobber the definition of bar from FOOLIB.

This gotcha is more insidious because it doesn't cause an error—from the evaluator's point of view it's just being asked to associate a new function with an old name, something that's perfectly legal. It's suspect only because the code doing the redefining was read with a different value for *PACKAGE* than the name's package. But the evaluator doesn't necessarily know that. However, in most Lisps you'll get an warning about "redefining BAR, originally defined 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.[233]

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 COMMON-LISP and maybe a few libraries. Then at the REPL you change to that package to play around. Then you decide to quit Lisp altogether 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, which will also save you from having to remember that in certain Common Lisp implementations the function to quit is exit, not quit.

You're almost done with your tour of Common Lisp. In the next chapter I'll discuss the details of the extended LOOP macro. After that, the rest of the book is devoted to "practicals": a spam filter, a library for parsing binary files, and various parts of a streaming MP3 server with a Web interface.

Оглавление книги


Генерация: 1.194. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз