In article <·····@shemp.CS.UCLA.EDU> ···@maui.cs.ucla.edu (Scott Turner) writes:
> (defmacro rule::define (...)
> `(progn
> (import 'rule::*spec*)
> ... ))
>
> This seems to work, and has the advantage of relieving the user of having
> to deal with such things. (Which of course is the whole point of the macro.)
>
> Similarly, several people suggested making the user type out "rule::*spec*"
> but I don't consider that an acceptable interface.
Actually, I doubt this would work if loaded into a fresh environment. Consider
what happens when you type the form
(rule:define rule-name
(test (list? *spec*))
(body (car *spec*)))
First it is read, producing a list. In this list are the symbols RULE::DEFINE
and *SPEC* (in the current package), among others. Next, this list is
evaluated. Since the car of the list is a macro, the macroexpansion function
is called to produce another list to evaluate instead. This would be
something like
(progn (import 'rule::*spec*)
...
(lambda (*spec*) (list? *spec*))
...)
Since the call to IMPORT is a part of the macroexpansion output, the
symbol-names have already been resolved; that happens at read-time.
Even before the above form is evaluated, there is a symbol called "*SPEC*" in
the current package. When you call IMPORT to drag the symbol called "*SPEC*"
from the RULE package into the current package, you will get a name-conflict
error.
Rather than trying to change the package hierarchy at run-time, set it up
beforehand. At the front of the file which defines the rule system itself,
add these forms:
(in-package "RULE")
(export '(rule::define rule::*spec*) "RULE")
and in the package which defines rules, add a form like
(in-package "RULE-USER" :use '("LISP" "RULE"))
This says that the rule package exports two symbols as its interface to the
outside world: DEFINE and *SPEC*. Packages which :USE the rule package
will inherit those two symbols as internal; that is, the symbols
RULE-USER::*SPEC* and RULE::*SPEC* will be the same. They are not just
equivalent, they different names for the same symbol. this will let the
code's users type
(define rule-name
(test (list? *spec*))
(body (car *spec*)))
and get the right versions of both *SPEC* and DEFINE.
It is very easy to make the package state inconsistent; since simply typing
a symbol causes it to be created and interned if it does not exist in the
current package, if, before creating the RULE-USER package, you happened to
say something like
(in-package "RULE-USER")
'*spec* ; <--- any mention of this symbol
then the form
(in-package "RULE-USER" :use '("LISP" "RULE"))
will no longer work, because the RULE-USER package is now in an incompatible
state. This is why it's usually a good idea to set up the package hierarchy
the way you want it, and then not touch it again. When you have code that
alters the package state at run-time (like the define-rule macro quoted at
the top of this message) you can get into some strange, hard to reproduce,
hard to debug situations.
>> If you don't feel like you fully understand the package system, don't use
>> it. Put all of your code in one package until you do.
>
> Which is probably good advice, though I wonder how one is to learn about the
> package system without trying out things.
You are, of course, absolutely right. I hope this message was a little less
snide. :-)
-- Jamie