From: David E. Young
Subject: Variable expansion with macros
Date: 
Message-ID: <3A2FDEDD.4EF5442D@nc.rr.com>
Greetings. I'm buliding a production-rule system and have encountered a
stumbling-block vis-a-vis macro expansion on rule right-hand-sides
(RHSs). It's probably best in this situation to start with an example;
so, let's consider the following rule:

(defrule nemesis
  (natasha (name "natasha") (nemesis ?nemesis "rocky"))
  (rocky (name ?nemesis))
  =>
  (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis))

Now, to fire the RHS I build a lexical context, at runtime, that
declares the variable ?NEMESIS and includes the FORMAT form; all of this
is wrapped in a LAMBDA expression, EVAL'd and eventually FUNCALL'd. Ok,
works great. Enter a new construct on the RHS. I now want to add support
for the following:

...
=>
(format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis)
(assert (nemesis-found (name ?nemesis)))

where ASSERT is a macro that parses a fact declaration and gives it to
the inference engine (NB: this macro shadows the CL symbol ASSERT). The
trouble I'm having is figuring out how to get the ?NEMESIS variable in
the ASSERT form properly bound. As implemented right now, ASSERT parses
the body and creates a FACT instance from the constituent parts; up
until now I've only tested ASSERT with literals.

The lexcal environment for the RHS (as implemented now) looks something
like this:

