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.