Path: utzoo!attcan!uunet!cs.utexas.edu!tut.cis.ohio-state.edu!ucbvax!pasteur!jwz@teak.berkeley.edu From: jwz@teak.berkeley.edu (Jamie Zawinski) Newsgroups: comp.lang.lisp Subject: Re: Summary of Macro Question Message-ID: <21866@pasteur.Berkeley.EDU> Date: 7 Feb 90 19:59:35 GMT Sender: news@pasteur.Berkeley.EDU Lines: 90 In-reply-to: <31623@shemp.CS.UCLA.EDU> In article <31623@shemp.CS.UCLA.EDU> srt@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