Macro fun 
Author Message
 Macro fun

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



Wed, 07 Jul 2004 12:21:44 GMT  
 Macro fun

Quote:

> 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



Wed, 07 Jul 2004 13:17:26 GMT  
 Macro fun

Quote:

> > (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



Wed, 07 Jul 2004 14:00:57 GMT  
 Macro fun

Quote:

>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)))

Quote:
>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:


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

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)

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.

Quote:
>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

Quote:
>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.


Wed, 07 Jul 2004 14:55:14 GMT  
 Macro fun

Quote:
> >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



Wed, 07 Jul 2004 22:51:14 GMT  
 Macro fun

Quote:

>> >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.



Thu, 08 Jul 2004 00:12:22 GMT  
 Macro fun

Quote:

> 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)))

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

         (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!       |                        
    /       /      `-----------------------'                        
   (   -.  |                              
   |     ) |                              
  (`-.  '--.)                              
   `. )----'                              



Thu, 08 Jul 2004 03:55:58 GMT  
 Macro fun

Quote:

> 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) ...)

--



Sun, 11 Jul 2004 07:06:19 GMT  
 
 [ 8 post ] 

 Relevant Pages 

1. count bits, fun fun fun.

2. more fun with macros

3. A macro involving two sub-macros - where the 2nd macro needs results from the first

4. macro -vs- macro/codeblock

5. Help with macros writing macros in Bigloo

6. syntax-rules macros with sub-macros

7. Scheme macro source: rewriter-widget, a widely applicable macro interface

8. Macros defining macros with define-syntax

9. symbol-macros and regular macros

10. Question about a macro-defining macro

11. Macro-Defining Macros

12. macro macros

 

 
Powered by phpBB® Forum Software