srt@maui.cs.ucla.edu (Scott Turner) (02/03/90)
[Using Lucid Common Lisp on an Apollo Workstation.] I'm trying to build a macro to create ``rules''. A rule has some named components which get turned into lambdas and then later apply-ed by an interpreter. So, for example: (rule:define a-rule-name (test (eql *spec* 'hyper)) (action (cons *spec* 'foo))) In this example, "*spec*" is a parameter that will be passed in to each of the various parts when executed. For example, test gets turned into: (lambda (*spec*) (eql *spec* 'hyper)) To do this, I write a simple defmacro, the important part of which looks like this: `(setf (structure-test foo) #'(lambda (*spec*) ,@test-part)) Let's say that I define this macro in the RULE package. Later on, I go to use this in the USER package, and looking at the macro-expansion I see this: (lambda (rule::*spec*) (eql *spec* 'hyper)) Not what I expected. What's happened? Well, during the reading of the list that forms the backquote structure in my macro, the reader found "*spec*" and turned that into an atom in the RULE package, hence rule::*spec*. What's the solution? I have to delay the de-reference of the symbol until the macro is actually expanded. I came up with this: `(setf (structure-test foo) #'(lambda (,(intern "*SPEC*")) ,@test-part)) Which (surprise!) works, but which I find pretty ugly and disgusting. So, is there a better way to do this? Or is my whole approach wrong-minded? Scott R. Turner UCLA Computer Science "If you act like a dumbshit, they'll treat you like an equal." Domain: srt@cs.ucla.edu
barmar@think.com (Barry Margolin) (02/03/90)
In article <31480@shemp.CS.UCLA.EDU> srt@maui.cs.ucla.edu (Scott Turner) writes: >What's the solution? I have to delay the de-reference of the symbol until >the macro is actually expanded. I came up with this: > `(setf (structure-test foo) > #'(lambda (,(intern "*SPEC*")) ,@test-part)) >Which (surprise!) works, but which I find pretty ugly and disgusting. >So, is there a better way to do this? Or is my whole approach wrong-minded? I have a couple of ideas. 1. Require the users of your package to (import 'rule::*spec*). 2. Don't automatically bind the variable *spec*. Allow the macro user to specify his variable. I think this is a good idea regardless of the package problem. If you need the special variable bound in order to communicate among internal routines of the Rule system, bind it using LET and then pass the value to the functions created by the macro. The only problem with this is that if the user writes (setq <his-spec-variable> <new-value>) it won't change *spec*. Symbolics Genera has some facilities that work like your macro; for instance, their ADVISE macro uses the variables ARGLIST and VALUES similarly. Their solution is a variant on #1 above -- these symbols are external in the COMMON-LISP package (VALUES is a CL function name, and ARGLIST is a CL declaration name), and most user programs are in packages that inherit from COMMON-LISP, so they get the right symbol. However, I wouldn't suggest this solution for your problem. -- Barry Margolin, Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
eliot@phoenix.Princeton.EDU (Eliot Handelman) (02/03/90)
In article <31480@shemp.CS.UCLA.EDU> srt@maui.cs.ucla.edu (Scott Turner) writes:
;
; `(setf (structure-test foo)
; #'(lambda (*spec*) ,@test-part))
;
;Let's say that I define this macro in the RULE package. Later on, I go to
;use this in the USER package, and looking at the macro-expansion I see
;this:
;
; (lambda (rule::*spec*) (eql *spec* 'hyper))
Well, if *spec* is special, as I maybe was your intention from using the
stars, then you don't need the lambda list at all, so that's one solution.
A second is to let the rule specify its parameters, something like this:
(defmacro defrule (name params . test-part)
`(setf (structure-test ',name)
#'(lambda ,params ,@test-part)))
Then you say:
(rule::defrule big-rule (x)
(:test (eql x 'foo))
(:action (cons x 'foo)))
It might eventually be handy to generalize in this way, supposing you decide
that the rules are interested in more than one parameter.
Humbly offered,
--Eliot Handelman
Princeton U., Music
lou@atanasoff.rutgers.edu (Lou Steinberg) (02/06/90)
In article <31480@shemp.CS.UCLA.EDU> srt@maui.cs.ucla.edu (Scott Turner) writes: > [...] I write a simple defmacro, the important part of which looks > like this: > > `(setf (structure-test foo) > #'(lambda (*spec*) ,@test-part)) > > Let's say that I define this macro in the RULE package. Later on, I go to > use this in the USER package, and looking at the macro-expansion I see > this: > > (lambda (rule::*spec*) (eql *spec* 'hyper)) > > What's the solution? I have to delay the de-reference of the symbol until > the macro is actually expanded. I came up with this: > > `(setf (structure-test foo) > #'(lambda (,(intern "*SPEC*")) ,@test-part)) Even this is not guaranteed to work - it depends on the current package being the same at macro expand time as it was when the macro call was read. On some systems, the interpreter expands macros at macro call time, when the package may be different. To really do it right, you need to search the value of test-part for any symbol whose print-name is "*SPEC*", and use that symbol for your lambda variable. Of course, this means different rules may be using different lambda variables, if they were read in under different packages. A possibly cleaner solution is to find any symbol in test-part that has a print-name of *SPEC* and replace it with YOUR symbol *SPEC*. `(setf (structure-test foo) #'(lambda (*SPEC*) ,@(subst-if '*SPEC* #'(lambda (x) (and (symbol x) (string-equal (symbol-name x) "*SPEC*")))n test-part))) (Warning - the above is not debugged.) -- Lou Steinberg uucp: {pretty much any major site}!rutgers!aramis.rutgers.edu!lou arpa: lou@cs.rutgers.edu
dan@meritaus.UUCP (Daniel Haug) (02/06/90)
From article <31480@shemp.CS.UCLA.EDU>, by srt@maui.cs.ucla.edu (Scott Turner): > Let's say that I define this macro in the RULE package. Later on, I go to > use this in the USER package, and looking at the macro-expansion I see > this: > > (lambda (rule::*spec*) (eql *spec* 'hyper)) > > Not what I expected. What's happened? Well, during the reading of the list > that forms the backquote structure in my macro, the reader found "*spec*" and > turned that into an atom in the RULE package, hence rule::*spec*. > If I were writing this, and wanted to have some lexical variables available to the user of RULE, I would export these symbols from RULE. Then the user of the RULE system would either have to USE the RULE package, or explicitly reference the variables with a RULE: prefix: So, your rule system would define this: (in-package :rule) (export '(define *spec*) :rule) and the user would do the following from his/her own package (a.k.a USER): (use-package :rule) (define some-rule () (eql *SPEC* 'hyper) ...) Also humbly offered... -- dan haug ==================================================================== Phonenet: (512)338-2450 Internet: execu!sequoia!meritaus!dan@cs.utexas.edu UUCP: {uunet, cs.utexas.edu!execu, texbell}!sequoia!meritaus!dan ``When all you have is a hammer, everything begins to look like a nail.''
jwz@teak.berkeley.edu (Jamie Zawinski) (02/06/90)
In article <Feb.5.11.39.45.1990.3151@atanasoff.rutgers.edu> lou@atanasoff.rutgers.edu (Lou Steinberg) writes: > > [...] To really do it right, you need to search the value of test-part for > any symbol whose print-name is "*SPEC*", and use that symbol for your lambda > variable. [...] > > A possibly cleaner solution is to find any symbol in test-part that > has a print-name of *SPEC* and replace it with YOUR symbol *SPEC*. > > `(setf (structure-test foo) > #'(lambda (*SPEC*) > ,@(subst-if '*SPEC* > #'(lambda (x) (and (symbol x) > (string-equal (symbol-name x) > "*SPEC*")))n > test-part))) > I think one of the biggest flaws with the Common Lisp package system is that it is confusing. In fact, it is so confusing, that sometimes people get the notion that evil hacks like the above are "right" or "clean." Don't try to circumvent the namespace hierarchy like this; you *will* lose, eventually. Come at it from the other direction - instead of bashing the code so that the symbols are the same, manipulate the hierarchy of your packages so that the symbols come out equivalent automatically. 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. It will make your life much happier. -- Jamie
lou@atanasoff.rutgers.edu (Lou Steinberg) (02/06/90)
In article <21779@pasteur.Berkeley.EDU> jwz@teak.berkeley.edu (Jamie Zawinski) writes: > In article <Feb.5.11.39.45.1990.3151@atanasoff.rutgers.edu> lou@atanasoff.rutgers.edu (Lou Steinberg) writes: > > > > A possibly cleaner solution is [...] ^^ > > I think one of the biggest flaws with the Common Lisp package system is that > it is confusing. In fact, it is so confusing, that sometimes people get the > notion that evil hacks like the above are "right" or "clean." Please note the indicated 2 characters above. > 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. It will make your life much > happier. You are right, but I suspect that Mr. Turner *thought* he understood the package system when he started, as you and I no doubt both *think* we understand it even though (due to the complexity) there may well be gotcha's lurking for us as well. -- Lou Steinberg uucp: {pretty much any major site}!rutgers!aramis.rutgers.edu!lou arpa: lou@cs.rutgers.edu