From: charlieb
Subject: Some Macro Questions.
Date: 
Message-ID: <bnon35$11n8eh$6@ID-208832.news.uni-berlin.de>
Hi,
	I've been playing with some macros for evaluating the output of a 
production system and I have a problem. Here's the code:

(defmacro close-let  (list &optional so-far)
;  (print so-far)
;  (print list)
   (if (null list)
       so-far
     (let ((item (gensym)))
       `(close-let ,(cdr list) ,`(cons
				 (list ',(caar list) ,(cdar list))
				 ,so-far)))))

(defmacro eval-prod (expan funcs)
;  (print expan)
   ``(let ,(close-let ,funcs)
       ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))


(defun test2 ()
  (eval-prod '(a b c)
	   ((a . (lambda () (print 'a)))
	    (b . (lambda () (print 'b)))
	    (c . (lambda () (print 'c))))))

When I run (test2) it just returns the (correct!) source for the 
function that I want:
(LET
((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>) (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
   (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
  (FUNCALL A) (FUNCALL B) (FUNCALL C))

but it doesn't evalute it. When I put an eval around the macro call it 
works like a dream. My first question is this: is the call to eval 
necessary or have I just missed a little something in the macro. If the 
answer is to use eval then I have misunderstood macros. I thought they 
were for code replacement not just generation so

(defun test2 ()
  (eval-prod '(a b c)
	   ((a . (lambda () (print 'a)))
	    (b . (lambda () (print 'b)))
	    (c . (lambda () (print 'c))))))

is equivalent to

(defun test2 ()
	(LET
		((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>)
  		 (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
		 (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
		(FUNCALL A)
		(FUNCALL B)
		(FUNCALL C)))

and will be evaluated/compiled as such.

My next question is of a much lower priority. Is this a "good" use of 
macros? I understand that there are problems with the way I have 
implemented the eval-prod macro. It should really expand to something like:
	(LET
		((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>)
  		 (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
		 (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
		(dolist (item expan t) (funcall item))))

That I can figure out but on a higher level is this a worthwile way to 
sidestep having to manage a hash of closures myself?

Thanks,
Charlie.

From: Thomas A. Russ
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <ymiekwvgal6.fsf@sevak.isi.edu>
charlieb <··@privacy.net> writes:

> (defmacro eval-prod (expan funcs)
> ;  (print expan)
>    ``(let ,(close-let ,funcs)
>        ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))

Did you really intend to have TWO backquotes in this form.  That is
presumably why it doesn't evaluate the way you expect it to.  Note that
if you remove one of the outermost backquotes, you will also want to
remove the "," in front of expan in the MAPCAR call.  You won't need it,
since the ",@" form will force expan to be evaluated as an argument to
the MAPCAR function.

I'm not sure if you are aware of it, but the functions MACROEXPAND and
MACROEXPAND-1 are very useful when you are trying to debug macros.
They let you see the expansion, so you can be sure that the macro really
is expanding into what you think it should be.

For example try:

(macroexpand '(eval-prod ...))

Note that you want to quote the macro form, since macroexpand is a
function!

> 
> (defun test2 ()
>   (eval-prod '(a b c)
> 	   ((a . (lambda () (print 'a)))
> 	    (b . (lambda () (print 'b)))
> 	    (c . (lambda () (print 'c))))))
> 
> When I run (test2) it just returns the (correct!) source for the 
> function that I want:
> (LET
> ((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>) (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
>    (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
>   (FUNCALL A) (FUNCALL B) (FUNCALL C))
> 
> but it doesn't evalute it. When I put an eval around the macro call it 
> works like a dream. My first question is this: is the call to eval 
> necessary or have I just missed a little something in the macro. If the 
> answer is to use eval then I have misunderstood macros. I thought they 
> were for code replacement not just generation so

I also wonder whether you really need to have the first argument to
eval-prod be evaluated.  The way to avoid that would be to introduce
some quoting of the that argument inside the macroexpansion function.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Kaz Kylheku
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <cf333042.0310291057.6289638e@posting.google.com>
charlieb <··@privacy.net> wrote in message news:<···············@ID-208832.news.uni-berlin.de>...
> (defmacro eval-prod (expan funcs)
> ;  (print expan)
>    ``(let ,(close-let ,funcs)
>        ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))
> 
> 
> (defun test2 ()
>   (eval-prod '(a b c)
> 	   ((a . (lambda () (print 'a)))
> 	    (b . (lambda () (print 'b)))
> 	    (c . (lambda () (print 'c))))))
> 
> When I run (test2) it just returns the (correct!) source for the 
> function that I want:
> (LET
> ((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>) (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
>    (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
>   (FUNCALL A) (FUNCALL B) (FUNCALL C))
> 
> but it doesn't evalute it.

This is because you have two levels of backquote in EVAL-PROD. So
EVAL-PROD actually returned code produced by the backquote reader, and
that is what was substituted for the macro. That code's job is to
produce source code, which is what it does. Maybe just delete one
backquote?


Could that be because you have two levels of nested backquote in
EVAL-PROD?

> My next question is of a much lower priority. Is this a "good" use of 
> macros? 

Not really. If you have a list of items, and a list of functions you
want to call with those items, you can just write:

(mapc #'funcall (list #'print #'print #'print) '(a b c))

Your macro provides hardly any syntactic sugar advantage over this.
From: Chris Riesbeck
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <b7acf703.0310291122.2f430cc6@posting.google.com>
charlieb <··@privacy.net> wrote in message news:<···············@ID-208832.news.uni-berlin.de>...
> Hi,
> 	I've been playing with some macros for evaluating the output of a 
> production system and I have a problem. Here's the code:
> 
> (defmacro close-let  (list &optional so-far)
> ;  (print so-far)
> ;  (print list)
>    (if (null list)
>        so-far
>      (let ((item (gensym)))
>        `(close-let ,(cdr list) ,`(cons
> 				 (list ',(caar list) ,(cdar list))
> 				 ,so-far)))))
> 
> (defmacro eval-prod (expan funcs)
> ;  (print expan)
>    ``(let ,(close-let ,funcs)
>        ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))
> 
> 
> (defun test2 ()
>   (eval-prod '(a b c)
> 	   ((a . (lambda () (print 'a)))
> 	    (b . (lambda () (print 'b)))
> 	    (c . (lambda () (print 'c))))))
> 
> When I run (test2) it just returns the (correct!) source for the 
> function that I want:
> (LET
> ((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>) (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
>    (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
>   (FUNCALL A) (FUNCALL B) (FUNCALL C))
> 
> but it doesn't evalute it. 

You have two backquotes at the start of the eval-prod. The first one
quotes the result of the second one, hence no evaluation.

> My next question is of a much lower priority. Is this a "good" use of 
> macros? 

I couldn't infer why you want this macro, so I can't say.
From: Barry Margolin
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <jzRnb.285$lK3.227@news.level3.com>
In article <···············@ID-208832.news.uni-berlin.de>,
charlieb  <··@privacy.net> wrote:
>Hi,
>	I've been playing with some macros for evaluating the output of a 
>production system and I have a problem. Here's the code:
>
>(defmacro close-let  (list &optional so-far)
>;  (print so-far)
>;  (print list)
>   (if (null list)
>       so-far
>     (let ((item (gensym)))
>       `(close-let ,(cdr list) ,`(cons
>				 (list ',(caar list) ,(cdar list))
>				 ,so-far)))))

What is ITEM for?  You never reference the variable after binding it.

>
>(defmacro eval-prod (expan funcs)
>;  (print expan)
>   ``(let ,(close-let ,funcs)
>       ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))
>
>
>(defun test2 ()
>  (eval-prod '(a b c)
>	   ((a . (lambda () (print 'a)))
>	    (b . (lambda () (print 'b)))
>	    (c . (lambda () (print 'c))))))
>
>When I run (test2) it just returns the (correct!) source for the 
>function that I want:
>(LET
>((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>) (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
>   (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
>  (FUNCALL A) (FUNCALL B) (FUNCALL C))
>
>but it doesn't evalute it. When I put an eval around the macro call it 
>works like a dream. My first question is this: is the call to eval 
>necessary or have I just missed a little something in the macro. 

I think the reason is the double backquoting in EVAL-PROD.  The inner
backquote is generating the code you want to evaluate, but the outer one is
then returning it as a QUOTE expression.  The final result *does* get
evaluated, but the result of evaluating a QUOTE expression is the parameter
to QUOTE.  You end up having to call EVAL explicitly to get a second level
of evaluation done.

>My next question is of a much lower priority. Is this a "good" use of 
>macros? I understand that there are problems with the way I have 
>implemented the eval-prod macro. It should really expand to something like:
>	(LET
>		((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>)
>  		 (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
>		 (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
>		(dolist (item expan t) (funcall item))))

I'm not sure this is good.  Macros are expanded at compile time, and the
lexical environment that the closures are associated with won't exist at
run time.

-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bnorss$13dn0i$1@ID-208832.news.uni-berlin.de>
Barry Margolin wrote:

> In article <···············@ID-208832.news.uni-berlin.de>,
> charlieb  <··@privacy.net> wrote:

>>(defmacro close-let  (list &optional so-far)
>>;  (print so-far)
>>;  (print list)
>>  (if (null list)
>>      so-far
>>    (let ((item (gensym)))
>>      `(close-let ,(cdr list) ,`(cons
>>				 (list ',(caar list) ,(cdar list))
>>				 ,so-far)))))
> 
> 
> What is ITEM for?  You never reference the variable after binding it.
Oops it's just a bit of leftover code that wasn't cleared up properly. 
It is now!

> 
> 
>>(defmacro eval-prod (expan funcs)
>>;  (print expan)
>>  ``(let ,(close-let ,funcs)
>>      ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))
>>
> 
> I think the reason is the double backquoting in EVAL-PROD.  The inner
> backquote is generating the code you want to evaluate, but the outer one is
> then returning it as a QUOTE expression.  The final result *does* get
> evaluated, but the result of evaluating a QUOTE expression is the parameter
> to QUOTE.  You end up having to call EVAL explicitly to get a second level
> of evaluation done.
> 
OK so then how do you make the line
	``(let ,(close-let ,funcs)
work. funcs needs to be substituted with its actual value and close-let 
needs to be expanded. How would I achieve that without the double 
backquote?

> 
>>My next question is of a much lower priority. Is this a "good" use of 
>>macros? I understand that there are problems with the way I have 
>>implemented the eval-prod macro. It should really expand to something like:
>>	(LET
>>		((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>)
>> 		 (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
>>		 (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
>>		(dolist (item expan t) (funcall item))))
> 
> 
> I'm not sure this is good.  Macros are expanded at compile time, and the
> lexical environment that the closures are associated with won't exist at
> run time.
> 
If I was to make sure that the lambdas in the call to close-let don't 
get made into closures would that solve the problem? I'd end up with 
something like (after expansion):
(defun test2 ()
    (let ((x 1))
	(LET
	    (a (lambda () (print (incf x))))
	    (b (lambda () (print 'b)))
	    (c (lambda () (print 'c)))))
	(dolist (item '(a b c) t) (funcall (coerce item 'function)))))

Is this a worthwile way of sidestepping having to manage a hash of 
lambdas myself?

Thanks.
From: Peter Seibel
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <m31xsvsyh2.fsf@javamonkey.com>
charlieb <··@privacy.net> writes:

> Barry Margolin wrote:
> 
> > In article <···············@ID-208832.news.uni-berlin.de>,
> > charlieb  <··@privacy.net> wrote:
> 
> >>(defmacro close-let  (list &optional so-far)
> >>;  (print so-far)
> >>;  (print list)
> >>  (if (null list)
> >>      so-far
> >>    (let ((item (gensym)))
> >>      `(close-let ,(cdr list) ,`(cons
> >>				 (list ',(caar list) ,(cdar list))
> >>				 ,so-far)))))
> > What is ITEM for?  You never reference the variable after binding it.
> Oops it's just a bit of leftover code that wasn't cleared up properly.
> It is now!
> 
> >
> >>(defmacro eval-prod (expan funcs)
> >>;  (print expan)
> >>  ``(let ,(close-let ,funcs)
> >>      ,@(mapcar (lambda (item) `(funcall ,item)) ,expan)))
> >>
> > I think the reason is the double backquoting in EVAL-PROD.  The inner
> > backquote is generating the code you want to evaluate, but the outer one is
> > then returning it as a QUOTE expression.  The final result *does* get
> > evaluated, but the result of evaluating a QUOTE expression is the parameter
> > to QUOTE.  You end up having to call EVAL explicitly to get a second level
> > of evaluation done.
> >
> OK so then how do you make the line
> 	``(let ,(close-let ,funcs)
> work. funcs needs to be substituted with its actual value and
> close-let needs to be expanded. How would I achieve that without the
> double backquote?

Why can't you just write

  `(let ,(close-let funcs) ...

?

But I think the real qustion is, what are you really trying to do?
Macros are the right tool when you say to yourself, "I wish I could
type X and have it be like I typed Y." In your case, as near as I can
tell, you wish that you could type this:

  (defun test2 ()
    (eval-prod (a b c)
               ((a . (lambda () (print 'a)))
                (b . (lambda () (print 'b)))
                (c . (lambda () (print 'c))))))

and have it be liked you typed this:

  (defun test2 ()
    (let ((a (lambda nil (print 'a)))
          (b (lambda nil (print 'b)))
          (c (lambda nil (print 'c))))
      (funcall a)
      (funcall b)
      (funcall c)))

If that's the case, you just need a program that converts everything
after the EVAL-PROD in the first version to the LET form in the second
version. This seems to do the trick:

  (defmacro eval-prod (expan funcs)
    `(let ,(mapcar #'(lambda (b) (list (first b) (rest b))) funcs)
       ,@(mapcar #'(lambda (sym) `(funcall ,sym)) expan)))


> >> My next question is of a much lower priority. Is this a "good" use
> >> of macros? I understand that there are problems with the way I have
> >> implemented the eval-prod macro. It should really expand to
> >> something like:
> >>	(LET
> >>		((C #<CLOSURE :LAMBDA NIL (PRINT 'C)>)
> >> 		 (B #<CLOSURE :LAMBDA NIL (PRINT 'B)>)
> >>		 (A #<CLOSURE :LAMBDA NIL (PRINT 'A)>))
> >>		(dolist (item expan t) (funcall item))))
> >
> > I'm not sure this is good. Macros are expanded at compile time,
> > and the lexical environment that the closures are associated with
> > won't exist at run time.
> >
> If I was to make sure that the lambdas in the call to close-let don't
> get made into closures would that solve the problem? I'd end up with
> something like (after expansion):
> (defun test2 ()
>     (let ((x 1))
> 	(LET
> 	    (a (lambda () (print (incf x))))
> 	    (b (lambda () (print 'b)))
> 	    (c (lambda () (print 'c)))))
> 	(dolist (item '(a b c) t) (funcall (coerce item 'function)))))

This isn't going to work the way you think it is because COERCE can
only coerce a symbol to a function if it's the name of a global
function. Presumably what you want to do here is funcall the values of
the variables bound by the LET. (But even that doesn't make much sense
because you might as well just expand test2 into this:

  (defun test2 ()
    (let ((x 1))
      (incf x)
      (print 'b)
      (print 'c)))

Which brings me back to the question, what are you really trying to
do?

> Is this a worthwile way of sidestepping having to manage a hash of
> lambdas myself?

Why do you need to manage a hash of lambdas?

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bnqj45$14jfoi$1@ID-208832.news.uni-berlin.de>
Peter Seibel wrote:
> charlieb <··@privacy.net> writes:
> 
> 
<snip>
> 
> 
> Why can't you just write
> 
>   `(let ,(close-let funcs) ...
> 
When I do that clisp tells me that "funcs is not a list"

> ?
> 
> But I think the real qustion is, what are you really trying to do?
The major point of this exercise is to better understand, for the first 
question, how macros work and for the second question, when to use them. 
By now I realise that this is probably not going to be a useful macro (I 
had already written a function that has the same effect) but when I do 
find a problem where only a macro will do I want to have had enough 
practice to be able to write it without too much hair tearing.


> 
> If that's the case, you just need a program that converts everything
> after the EVAL-PROD in the first version to the LET form in the second
> version. This seems to do the trick:
> 
>   (defmacro eval-prod (expan funcs)
>     `(let ,(mapcar #'(lambda (b) (list (first b) (rest b))) funcs)
>        ,@(mapcar #'(lambda (sym) `(funcall ,sym)) expan)))
> 

Mmmmm short and sweet!

> Which brings me back to the question, what are you really trying to
> do?
> 
> 
>>Is this a worthwile way of sidestepping having to manage a hash of
>>lambdas myself?
> 
> 
> Why do you need to manage a hash of lambdas?
> 
OK so lets say I want to do this without using macros. The way that 
initially suggests itself for convenience is an a-list of (symbol . 
lambda) which would have a call to assoc for every item in the expan 
list. To make it faster that would eventually have to become a hash 
table but that would give the coding overhead of managing the hash 
table. So I thought that maybe I could move the problem down a level and 
let lisp manage the problem of associating the symbol from expan with 
the function that shares its name. I realise that I was probably over 
complicating matters but I thought it would be an interesting exercise.

Thanks for your help and interest.
Charlie.
From: Kaz Kylheku
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <cf333042.0310301052.5cbb0147@posting.google.com>
charlieb <··@privacy.net> wrote in message news:<···············@ID-208832.news.uni-berlin.de>...
> Peter Seibel wrote:
> > charlieb <··@privacy.net> writes:
> > 
> > 
>  <snip>
> > 
> > 
> > Why can't you just write
> > 
> >   `(let ,(close-let funcs) ...
> > 
> When I do that clisp tells me that "funcs is not a list"

That's because you should have made your life easier and written
CLOSE-LET as a function, rather than a macro.

Typically, only the top-level of your custom syntax needs to be
implemented as a macro. If a macro is large so that its expansion
logic needs to be separated into smaller modules, those modules ought
to be expander functions.

What is CLOSE-LET? Ideally, it's a function that takes a list, and
spits out another list. If you make it a macro, then you add the
undesirable properties that the argument is not evaluated, and the
result is substituted.

When a macro does depend on other macros, it's usually done
differently. The top level macro acts as a transformer which takes the
input macro language and translates it into a different language,
which is also a macro language. That second macro language is
expressed using the lower-level macros, which are handled the next
macroexpansion round, independently of the original macro that
produced them; they are not subordinate transformers called from the
orginal macro.

You can't do that here, because your lower level expander produces a
lambda list for a LET; a lambda list is not a form that can be further
macroexpanded. It's a fragment of the syntax of the LET, not an
independent operator or function form.

When you need to compute non-evaluated pieces of some syntactic form
like LET, write that computation as a function! Then you can just
write:

  (defun compute-piece-of-syntax (arg) ...)

  (defmacro mymacro (this-argument)
    `(some-form ,(compute-piece-of-syntax this-argument) ...))

Last note: if these subordinate functions are top level functions
lather than inner LABELS or FLETs within the macro, you have to learn
how to use EVAL-WHEN to tell Lisp that these functions need to be
instantiated within the compiler for use by macros.
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bntlgp$159fuk$1@ID-208832.news.uni-berlin.de>
Kaz Kylheku wrote:

> charlieb <··@privacy.net> wrote in message news:<···············@ID-208832.news.uni-berlin.de>...
> 
>>Peter Seibel wrote:
>>
>>>charlieb <··@privacy.net> writes:
>>>
>>>
>>
>> <snip>
>>
>>>
>>>Why can't you just write
>>>
>>>  `(let ,(close-let funcs) ...
>>>
>>
>>When I do that clisp tells me that "funcs is not a list"
> 
> 
> That's because you should have made your life easier and written
> CLOSE-LET as a function, rather than a macro.
> 
> Typically, only the top-level of your custom syntax needs to be
> implemented as a macro. If a macro is large so that its expansion
> logic needs to be separated into smaller modules, those modules ought
> to be expander functions.
> 
> What is CLOSE-LET? Ideally, it's a function that takes a list, and
> spits out another list. If you make it a macro, then you add the
> undesirable properties that the argument is not evaluated, and the
> result is substituted.
> 
> When a macro does depend on other macros, it's usually done
> differently. The top level macro acts as a transformer which takes the
> input macro language and translates it into a different language,
> which is also a macro language. That second macro language is
> expressed using the lower-level macros, which are handled the next
> macroexpansion round, independently of the original macro that
> produced them; they are not subordinate transformers called from the
> orginal macro.
> 
> You can't do that here, because your lower level expander produces a
> lambda list for a LET; a lambda list is not a form that can be further
> macroexpanded. It's a fragment of the syntax of the LET, not an
> independent operator or function form.
> 
> When you need to compute non-evaluated pieces of some syntactic form
> like LET, write that computation as a function! Then you can just
> write:
> 
>   (defun compute-piece-of-syntax (arg) ...)
> 
>   (defmacro mymacro (this-argument)
>     `(some-form ,(compute-piece-of-syntax this-argument) ...))
> 
> Last note: if these subordinate functions are top level functions
> lather than inner LABELS or FLETs within the macro, you have to learn
> how to use EVAL-WHEN to tell Lisp that these functions need to be
> instantiated within the compiler for use by macros.


I've just downloaded On Lisp and had a little read. A couple of things 
sprang out at me. The first is what you just said, let functions do the 
internal work of macros. I suppose it's just me making the first mistake 
of macrology, thinking that macros and functions somehow live in 
different worlds (excepting time of expansion/evaluation) and should be 
kept separate except for special circumstances.
The second seems to echo my justification for wanting to create a let 
for so that I can run the functions by funcalling the appropriate 
symbol. I wonder if you had any thoughts on whether this approach 
constitutes "Integration with Lisp" (On Lisp, page 100, section 8.2, 
Pros point 2). When I read that section I thought, "That's exactly what 
I was trying to do".
My intention was to have the function a-list defined at compile time but 
to have the expansion (of the production system) calculated at runtime 
and evaluated within the context of the 'a-list made let'.

Cheers.
Charlie.
From: Peter Seibel
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <m34qxpmjkp.fsf@javamonkey.com>
charlieb <··@privacy.net> writes:

> The second seems to echo my justification for wanting to create a
> let for so that I can run the functions by funcalling the
> appropriate symbol. I wonder if you had any thoughts on whether this
> approach constitutes "Integration with Lisp" (On Lisp, page 100,
> section 8.2, Pros point 2). When I read that section I thought,
> "That's exactly what I was trying to do". My intention was to have
> the function a-list defined at compile time but to have the
> expansion (of the production system) calculated at runtime and
> evaluated within the context of the 'a-list made let'.

Maybe other folks are following you here but you're still expressing
your problem statement in way too compressed a form for me to get it.
Can you give a high level description of what you're trying to do
without using any terms that give away your assumptions about how it
should be implemented. (i.e. no "let", "alist", etc.)?

Reading between the lines it seems maybe you want FLET or LABELS, and
maybe a macro that expands into one of them.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bo5pjl$187ke0$1@ID-208832.news.uni-berlin.de>
Peter Seibel wrote:
> 
> Maybe other folks are following you here but you're still expressing
> your problem statement in way too compressed a form for me to get it.
> Can you give a high level description of what you're trying to do
> without using any terms that give away your assumptions about how it
> should be implemented. (i.e. no "let", "alist", etc.)?
> 
> Reading between the lines it seems maybe you want FLET or LABELS, and
> maybe a macro that expands into one of them.
> 
> -Peter
> 

Quite so, in fact on reflection I came up with the following:

(defmacro with-functions (funcs &rest body)
   (labels ((make-labels (func)
			`(,(car func) () ,@(cdr func))))
     `(labels ((funcp (sym) (member sym ',(mapcar #'car funcs)))
	      ,@(mapcar #'make-labels funcs))
	 ,@body))))

which seems to work quite nicely.

The only problem now it when I try to use it e.g.

(defun test-with (list)
   (with-functions ((a (print 'a) (print 'a2))
		   (b (print 'b) (print 'b2)))
		  (dolist (item list t)
		    (when (funcp item)
		      (funcall item)))))

[]>(with-test '(a c b d))
*** - FUNCALL: the function A is undefined

Is this because I am doing (funcall 'a) as opposed to (funcall #'a).
You've already commented on this w.r.t a throw-away code fragment I 
wrote that included a coerce. I wonder if you might like to comment on 
the above or suggest an approach that might work.

Cheers.
Charlie.

PS the non-macro version of this I wrote in like 10 mins is:

(defun eval-production (expansion functions)
   (let ((lamb nil))
    (dolist (symb expansion expansion)
      (setq lamb (assoc symb functions))
      (unless (null lamb)
        (funcall (cdr lamb))))))

(defun koch-island (iterations sock)
   (set-length 0.01 sock)
   (eval-production (print (produce iterations '(F + + F + + F)
				   '((F . (F - F + + F - F)))))
		   `((F . ,(lambda () (draw sock) (finish-output sock)))
		     (+ . ,(lambda () (add-rotation 60 sock)))
		     (- . ,(lambda () (add-rotation -60 sock))))))

My eventual aim is to be able to combine the expansion rules with the 
symbols' meanings (3rd and 4th args to produce respectively) in a single 
expression that will yield an expansion that would be able to defun 
multiple production system definitions.
Please don't solve this for me. It's a fun exercise for me.
From: Chris Riesbeck
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <b7acf703.0311031204.4f27ea05@posting.google.com>
charlieb <··@privacy.net> wrote in message news:<···············@ID-208832.news.uni-berlin.de>...
> Peter Seibel wrote:
> > 
> > 
> > Reading between the lines it seems maybe you want FLET or LABELS, and
> > maybe a macro that expands into one of them.
> > 
> > -Peter
> > 
> 
> Quite so, in fact on reflection I came up with the following:
> 
> (defmacro with-functions (funcs &rest body)
>    (labels ((make-labels (func)
> 			`(,(car func) () ,@(cdr func))))
>      `(labels ((funcp (sym) (member sym ',(mapcar #'car funcs)))
> 	      ,@(mapcar #'make-labels funcs))
> 	 ,@body))))
> 
> which seems to work quite nicely.
> 
> The only problem now it when I try to use it e.g.
> 
> (defun test-with (list)
>    (with-functions ((a (print 'a) (print 'a2))
> 		   (b (print 'b) (print 'b2)))
> 		  (dolist (item list t)
> 		    (when (funcp item)
> 		      (funcall item)))))
> 
> []>(with-test '(a c b d))
> *** - FUNCALL: the function A is undefined
> 
> Is this because I am doing (funcall 'a) as opposed to (funcall #'a).

No. It's because (funcall 'a) calls the global definition of A, i.e.,
the one found with (symbol-function 'a). LABELS and FLET create local
definitions.

If you change your macro to expand into

  (progn (setf (symbol-function 'a) #'(lambda () ...))
    (setf (symbol-function 'b) #'(lambda () ...))
    ...
    (dolist (item list t) ...))

then it should work. But you will be clobbering global definitions.

Alternatively, if you want local functions, change (funcall item) in
your dolist to (funcall (lookup item)) and have your macro expand into

  (labels
    ((lookup (name)
      (cadr (assoc name (list (list 'a #'(lambda () ...))
                              (list 'b #'(lambda () ...))
                              ...))))
     (funcp (name) (not (null (lookup name)))))
   (dolist (item list t) ...))
From: Barry Margolin
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <U8vpb.331$lK3.96@news.level3.com>
In article <···············@ID-208832.news.uni-berlin.de>,
charlieb  <··@privacy.net> wrote:
>Quite so, in fact on reflection I came up with the following:
>
>(defmacro with-functions (funcs &rest body)
>   (labels ((make-labels (func)
>			`(,(car func) () ,@(cdr func))))
>     `(labels ((funcp (sym) (member sym ',(mapcar #'car funcs)))
>	      ,@(mapcar #'make-labels funcs))
>	 ,@body))))
>
>which seems to work quite nicely.
>
>The only problem now it when I try to use it e.g.
>
>(defun test-with (list)
>   (with-functions ((a (print 'a) (print 'a2))
>		   (b (print 'b) (print 'b2)))
>		  (dolist (item list t)
>		    (when (funcp item)
>		      (funcall item)))))
>
>[]>(with-test '(a c b d))
>*** - FUNCALL: the function A is undefined
>
>Is this because I am doing (funcall 'a) as opposed to (funcall #'a).

Yes.  When a symbol is used as a function, its global function binding is
used, not any lexical bindings.  If you want to be able to refer to these
functions via a symbol, you need to create some kind of association between
the symbol and the function.

Or maybe you should rethink what you're trying to do in the first place.
Why should the user of WITH-TEST know what function names were assigned
internally by the macro?

-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bo60r4$18jmn0$1@ID-208832.news.uni-berlin.de>
Barry Margolin wrote:

> In article <···············@ID-208832.news.uni-berlin.de>,
> charlieb  <··@privacy.net> wrote:
>>Is this because I am doing (funcall 'a) as opposed to (funcall #'a).
> 
> 
> Yes.  When a symbol is used as a function, its global function binding is
> used, not any lexical bindings.  If you want to be able to refer to these
> functions via a symbol, you need to create some kind of association between
> the symbol and the function.

This is the step I am trying to avoid. I already have a non-macro 
version that uses assoc lists and I was hoping (in vain it would seem) 
to find a way to have a context creating macro (or whatever!) allow me 
to dispense with the lookup operation.

> 
> Or maybe you should rethink what you're trying to do in the first place.
> Why should the user of WITH-TEST know what function names were assigned
> internally by the macro?
> 

The function names will correspond to the symbols used by the production 
system. e.g. koch-island uses the symbols F, + and - for its expansion 
rules and it seems only logical (to me) to use functions with those names.

I wrote a non-macro version of this:

(defun eval-production (expansion functions)
   (let ((lamb nil))
    (dolist (symb expansion expansion)
      (setq lamb (assoc symb functions))
      (unless (null lamb)
        (funcall (cdr lamb))))))

(defun koch-island (iterations sock)
   (eval-production (print (produce iterations '(F + + F + + F)
                    '((F . (F - F + + F - F)))))
            `((F . ,(lambda () (draw sock) (finish-output sock)))
              (+ . ,(lambda () (add-rotation 60 sock)))
              (- . ,(lambda () (add-rotation -60 sock))))))

My eventual aim is to be able to combine the expansion rules with the 
symbols' meanings (3rd and 4th args to produce respectively) in a single 
expression that will yield an expansion that would be able to defun 
koch-island-like functions.
Please don't solve this for me. It's a fun exercise for me.
From: Peter Seibel
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <m37k2hjtxx.fsf@javamonkey.com>
charlieb <··@privacy.net> writes:

> Peter Seibel wrote:
> > Maybe other folks are following you here but you're still expressing
> > your problem statement in way too compressed a form for me to get it.
> > Can you give a high level description of what you're trying to do
> > without using any terms that give away your assumptions about how it
> > should be implemented. (i.e. no "let", "alist", etc.)?
> > Reading between the lines it seems maybe you want FLET or LABELS, and
> > maybe a macro that expands into one of them.
> > -Peter
> >
> 
> Quite so, in fact on reflection I came up with the following:
> 
> (defmacro with-functions (funcs &rest body)
>    (labels ((make-labels (func)
> 			`(,(car func) () ,@(cdr func))))
>      `(labels ((funcp (sym) (member sym ',(mapcar #'car funcs)))
> 	      ,@(mapcar #'make-labels funcs))
> 	 ,@body))))
> 
> which seems to work quite nicely.
> 
> The only problem now it when I try to use it e.g.
> 
> (defun test-with (list)
>    (with-functions ((a (print 'a) (print 'a2))
> 		   (b (print 'b) (print 'b2)))
> 		  (dolist (item list t)
> 		    (when (funcp item)
> 		      (funcall item)))))
> 
> []>(with-test '(a c b d))
> *** - FUNCALL: the function A is undefined
> 
> Is this because I am doing (funcall 'a) as opposed to (funcall #'a).

Yes. There's no way to call a lexically bound function starting from
the runtime value of a symbol. That is, you can do this:

  (flet ((a () (print 'a))) (funcall a))

but not this:

  (flet ((a () (print 'a))) (funcall 'a))

nor this:

  (let ((sym 'a)) (flet ((a () (print 'a))) (funcall sym)))

If you don't immediately understand the differences (and similarities)
between those three expressions you should probably meditate on them
for a while.

> You've already commented on this w.r.t a throw-away code fragment I
> wrote that included a coerce. I wonder if you might like to comment
> on the above or suggest an approach that might work.
> 
> Cheers.
> Charlie.
> 
> PS the non-macro version of this I wrote in like 10 mins is:
> 
> (defun eval-production (expansion functions)
>    (let ((lamb nil))
>     (dolist (symb expansion expansion)
>       (setq lamb (assoc symb functions))
>       (unless (null lamb)
>         (funcall (cdr lamb))))))

Okay, I'm getting what you're doing now. In this version you're
maintaining your own mapping from symbols to anonymous functions.
That's fine. And in your macro version above you're trying to avoid
having to maintain that mapping explicitly.

If that's what you want to do you actually need to go *further*. That
is, when you get rid of the explict mapping from symbols to functions,
you need to also get rid of the FUNCALL.

Instead you must generate a single chunk of code that contains both
the function bindings (via FLET or LABELS) and then the actual calls,
just as if you were calling normal functions. That is, start from what
you'd *like* to write, something along the lines of:

  (eval-production (f + + f + + f)
    ((f (draw sock) (finish-output sock))
     (+ (add-rotation 60 sock))
     (- (add-rotation -60 sock))))

then think how you'd translate that into code if you were just going
to write it out by hand:

  (flet ((f () (draw sock) (finish-output sock))
         (+ () (add-rotation 60 sock))
         (- () (add-rotation -60 sock)))
    (f)
    (+)
    (+)
    (f)
    (+)
    (+)
    (f))

Notice no FUNCALLs--they aren't needed if we're willing to write the
code out by hand. Now you want to write a macro that translates the
first expression (the eval-production) into the second (the flet). If
the arguments to eval-production are all stacit (i.e. known at compile
time) you can use a macro. Otherwise you can make eval-production a
function that generates a single lambda form wrapped around the whole
FLET and then FUNCALL it. (You could also COMPILE it and then FUNCALL
the resulting compiled function.)

Note: there's one problem with the code translation I gave you which
is you're not allowed to FLET symbols in the COMMON-LISP package so
you can't use the symbols + and - unless you've created your own
package that shadows them. But you could, in the implementation of
eval-production translate the symbols on the fly--as long as
everywhere in the input you have a + you put the same new symbol you
could replace it with anything. Look up GENSYM if you want to be sure
of having a unique symbol.

> (defun koch-island (iterations sock)
>    (set-length 0.01 sock)
>    (eval-production (print (produce iterations '(F + + F + + F)
> 				   '((F . (F - F + + F - F)))))
> 		   `((F . ,(lambda () (draw sock) (finish-output sock)))
> 		     (+ . ,(lambda () (add-rotation 60 sock)))
> 		     (- . ,(lambda () (add-rotation -60 sock))))))
> 
> My eventual aim is to be able to combine the expansion rules with the
> symbols' meanings (3rd and 4th args to produce respectively) in a
> single expression that will yield an expansion that would be able to
> defun multiple production system definitions.
> Please don't solve this for me. It's a fun exercise for me.

Okay. Hopefully I haven't given too much away. Let me know if you need
another hint.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bo61qj$18eugn$1@ID-208832.news.uni-berlin.de>
Peter Seibel wrote:

> charlieb <··@privacy.net> writes:
> 
> 
>>Peter Seibel wrote:  [alot!]

Thank-you. I now have a head ache but on the plus side I have some 
reading/understanding to do on the train ride home.
btw having just skimmed the article you wrote it occurs to me that I 
should have also posted my bash at the produce function because the '(F 
+ + F + + F) is not the expansion it is the axiom (starting point) the 
next arg defines the expansion rule/s.
						Axiom only.
(eval-production (print (produce iterations '(F + + F + + F)
					This is an expansion rule.
 > 				   '((F . (F - F + + F - F)))))

 > 		   `((F . ,(lambda () (draw sock) (finish-output sock)))
...

This is one of the mistakes I made before. The expanded version of the 
'(F + + F + + F) list is not available at compile-time because it 
depends on the iterations variable.

I'll have a closer read of your message so if you already knew this and 
took it into account please ignore me :)

Cheers
Charlie.
From: Peter Seibel
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <m3sml5ib2r.fsf@javamonkey.com>
charlieb <··@privacy.net> writes:

> Peter Seibel wrote:
> 
> > charlieb <··@privacy.net> writes:
> >
> >>Peter Seibel wrote:  [alot!]
> 
> Thank-you. I now have a head ache but on the plus side I have some
> reading/understanding to do on the train ride home.
> btw having just skimmed the article you wrote it occurs to me that I
> should have also posted my bash at the produce function because the
> '(F + + F + + F) is not the expansion it is the axiom (starting point)
> the next arg defines the expansion rule/s.
> 						Axiom only.
> (eval-production (print (produce iterations '(F + + F + + F)
> 					This is an expansion rule.
>  > 				   '((F . (F - F + + F - F)))))
> 
>  > 		   `((F . ,(lambda () (draw sock) (finish-output sock)))
> ...
> 
> This is one of the mistakes I made before. The expanded version of the
> '(F + + F + + F) list is not available at compile-time because it
> depends on the iterations variable.

Ah, I see. So presumably PRODUCE starts from (f + + f + + f) and on
each iteration replaces f with (f - f + + f - f) according to the
expansion rule? But the number of iterations isn't known until runtime?

> I'll have a closer read of your message so if you already knew this
> and took it into account please ignore me :)

No, I hadn't understood that before. That makes sense. Well, there are
a couple possibilities depending on what you know and when you know it
and how many times these things are going to be evaluated. One thing
to consider is that assuming the expansion works the way I described
above, is these expansions are going to get pretty big in a hurry. So
you might not want to generate the complete expansion at compile time.
Instead you may want to generate a recursive function F that is called
with a variable which will be a number. When called with 0 it executes
the normal function definition. Otherwise it generates the sequence of
calls indicated by the expansion rule, translating calls to itself to
calls with its argument decremented by one. As long as you aren't
doing super large numbers of iterations this should be fine--since it
recurses you could run into stack-depth limits with really large
numbers of iterations. Anyway, if you want to see it I've got a macro
that causes this:

  (eval-production
   :iterations 2
   :axiom      (f + + f + + f)
   :expansions ((f . (f - f + + f - f)))
   :functions  ((f (prin1 'f))
                (+ (prin1 '+))
                (- (prin1 '-))))

To generate this output:

  F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bo87uv$1aihc1$1@ID-208832.news.uni-berlin.de>
Peter Seibel wrote:

> charlieb <··@privacy.net> writes:
> 
> 
>>Peter Seibel wrote:
>>
>>
>>>charlieb <··@privacy.net> writes:
>>>
>>>
>>>>Peter Seibel wrote:  [alot!]
>>
>>Thank-you. I now have a head ache but on the plus side I have some
>>reading/understanding to do on the train ride home.
>>btw having just skimmed the article you wrote it occurs to me that I
>>should have also posted my bash at the produce function because the
>>'(F + + F + + F) is not the expansion it is the axiom (starting point)
>>the next arg defines the expansion rule/s.
>>						Axiom only.
>>(eval-production (print (produce iterations '(F + + F + + F)
>>					This is an expansion rule.
>> > 				   '((F . (F - F + + F - F)))))
>>
>> > 		   `((F . ,(lambda () (draw sock) (finish-output sock)))
>>...
>>
>>This is one of the mistakes I made before. The expanded version of the
>>'(F + + F + + F) list is not available at compile-time because it
>>depends on the iterations variable.
> 
> 
> Ah, I see. So presumably PRODUCE starts from (f + + f + + f) and on
> each iteration replaces f with (f - f + + f - f) according to the
> expansion rule? But the number of iterations isn't known until runtime?
> 
> 
>>I'll have a closer read of your message so if you already knew this
>>and took it into account please ignore me :)
> 
> 
> No, I hadn't understood that before. That makes sense. Well, there are
> a couple possibilities depending on what you know and when you know it
> and how many times these things are going to be evaluated. One thing
> to consider is that assuming the expansion works the way I described
> above, is these expansions are going to get pretty big in a hurry. So
> you might not want to generate the complete expansion at compile time.
> Instead you may want to generate a recursive function F that is called
> with a variable which will be a number. When called with 0 it executes
> the normal function definition. Otherwise it generates the sequence of
> calls indicated by the expansion rule, translating calls to itself to
> calls with its argument decremented by one. As long as you aren't
> doing super large numbers of iterations this should be fine--since it
> recurses you could run into stack-depth limits with really large
> numbers of iterations. Anyway, if you want to see it I've got a macro
> that causes this:
> 
>   (eval-production
>    :iterations 2
>    :axiom      (f + + f + + f)
>    :expansions ((f . (f - f + + f - f)))
>    :functions  ((f (prin1 'f))
>                 (+ (prin1 '+))
>                 (- (prin1 '-))))
> 
> To generate this output:
> 
>   F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F
> 
> -Peter
> 
I think I have a solution that works but is not quite perfect:
I would explain how it works but I need to have lunch before the english 
speaking part of my brain will wake up. Produce is included for clarity 
it is my next sub-project to re-work it in some way.

Charlie.

(defmacro with-production ((rules-var syms-var) rule-list &rest body)
   (flet ((functionise (sym) `#',sym))
     (let ((rule-functions
        (mapcar (lambda (rule) (append `(,(car rule)) `(()) (cddr rule)))
		   (remove-if-not #'cddr rule-list)))
	  (rule-expansions
	   (mapcar (lambda (rule)
		     (cons 'list (cons (functionise (car rule))
        			           (mapcar #'functionise (cadr rule)))))
		   rule-list))
	  (rule-symbols
	    (mapcar (lambda (item) (functionise (car item)))
		    (remove-if-not #'cddr rule-list))))

    `(flet
        ,rule-functions
      (let ((,rules-var (list ,@rule-expansions))
	   (,syms-var (list ,@rule-symbols)))
        ,@body)))))

(defun produce (iters axiom rules)
   (labels ((replacement (item)
			(let ((result (assoc item rules)))
			  (listise (if (null result)
				       item
				     (cdr result)))))
	   (prod-aux (depth ax-aux)
		     (if (zerop depth)
			 ax-aux
		       (let ((expansion '()))
			 (dolist (item ax-aux expansion)
			   (setq expansion
				 (append (replacement item) expansion)))
			 (prod-aux (1- depth) expansion)))))
     (prod-aux iters (listise axiom))))

(defun test-dolist (syms)
   (dolist (var syms t) (funcall var)))

(defun test-with ()
   (with-production (rules syms)
		   ((a (a b) (print 'a-sym))
		    (b (b a) (print 'b-sym)))
		   (test-dolist (produce 3 (list #'a #'b) rules))))
From: Peter Seibel
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <m3ism0dul5.fsf@javamonkey.com>
charlieb <··@privacy.net> writes:

> I think I have a solution that works but is not quite perfect: I
> would explain how it works but I need to have lunch before the
> english speaking part of my brain will wake up. Produce is included
> for clarity it is my next sub-project to re-work it in some way.
> 
> Charlie.
> 
> (defmacro with-production ((rules-var syms-var) rule-list &rest body)
>    (flet ((functionise (sym) `#',sym))
>      (let ((rule-functions
>         (mapcar (lambda (rule) (append `(,(car rule)) `(()) (cddr rule)))
> 		   (remove-if-not #'cddr rule-list)))
> 	  (rule-expansions
> 	   (mapcar (lambda (rule)
> 		     (cons 'list (cons (functionise (car rule))
>         			           (mapcar #'functionise (cadr rule)))))
> 		   rule-list))
> 	  (rule-symbols
> 	    (mapcar (lambda (item) (functionise (car item)))
> 		    (remove-if-not #'cddr rule-list))))
> 
>     `(flet
>         ,rule-functions
>       (let ((,rules-var (list ,@rule-expansions))
> 	   (,syms-var (list ,@rule-symbols)))
>         ,@body)))))
> 
> (defun produce (iters axiom rules)
>    (labels ((replacement (item)
> 			(let ((result (assoc item rules)))
> 			  (listise (if (null result)
> 				       item
> 				     (cdr result)))))
> 	   (prod-aux (depth ax-aux)
> 		     (if (zerop depth)
> 			 ax-aux
> 		       (let ((expansion '()))
> 			 (dolist (item ax-aux expansion)
> 			   (setq expansion
> 				 (append (replacement item) expansion)))
> 			 (prod-aux (1- depth) expansion)))))
>      (prod-aux iters (listise axiom))))
> 
> (defun test-dolist (syms)
>    (dolist (var syms t) (funcall var)))
> 
> (defun test-with ()
>    (with-production (rules syms)
> 		   ((a (a b) (print 'a-sym))
> 		    (b (b a) (print 'b-sym)))
> 		   (test-dolist (produce 3 (list #'a #'b) rules))))
> 

So that looks pretty good. I think you're far enough along that it
won't spoil anything for you to see the version I hacked up. (If you
disagree, stop reading now.)

I built the PRODUCE functionality into my macro; obviously if you had
other schemes by which to iterate through productions that PRODUCE
you'd need to do something slightly different.

The other feature of this macro is that it translates from the symbols
that are used in the productions to gensym'd symbols so we can use
symbols like + and - without having to worry about the illegality of
binding new functions on CL:+ and CL:-. But this symbol translation is
all done at compile (macroexpansion) time so there's no funcalling
when the expanded code is actually run as there was in one of your
early versions.

Here's the macro:

  (defmacro eval-production (&key iterations axiom expansions functions)
    (let ((symbol-translations ())
          (iters-value (gensym "ITERS-VALUE"))
          (iter-param (gensym "N")))

      ;; Create mappings from the symbols used in the axiom and as names 
      ;; of "functions" to fresh symbols
      (dolist (x axiom)
        (when (and (symbolp x) (not (assoc x symbol-translations)))
          (push (cons x (gensym (format nil "~a-" x))) symbol-translations)))
      (dolist (fn functions)
        (let ((name (first fn)))
          (when (and (symbolp name) (not (assoc name symbol-translations)))
            (push (cons name (gensym (format nil "~a-" name))) symbol-translations))))

      ;; Some helper functions for generating the exapnsion
      (labels ((trans-sym (sym)
                 (or (cdr (assoc sym symbol-translations))
                     (error "Unknown symbol ~A" sym)))
               (fn-def (fn)
                 (let* ((name (first fn))
                        (expn (cdr (assoc name expansions))))
                   (flet ((call-in-expansion (sym)
                            (if (eql sym name)
                              `(,(trans-sym sym) ,iter-param)
                              `(,(trans-sym sym)))))
                     `(,(trans-sym name) 
                       ,@(if expn
                           `((,iter-param)
                             (if (zerop ,iter-param)
                               (progn ,@(rest fn))
                               (let ((,iter-param (1- ,iter-param)))
                                 ,@(mapcar #'call-in-expansion expn))))
                           `(() ,@(rest fn)))))))
               (call-in-body (sym)
                 (if (assoc sym expansions)
                   `(,(trans-sym sym) ,iters-value)
                   `(,(trans-sym sym)))))

        ;; The actual expansion ...
        `(let ((,iters-value ,iterations))
           (labels (,@(mapcar #'fn-def functions))
             ,@(mapcar #'call-in-body axiom))))))

Called like this ...

  (eval-production
    :iterations 2
    :axiom      (f + + f + + f)
    :expansions ((f . (f - f + + f - f)))
    :functions  ((f (prin1 'f))
                 (+ (prin1 '+))
                 (- (prin1 '-))))


... it macro expands to this:

  (let ((#:iters-value5737 2))
    (labels ((#:f-5739 (#:n5738)
               (if (zerop #:n5738)
                   (progn (prin1 'f))
                 (let ((#:n5738 (1- #:n5738)))
                   (#:f-5739 #:n5738)
                   (#:--5741)
                   (#:f-5739 #:n5738)
                   (#:+-5740)
                   (#:+-5740)
                   (#:f-5739 #:n5738)
                   (#:--5741)
                   (#:f-5739 #:n5738))))
             (#:+-5740 nil (prin1 '+))
             (#:--5741 nil (prin1 '-)))
      (#:f-5739 #:iters-value5737)
      (#:+-5740)
      (#:+-5740)
      (#:f-5739 #:iters-value5737)
      (#:+-5740)
      (#:+-5740)
      (#:f-5739 #:iters-value5737)))

Which generates the output I gave before:

  F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F++F-F++F-F-F-F++F-F

To see what's happening a bit more clearly you can turn off the
symbol translation and get this expansion:

  (let ((#:iters-value5784 2))
    (labels ((f (#:n5785)
               (if (zerop #:n5785)
                   (progn (prin1 'f))
                 (let ((#:n5785 (1- #:n5785)))
                   (f #:n5785)
                   (-)
                   (f #:n5785)
                   (+)
                   (+)
                   (f #:n5785)
                   (-)
                   (f #:n5785))))
             (+ nil (prin1 '+))
             (- nil (prin1 '-)))
      (f #:iters-value5784)
      (+)
      (+)
      (f #:iters-value5784)
      (+)
      (+)
      (f #:iters-value5784)))

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Kenny Tilton
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <H98ob.20785$Gq.6103742@twister.nyc.rr.com>
charlieb wrote:

> Peter Seibel wrote:
> 
>> charlieb <··@privacy.net> writes:
>>
>>
> <snip>
> 
>>
>>
>> Why can't you just write
>>
>>   `(let ,(close-let funcs) ...
>>
> When I do that clisp tells me that "funcs is not a list"
> 
>> ?
>>
>> But I think the real qustion is, what are you really trying to do?
> 
> The major point of this exercise is to better understand, for the first 
> question, how macros work and for the second question, when to use them. 
> By now I realise that this is probably not going to be a useful macro (I 
> had already written a function that has the same effect) but when I do 
> find a problem where only a macro will do I want to have had enough 
> practice to be able to write it without too much hair tearing.

:)

One does not learn to use a screwdriver by hammering a nail with the 
handle, you just fuck up the nail, the screwdriver, and your hand. I say 
wait for a Good Application(tm) of macros.

As for hair-tearing, don't forget that you can put print statements in 
the expand/ing/ code. (Click here if you do not want to see these tips 
each time you open an article from me.)


kenny

-- 
http://tilton-technology.com

Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film

Your Project Here! http://alu.cliki.net/Industry%20Application
From: charlieb
Subject: Re: Some Macro Questions.
Date: 
Message-ID: <bnr6lg$13hmiu$1@ID-208832.news.uni-berlin.de>
Kenny Tilton wrote:

> 
> :)
> 
> One does not learn to use a screwdriver by hammering a nail with the 
> handle, you just fuck up the nail, the screwdriver, and your hand. I say 
> wait for a Good Application(tm) of macros.
> 
> As for hair-tearing, don't forget that you can put print statements in 
> the expand/ing/ code. (Click here if you do not want to see these tips 
> each time you open an article from me.)
> 
> 
> kenny
> 
Ah, but you can learn to use a hammer by hammering screws you just won't 
end up with 1) A stable/useful bond and 2) Knowledge about when it's 
better to use a screwdriver. But you will know how to use a hammer! :)

An analogy is a lot like a drunkard; push it too hard and it'll fall 
over and puke on your shoes.

Thanks for the advice. When I find a bona-fide screw (yours) or nail 
(mine) I'm sure I'll have more questions for you.

Cheers.
Charlie.