(lambda ()
   (let ((?nemesis "rocky"))
      (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis))

As I mentioned, the LET forms are built up at runtime and the action
forms are spliced in.

ASSERT is currently written as:

(defmacro assert ((&body body))
   `(parse-and-insert-fact ',body))

but now needs to change to accommodate variables in the fact
declaration. BTW, I thought about using SYMBOL-VALUE ,during the parsing
of the fact, for variables encountered; however, I see that SYMBOL-VALUE
cannot access the value of a lexical variable. Sigh.

In any event, I hope I've been clear enough. I'm probably just missing a
simple concept, but I've been wrapped up in other areas of the system
and am just drawing a blank. Thanks for the help.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike

From: Thomas A. Russ
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <ymi4s0fztm5.fsf@sevak.isi.edu>
"David E. Young" <·······@nc.rr.com> writes:
> The lexcal environment for the RHS (as implemented now) looks something
> like this:
> 
> (lambda ()
>    (let ((?nemesis "rocky"))
>       (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis))
> 
> As I mentioned, the LET forms are built up at runtime and the action
> forms are spliced in.
> 
> ASSERT is currently written as:
> 
> (defmacro assert ((&body body))
>    `(parse-and-insert-fact ',body))

Another alternative is to have the assert macro do something more like:

  (defmacro assert ((&body body))
    (parse-and-insert-fact ',body))

and then modify the PARSE-AND-INSERT-FACT function to return the actual
Lisp code which creates the fact and adds it to the inference engine.
To borrow an example from the Loom Knowledge Representation system, the
way things like that are handled is by a TELL macro that does expansions
something like:

  (tell (R Fred ?variable 3))

expanding into something like(*) the following:

   (LET* ((#:FRED-14803 (FIND-OR-CREATE-INSTANCE 'FRED))
          (#:R-14804    (FIND-RELATION 'R)))
     (LOOM::ASSERT-TUPLE (LIST #:FRED-14803 ?VARIABLE 3) #:R-14804))

This involves (among other things), making the fact parser aware of the
syntax of variables and having it just return the variable value,
relying on code outside of the macro form to properly bind the
variable.  In other words, the TELL form shown above would need to
appear somewhere inside the lexical scope of ?VARIABLE.



(*) Some details were simplified for didactic purposes.

-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu    
From: David E. Young
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A2FF987.1AC3CFBE@nc.rr.com>
"David E. Young" wrote:

> Greetings. I'm buliding a production-rule system and have encountered a
> stumbling-block vis-a-vis macro expansion on rule right-hand-sides
> (RHSs)...
>

Following-up my own post here. I actually have an alternate solution that
doesn't sit particularly well with me, but nevertheless will work. While
the RHS's lexical environment is being constructed, variable bindings can
also be stored with the inference engine; these bindings can then be
accessed by ASSERT macros that might be present on the RHS. It seems a bit
redundant, and has a limitation that isn't really significant, but perhaps
I can't do what I want in any event.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike
From: Sunil Mishra
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A3017F3.3080200@everest.com>
Have you a copy of "Building Problem Solvers" (Forbus/De Kleer)? It 
talks about writing TMS (Truth Maintainence System) based problem 
solvers, in lisp. (Ken's picked up a few habits from scheme, so the 
syntax is at times a little non-lispy at times.) Having studied the 
issues and programs in that book, the two immediate solutions that come 
to mind are:

1. Write your assert macro to grovel through the code and replace all 
?<var> forms with evaluations rather than literal values. This of course 
relies on not having macros in your assertions. Given that your 
assertions are on the knowledge in the KB, you should be free to do so 
without hindering your ability to write code. You might also consider 
making the ?<var> substitution process recursive, but depending on what 
you put in your KB that might be a bad idea.

2. Put explicit (:eval ?<var>) expressions for expressions that must be 
evaluated. This is the approach the book takes.

I tend to prefer the first solution, and have implemented in a very 
simple rule firing system I had cobbled together in a couple of days.

Sunil

David E. Young wrote:

> "David E. Young" wrote:
> 
> 
>> Greetings. I'm buliding a production-rule system and have encountered a
>> stumbling-block vis-a-vis macro expansion on rule right-hand-sides
>> (RHSs)...
>> 
> 
> 
> Following-up my own post here. I actually have an alternate solution that
> doesn't sit particularly well with me, but nevertheless will work. While
> the RHS's lexical environment is being constructed, variable bindings can
> also be stored with the inference engine; these bindings can then be
> accessed by ASSERT macros that might be present on the RHS. It seems a bit
> redundant, and has a limitation that isn't really significant, but perhaps
> I can't do what I want in any event.
> 
> Regards,
> 
> --
> -----------------------------------------------------------------
> David E. Young
> Fujitsu Network Communications  (defun real-language? (lang)
> (········@computer.org)           (cond ((eql lang 'LISP)
>                                          'TRUE)
>                                         (t 'FALSE)))
> "The fact that ... we still
>  live well cannot ease the pain of
>  feeling that we no longer live nobly."
>   -- John Updike
From: David E. Young
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A301C13.7889C0B0@nc.rr.com>
Sunil Mishra wrote:

> Have you a copy of "Building Problem Solvers" (Forbus/De Kleer)? It
> talks about writing TMS (Truth Maintainence System) based problem
> solvers, in lisp. ..

No, but I'll get myself a copy. Thanks.

>
> 1. Write your assert macro to grovel through the code and replace all
> ?<var> forms with evaluations rather than literal values...

Could you elaborate on this point a bit? I'm not clear on what you mean by
"evaluations" in this context. Sorry if I'm being obtuse.

Thanks much.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike
From: Sunil Mishra
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A312F3C.3030002@everest.com>
David E. Young wrote:

> Sunil Mishra wrote:
> 
> 
>> Have you a copy of "Building Problem Solvers" (Forbus/De Kleer)? It
>> talks about writing TMS (Truth Maintainence System) based problem
>> solvers, in lisp. ..
> 
> 
> No, but I'll get myself a copy. Thanks.
> 
> 
>> 1. Write your assert macro to grovel through the code and replace all
>> ?<var> forms with evaluations rather than literal values...
> 
> 
> Could you elaborate on this point a bit? I'm not clear on what you mean by
> "evaluations" in this context. Sorry if I'm being obtuse.
> 

I just went back and reviewed the code I had written. This will only 
work if you are expecting to use a pattern matcher, as opposed to a 
unifier. I suspect adapting this approach to a unifier would be much 
more challenging. The scheme I had used created a lexical environment, 
binding the pattern variables as they were matched against forms in the 
KB. In that lexical context, the body of the rule was executed. Below I 
attempt to outline more details of the process, let me know if it is 
still unclear.

First, let me make sure I'm interpreting your rule correctly. You have:


(defrule nemesis
   (natasha (name "natasha") (nemesis ?nemesis "rocky"))
   (rocky (name ?nemesis))
   => (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis))


This rule has three clauses which must be satisfied for the rule to 
fire. (I can't figure out this rule, incidentally. Perhaps you meant 
(nemesis "natasha" ?nemesis) instead?)

In my implementation, I analyzed each of the pre-condition clauses to 
gather a list of variables contained. In this case, the list is 
'(?nemesis). Then, in the body form, I assumed that the only valid 
variables possible would be from this list. Then, for executing the 
forms in the consequent, I created a lexical environment with the 
variable bindings. Given that you know the variables that exist, 
expanding the assertion would involve ensuring everything but the known 
variables are quoted in the form you construct.

To get this to work right, you might have to make assert into a macrolet 
rather than a macro. Or perhaps have it run in a dynamic environment 
with the list of variables declared special. The latter approach has the 
advantage of handling nested rule definitions correctly. I think. The 
trick is to have lisp's mechanisms do the evaluation for you, rather 
than trying to manually force an evaluation of the binding.

I hope this is what you were looking for.

Sunil

> Thanks much.
> 
> Regards,
> 
> --
> -----------------------------------------------------------------
> David E. Young
> Fujitsu Network Communications  (defun real-language? (lang)
> (········@computer.org)           (cond ((eql lang 'LISP)
>                                          'TRUE)
>                                         (t 'FALSE)))
> "The fact that ... we still
>  live well cannot ease the pain of
>  feeling that we no longer live nobly."
>   -- John Updike
From: David E. Young
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A32ACCB.E69B2971@nc.rr.com>
Sunil Mishra wrote:

> (defrule nemesis
>    (natasha (name "natasha") (nemesis ?nemesis "rocky"))
>    (rocky (name ?nemesis))
>    => (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis))
>
> This rule has three clauses which must be satisfied for the rule to
> fire. (I can't figure out this rule, incidentally. Perhaps you meant
> (nemesis "natasha" ?nemesis) instead?)
>

Hi Sunil. Actually, the rule as written is correct, in its current incantation.
LISA (my production-rule system) borrows heavily from CLIPS and Jess; the
pattern that's drawn your attention would be written in Jess as

  (natasha (name "natasha") (nemesis ?nemesis&"rocky"))

The notation I use is actually short-hand for

  (nemesis ?nemesis (string= ?nemesis "rocky"))

Whether or not this "short-hand" notation finds its way into the production
version of LISA remains to be seen.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike
From: Eugene Zaikonnikov
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <6yy9xr93ih.fsf@localhost.localdomain>
* "David" == David E Young <·······@nc.rr.com> writes:

[...]

David>  (defun real-language? (lang) 
David>  (cond ((eql lang 'LISP) 'TRUE) (t 'FALSE)))
                ^^^Wouldn't EQ suffice here?

-- 
  Eugene
From: David E. Young
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A3042BE.E1B5779E@nc.rr.com>
Eugene Zaikonnikov wrote:

> * "David" == David E Young <·······@nc.rr.com> writes:
>
> [...]
>
> David>  (defun real-language? (lang)
> David>  (cond ((eql lang 'LISP) 'TRUE) (t 'FALSE)))
>                 ^^^Wouldn't EQ suffice here?
>

Actually, the whole thing could be rewritten as

  (defun real-language? (lang)
      (eq lang 'LISP))

but the former makes for a more, er, obvious meaning. Besides, I stole
it so the least I can do is reproduce the thing verbatim.

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike
From: Robert St. Amant
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <lpnsnnyy23s.fsf@wayback.csc.ncsu.edu>
"David E. Young" <·······@nc.rr.com> writes:
> Greetings. I'm buliding a production-rule system and have encountered a
> stumbling-block vis-a-vis macro expansion on rule right-hand-sides
> (RHSs). It's probably best in this situation to start with an example;
> so, let's consider the following rule:
> 
> (defrule nemesis
>   (natasha (name "natasha") (nemesis ?nemesis "rocky"))
>   (rocky (name ?nemesis))
>   =>
>   (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis))
> 
> Now, to fire the RHS I build a lexical context, at runtime, that
> declares the variable ?NEMESIS and includes the FORMAT form; all of this
> is wrapped in a LAMBDA expression, EVAL'd and eventually FUNCALL'd. Ok,
> works great. Enter a new construct on the RHS. I now want to add support
> for the following:
> 
> ...
> =>
> (format t "nemesis fired! Value of ?nemesis is ~S~%" ?nemesis)
> (assert (nemesis-found (name ?nemesis)))

(more details snipped)

I once (actually more than once) built a planning system that required
binding variables to values dynamically, over the preconditions and
effects of planning operators.  It's a similar problem to the one
above, as far as I can tell.  My code collected variable symbols and
values, and used progv to put them together over both "sides" of the
operator.  I can't comment on the efficiency of this approach, but I
suspect it is not great.  Just what the doctor ordered, in my case,
however.

-- 
Rob St. Amant
From: David E. Young
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A31A500.33AFA07E@nc.rr.com>
"David E. Young" wrote:

> Greetings. I'm buliding a production-rule system and have encountered a
> stumbling-block vis-a-vis macro expansion on rule right-hand-sides
> (RHSs)...

Many thanks to those who responded; helpful suggestions all. I decided upon
Tom Russ' approach, as that most closely fits the design already in place
in my system (recall that I build a _lexical_ environment at runtime and
splice in the lisp forms found on a rule's RHS).

I've included here code that implements most of what I want. I've left out
some validation and the stuff that actually asserts a fact, but you'll get
the idea. I would appreciate comment on whether I've "hit the mark", as it
were. Suggestions for improvements would also be appreciated. If you look
at function SCHTUM, you'll see exactly how macros ASSERT (ASSERT-FACT in
the sample) and, in the future, MODIFY will be called at runtime.

(in-package :user)

(defun normalize-assertion (body)
  (labels ((examine-slots (slots bindings)
              (let ((slot (first slots)))
                (cond ((null slot)
                       (values bindings))
                      (t
                       (let ((slot-name (first slot))
                             (slot-value (second slot)))
                         (examine-slots (rest slots)
                                        (nconc bindings `(',slot-name
,slot-value)))))))))
    `(flet ((normalize-slots (&rest args)
              (labels ((compose-slots (arglist slot-name slots)
                         (cond ((null arglist)
                                (values slots))
                               ((null slot-name)
                                (compose-slots (rest arglist)
                                               (first arglist)
                                               slots))
                               (t
                                (compose-slots
                                 (rest arglist)
                                 nil
                                 (nconc slots
                                        `((,slot-name ,(first
arglist)))))))))
                (compose-slots args nil nil))))
       (normalize-slots ,@(examine-slots body nil)))))

(defun parse-and-insert-fact (body)
  (normalize-assertion (rest body)))

(defmacro assert-fact ((&rest body))
  (parse-and-insert-fact body))

(defun schtum ()
  (let ((?name "rocky")
        (?age 41))
    (assert-fact (rocky (name ?name) (age ?age) (town "frostbite
falls")))))

Thanks much.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike
From: David E. Young
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <3A327BA3.32677C08@nc.rr.com>
"David E. Young" wrote:

> I've included here code that implements most of what I want...

I intended to mention that my initial solution isn't quite what I was looking
for. Given an assertion like this:

  (assert (rocky (name "rocky") (nemesis ?nemesis))

you'll notice that my code "flattens" the list of slots (the name/value pairs
above: (name "rocky"), etc.); EXAMINE-SLOTS returns a flattened list which is
spliced into the call to NORMALIZE-SLOTS, which reassembles the slots into
(name value) form. It is during this splicing that variables are expanded.

I would have liked to maintain the slot structure and effect variable binding
anyway, but I haven't figured out how to do this.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eql lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike
From: Pekka P. Pirinen
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <ixpuix8m8v.fsf@harlequin.co.uk>
"David E. Young" <·······@nc.rr.com> writes:
> you'll notice that my code "flattens" the list of slots (the name/value pairs
> above: (name "rocky"), etc.); EXAMINE-SLOTS returns a flattened list which is
> spliced into the call to NORMALIZE-SLOTS, which reassembles the slots into
> (name value) form. It is during this splicing that variables are expanded.
> 
> I would have liked to maintain the slot structure and effect variable binding
> anyway, but I haven't figured out how to do this.

Visualize what the macroexpanded code must look like:

  (list `(name ,?name)
        `(age ,?age)
        `(town ,"frostbite falls"))

then write the code to generate that:

  (defun normalize-assertion (slots)
    `(list ,@(mapcar #'(lambda (slot)
                         (let ((slot-name (first slot))
                               (slot-value (second slot)))
                           ``(,',slot-name ,,slot-value)))
                     slots)))
-- 
Pekka P. Pirinen, Adaptive Memory Management Group, Harlequin Limited
 The Risks of Electronic Communication
 http://www.best.com/~thvv/emailbad.html
From: ···········@fnc.fujitsu.com
Subject: Re: Variable expansion with macros
Date: 
Message-ID: <918j05$407$1@nnrp1.deja.com>
> Visualize what the macroexpanded code must look like...

Oh my goodness, of course. I seem to have neglected the first principle
of macro writing, haven't I. This probably would have continued to stump
me I'm afraid, as I haven't yet in my Lisp travels written macros that
employ "double backquotes" (``). I'll examine this further; thanks very
much for your assistance.

Regards,

--
-----------------------------------------------------------------
David E. Young
Fujitsu Network Communications  (defun real-language? (lang)
(········@computer.org)           (cond ((eq lang 'LISP)
                                         'TRUE)
                                        (t 'FALSE)))
"The fact that ... we still
 live well cannot ease the pain of
 feeling that we no longer live nobly."
  -- John Updike


Sent via Deja.com
http://www.deja.com/