From: Greg Menke
Subject: Macro fun
Date: 
Message-ID: <m3lmev55t3.fsf@europa.pienet>
In an ongoing process of setting up routines to remotely control our
logic analyzer, I wrote a few (with-* style macros to set up the
analyzer, configure it, etc..  That worked out pretty well, but I ran
into trouble with macrolet in one routine.  For example;

... stuff ...

(macrolet ((myfunction (e)  (cond ((var1
                                     `(do-something ,e var2 var3)))
                                  (t
                                     `(do-something-else ,e var4 var5)))))

   .. stuff ..

   (myfunction foo)


the idea being that myfunction becomes one of the 2 backquoted macros,
depending upon the value of var1 at the time the macrolet is entered.
I think I found that the var1 case was always in effect even when I
was fairly sure var1 was nil, though I certainly could be
misunderstanding what I saw.

I'm finding the role of "un-backquoted" code either in a defmacro or
in something like the macrolet above hard to understand, particularly
when trying to see how things work compiled vs interpreted.  I've seen
gensym stuff in some instances, tests against defmacro parameters in
others.  At this point I'm a little perplexed, though I can make
straightforward things work.

Is there an online resource about how macro definitions work?  I've
looked thru the CLHS, but I lack the background to properly understand
whats being presented there.

Thanks for any help,

Greg Menke

From: Kenny Tilton
Subject: Re: Macro fun
Date: 
Message-ID: <3C49027D.80760084@nyc.rr.com>
Greg Menke wrote:
> 
> For example;
> 
> ... stuff ...
> 
> (macrolet ((myfunction (e)  (cond ((var1
>                                      `(do-something ,e var2 var3)))
>                                   (t
>                                      `(do-something-else ,e var4 var5)))))
> 
>    .. stuff ..
> 
>    (myfunction foo)
> 

First, leave "stuff" to George Carlin. :) I mean, if you do not know
where the problem is, the problem may be in your stuff. So as not to
divulge your secret formula for cold fusion, you may have to distill the
actual macro down to a standalone example you can share, but (a) we'll
have a better shot at helping you, and (2) you'll probably figure out
the problem in the process.

You mentioned you were pretty sure var1 was nil. Can we start there? :)
I too had a devil of a time with macros early on, and one breakthru for
me was realizing I could print out stuff in the body of a macro and
'watch' the expansion. So print out var1, I imagine you'll find it is...
well, I better shut up, speculation will do more harm than good.

Hang in there. Being able to code macros without thinking is a sure sign
I need to get out more often.

kenny
clinisys
From: Greg Menke
Subject: Re: Macro fun
Date: 
Message-ID: <m3lmeuop5y.fsf@europa.pienet>
> > 
> > (macrolet ((myfunction (e)  (cond ((var1
> >                                      `(do-something ,e var2 var3)))
> >                                   (t
> >                                      `(do-something-else ,e var4 var5)))))
> > 
> >    .. stuff ..
> > 
> >    (myfunction foo)
> > 
> 
> First, leave "stuff" to George Carlin. :) I mean, if you do not know
> where the problem is, the problem may be in your stuff. So as not to
> divulge your secret formula for cold fusion, you may have to distill the
> actual macro down to a standalone example you can share, but (a) we'll
> have a better shot at helping you, and (2) you'll probably figure out
> the problem in the process.

In other words;

Re-examining the CLHS macrolet suggested what I was doing wrong,
'var1' (given it is in the macrolet's surrounding lexical environment)
as above is not defined to be accessible.

and

macroexpand is your friend.



I was trying to think of a witty comeback but nothing happened....

Thanks,

Gregm
From: Kaz Kylheku
Subject: Re: Macro fun
Date: 
Message-ID: <mL828.14780$0c.693294@news3.calgary.shaw.ca>
In article <··············@europa.pienet>, Greg Menke wrote:
>
>In an ongoing process of setting up routines to remotely control our
>logic analyzer, I wrote a few (with-* style macros to set up the
>analyzer, configure it, etc..  That worked out pretty well, but I ran
>into trouble with macrolet in one routine.  For example;
>
>... stuff ...
>
>(macrolet ((myfunction (e)  (cond ((var1
>                                     `(do-something ,e var2 var3)))
>                                  (t
>                                     `(do-something-else ,e var4 var5)))))
>
>   .. stuff ..
>
>   (myfunction foo)
>
>
>the idea being that myfunction becomes one of the 2 backquoted macros,
>depending upon the value of var1 at the time the macrolet is entered.
>I think I found that the var1 case was always in effect even when I
>was fairly sure var1 was nil, though I certainly could be
>misunderstanding what I saw.

You must assume that your macro is expanded only once. Its expansion
cannot therefore depend on dynamic state. The value of var1 will be
different each time the form containing the expanded macro is executed,
but the macro will be frozen just once. This could happen at compile
time, or when the code is read. Or it could be delayed until the
macro is evaluated for the first time.  A correctly written macro
works the same way regardless.

Your macro's job is to generate code. If that code must dynamically do
something different based on the state of a variable, then that code
must be written such that it evaluates var1 and handles all of the cases.

A good trick is to just write the code you want without the help of the macro.
Then think of how a macro might eliminate extraneous detail from that
code.

Suppose that in three places, you write this code.

(cond 
  (var1 (do-something (bleep) var2 var3))
  (t (do-something-else (bleep) var4 var5)))

;; ...

(cond 
  (var1 (do-something (bloop) var2 var3))
  (t (do-something-else (bloop) var4 var5)))

;; ...

(cond 
  (var1 (do-something (blorg) var2 var3))
  (t (do-something-else (blorg) var4 var5)))

Shoot, that's a lot of repetitive coding? What can you do? Well, you can
make a function. Or you cold make a macro. The only variable part is
the (bloop), (bleep) and (blorg). You will want to supply this form 
as your one and only macro parameter. Say that parameter is e.
Then you can just have this one backquote in your macro:

`(cond 
  (var1 (do-something ,e var2 var3))
  (t (do-something-else ,e 5)))

>I'm finding the role of "un-backquoted" code either in a defmacro or
>in something like the macrolet above hard to understand, particularly

Note that the backquote tool is distinct from macros. You can use
it wherever you want.

The whole idea behidn backquoting is to have a visual tool for
constructing lists, by making a picture of what they look like,
and indicating that things are to be inserted.

Suppose you have some bound variables a, b, c, d, e, f and construct
this list:

  ;; Note: f is assumed to be bound to a list!

  (list a b 'sym1 'sym2 (list c (append (list d e) f)))

you can do it instead with a backquote:

  `(,a ,b sym1 sym2 (,c (,d ,e ,@f)))

This notation is quite convenient, especially when you have 
lots of invariant parts in the list and lots of symbols that
would normally require quoting. We no longer have to quote
sym1 and sym2. And list f is spliced in nicely using the ,@
notation rather than using append.

Without that notation, you could still write macros using explicit
constructing functions to generate the form you want.

E.g suppose you have this macro:

(defmacro one-var-let (var-or-var-init &body forms)
  `(let (,var-or-var-init) ,@forms))

The job of this useless thing is to behave like let, but only
let you define one variable, e.g.

	(one-var-let (x) ...)
	(one-var-let ((z 3)) ...)

Without the backquote you could write it like this:

(defmacro one-var-let (var-or-var-init &body forms)
  ;; huff and puff, append, list, quote...
  (append (list 'let (list var-or-var-init)) forms))

You see how this is hard to follow? Someone got sick of doing this,
and so the backquote thing was invented to do it more visually,
so you can write the expansion in almost the same way you write ordinary
Lisp code.  In other words allows the *implementing* program to
closely resemble the *implemented* program. But that resemblance also
can create more confusion.

>when trying to see how things work compiled vs interpreted.  I've seen
>gensym stuff in some instances, tests against defmacro parameters in
>others.

Gensym is needed when you need to generate code, and that code needs
its own variables. If a programmer wrote the code by hand, he or she
would invent symbol names; these would be read by Lisp and turned
into interned Lisp symbol objects.

A macro isn't writing code to be read, it's writing an object directly.
So when it needs symbols, it generates them using (gensym), and
retains them by binding them to local variables. Then when it needs
to inject these symbols into the code it is generating, it, for
instance, sticks them into a backquoted template.

Here is how you construct a list that can serve as a let form,
one which invents a unique variable, and binds it to the value 42:

  (let ((invented-variable (gensym))
    ;; okay, we have the invented variable, let's stick it into
    ;; the let form:
    `(let ((,invented-variable 42)) ...)))

  At this point I'm a little perplexed, though I can make
>straightforward things work.
>
>Is there an online resource about how macro definitions work?  I've
>looked thru the CLHS, but I lack the background to properly understand
>whats being presented there.

You might want to read the section on the backquote; it's described
separately from macros.
From: Greg Menke
Subject: Re: Macro fun
Date: 
Message-ID: <m3zo3acs2l.fsf@europa.pienet>
> >when trying to see how things work compiled vs interpreted.  I've seen
> >gensym stuff in some instances, tests against defmacro parameters in
> >others.
> 
> Gensym is needed when you need to generate code, and that code needs
> its own variables. If a programmer wrote the code by hand, he or she
> would invent symbol names; these would be read by Lisp and turned
> into interned Lisp symbol objects.
> 
> A macro isn't writing code to be read, it's writing an object directly.
> So when it needs symbols, it generates them using (gensym), and
> retains them by binding them to local variables. Then when it needs
> to inject these symbols into the code it is generating, it, for
> instance, sticks them into a backquoted template.
> 
> Here is how you construct a list that can serve as a let form,
> one which invents a unique variable, and binds it to the value 42:
> 
>   (let ((invented-variable (gensym))
>     ;; okay, we have the invented variable, let's stick it into
>     ;; the let form:
>     `(let ((,invented-variable 42)) ...)))
> 

Thanks for your very detailed reply!  In this case, is the advantage
of introducing gensym'ed symbols over programmer-generated symbols
that they can't obscure (shadow?) "userspace" symbols which by
accident could have the same name?


Gregm
From: Thomas F. Burdick
Subject: Re: Macro fun
Date: 
Message-ID: <xcv665yw1wx.fsf@conquest.OCF.Berkeley.EDU>
Greg Menke <··········@mindspring.com> writes:

> Thanks for your very detailed reply!  In this case, is the advantage
> of introducing gensym'ed symbols over programmer-generated symbols
> that they can't obscure (shadow?) "userspace" symbols which by
> accident could have the same name?

Yes, or even macro-generated symbols.  Think, for example, of:

  (defmacro with-frobbing-to (destination &body body)
    `(let ((=x= ,destination))
       (macrolet ((frob (thing)
                    `(frob::internal-frob :destination =x= :thing thing)))
         ,@body)))

  (defmacro with-foo ((var) &body body)
    `(let ((=x= (make-foo)))
       (unwind-protect
           (let ((,var =x=))
             ,@body)
         (destroy-foo =x=))))

Now the form:

  (with-frobbing-to *destination*
    (frob 1)
    (with-foo (foo)
      (frob foo)))

Is will be expanded to:

  (let ((=x= *destination*))
    (frob::internal-frob :destination =x= :thing thing)
    (let ((=x= (make-foo)))
      (unwind-protect
          (let ((foo =x=))
            (frob::internal-frob :destination =x=  ; This is not *destination*!
                                 :thing foo))
        (destroy-foo =x=))))

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Kaz Kylheku
Subject: Re: Macro fun
Date: 
Message-ID: <GVg28.15647$0c.764615@news3.calgary.shaw.ca>
In article <··············@europa.pienet>, Greg Menke wrote:
>
>> >when trying to see how things work compiled vs interpreted.  I've seen
>> >gensym stuff in some instances, tests against defmacro parameters in
>> >others.
>> 
>> Gensym is needed when you need to generate code, and that code needs
>> its own variables. If a programmer wrote the code by hand, he or she
>> would invent symbol names; these would be read by Lisp and turned
>> into interned Lisp symbol objects.
>> 
>> A macro isn't writing code to be read, it's writing an object directly.
>> So when it needs symbols, it generates them using (gensym), and
>> retains them by binding them to local variables. Then when it needs
>> to inject these symbols into the code it is generating, it, for
>> instance, sticks them into a backquoted template.
>> 
>> Here is how you construct a list that can serve as a let form,
>> one which invents a unique variable, and binds it to the value 42:
>> 
>>   (let ((invented-variable (gensym))
>>     ;; okay, we have the invented variable, let's stick it into
>>     ;; the let form:
>>     `(let ((,invented-variable 42)) ...)))
>> 
>
>Thanks for your very detailed reply!  In this case, is the advantage
>of introducing gensym'ed symbols over programmer-generated symbols
>that they can't obscure (shadow?) "userspace" symbols which by
>accident could have the same name?

Yes. Or rather it's not the *same names* that matter but rather that
the symbols could be the *same objects*. If the user writes ABC and the
macro's source code also contains a reference to ABC, and both
are read in the same package, then they resolve to the same symbol object.

But names only matter when a symbol is being read, or written out.

The key idea is that each call to gensym allocates a new object (of
symbol type) that, by virtue of being new, is not EQ to any other object
in the Lisp system. That symbol has a name based on a prefix and an
incrementing integer, but that name is a red herring. The uniqueness of
the gensym does not in any way depend on that prefix and incrementing
number.

The number-suffixed gensym names are only for human readability of the
printed object. Even if you reset the counter inside (gensym) so that
it generates a symbol named G0001 a second time, that symbol
object is different from the previously generated G0001. They merely
have the same name.
From: Thomas A. Russ
Subject: Re: Macro fun
Date: 
Message-ID: <ymibsfmm1ec.fsf@sevak.isi.edu>
Greg Menke <··········@mindspring.com> writes:

> In an ongoing process of setting up routines to remotely control our
> logic analyzer, I wrote a few (with-* style macros to set up the
> analyzer, configure it, etc..  That worked out pretty well, but I ran
> into trouble with macrolet in one routine.  For example;
> 
> ... stuff ...
> 
> (macrolet ((myfunction (e)  (cond ((var1
>                                      `(do-something ,e var2 var3)))
>                                   (t
>                                      `(do-something-else ,e var4 var5)))))
> 
>    .. stuff ..
> 
>    (myfunction foo)
> 
> 
> the idea being that myfunction becomes one of the 2 backquoted macros,
> depending upon the value of var1 at the time the macrolet is entered.
> I think I found that the var1 case was always in effect even when I
> was fairly sure var1 was nil, though I certainly could be
> misunderstanding what I saw.

Well, you could try interspersing the macro with print statements to get
a better handle on what the Macro is seeing when it is being expanded.
For example:

  (macrolet ((myfunction (e)  (format t 
                                      "Expanding myfunction~% var1=~s~%"
				       var1)
			      (cond ((var1
                                      `(do-something ,e var2 var3)))
                                   (t
                                      `(do-something-else ,e var4 var5)))))
 
    .. stuff ..
 
    (myfunction foo) ...)


-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu