Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Controlling the expansion of a macro

16 views
Skip to first unread message

l...@healy.washington.dc.us

unread,
Jul 4, 2000, 3:00:00 AM7/4/00
to
I would like to define macros whose expansion can be controlled by a
enclosing form. For the sake of illustration, suppose I
want to have a macro, plusminus, that expands to a form that either adds or
subtracts its arguments. Now I would like to have enclosing form(s)
that control the expansion of plusminus, e.g.
(adding-all
(foo (* (bar (plusminus x y)) (plusminus z w))))
will expand into
(foo (* (bar (+ x y)) (+ z w)))
and
(subtracting-all
(foo (* (bar (plusminus x y)) (plusminus z w))))
will expand into
(foo (* (bar (- x y)) (- z w)))

If I were dealing with functions, I would defvar some variable
*plusminus* and make adding-all and subtracting-all be macros that
let bind that variable. But with a macro, say,
(defmacro plusminus (a b)
(if *plusminus*
`(+ ,a ,b)
`(- ,a ,b)))
a let binding of *plusminus* by adding-all is not visible at
macroexpand time, so having adding-all bind it is fruitless.

The only solution I have come up with is to use macrolet, e.g.
make a new macro
(defmacro plusminus-int (a b pm)
(if pm
`(+ ,a ,b)
`(- ,a ,b)))
then macrolet plusminus in the wrapping macro,
(defmacro adding-all (&body body)
`(macrolet ((plusminus (a b) `(plusminus-int ,a ,b t)))
,@body))
now the form
(adding-all (foo (* (bar (plusminus x y)) (plusminus z w))))
will expand correctly.

While this approach works, it would be clumsy for the application I
have in mind, because the controlling variable has to be passed along
in the lambda list. Is there a "special variable" type of approach
that I can use, or is this just dreaming?

Side question: In ACL, the scope of a macroletted name encompasses the
macro definitions themselves, rather like a labels instead of a flet.
Is this standard? (CLHS doesn't seem to say, but I may not be reading
it correctly.) Since recursive macros are disallowed, wouldn't it be
more useful to not include the definitions; then, a macrolet macro
could both shadow and use a global one; e.g., I could use plusminus
for both macros above instead of having a plusminus-int. Yes, I can
see the value of having succeeding macroletted definitions (like let*)
- but I like the idea of shadowing a macro better.

Side question: Symbolics had a compiler-let which maybe would solve
this problem? Is there an equivalent in ANSI CL?

Thanks for insight.


Liam


Erik Naggum

unread,
Jul 4, 2000, 3:00:00 AM7/4/00
to
* l...@healy.washington.dc.us

| I would like to define macros whose expansion can be controlled by a
| enclosing form.
:

| The only solution I have come up with is to use macrolet, e.g.
| make a new macro
| (defmacro plusminus-int (a b pm)
| (if pm
| `(+ ,a ,b)
| `(- ,a ,b)))
| then macrolet plusminus in the wrapping macro,
| (defmacro adding-all (&body body)
| `(macrolet ((plusminus (a b) `(plusminus-int ,a ,b t)))
| ,@body))
| now the form
| (adding-all (foo (* (bar (plusminus x y)) (plusminus z w))))
| will expand correctly.

Why the complicating plusminus-int? You may define the macro body
directly:

(defmacro adding-all (&body body)
`(macrolet ((plusminus (a b) `(+ ,a ,b)))
,@body))

| While this approach works, it would be clumsy for the application I
| have in mind, because the controlling variable has to be passed
| along in the lambda list. Is there a "special variable" type of
| approach that I can use, or is this just dreaming?

Well, I don't see why you need it in this case.

| Side question: In ACL, the scope of a macroletted name encompasses the
| macro definitions themselves, rather like a labels instead of a flet.
| Is this standard? (CLHS doesn't seem to say, but I may not be reading
| it correctly.)

It is my understanding that this is mandated by the standard.

| Since recursive macros are disallowed ...

Are they?

| Side question: Symbolics had a compiler-let which maybe would solve
| this problem? Is there an equivalent in ANSI CL?

Not in ANSI CL, but in Allegro CL, you have excl::compiler-let.
It can be indispensable in certain situations.

#:Erik
--
If this is not what you expected, please alter your expectations.

Kent M Pitman

unread,
Jul 4, 2000, 3:00:00 AM7/4/00
to
l...@healy.washington.dc.us writes:

> [Query where MACROLET is the right answer elided.]


>
> While this approach works, it would be clumsy for the application I
> have in mind, because the controlling variable has to be passed along
> in the lambda list. Is there a "special variable" type of approach
> that I can use, or is this just dreaming?

Macro expansion happens at compile time, before the time when special
variables are available (other than compiler switches). If you are
passing runtime special variables and expecting them to affect compile
time, you may have bigger problems than you think. Implementations
differ dramatically in how aggressively they expand macros in interpreted
code, and it's not hard in some implementations to get a partly or
non-expanded piece of code caught in a closure and moved to a place where
the special variable binding is not in effect. I *strongly* recommend
that you reconsider your data flow and make it fully lexical, so that
the MACROLET solution will suffice.



> Side question: In ACL, the scope of a macroletted name encompasses the
> macro definitions themselves, rather like a labels instead of a flet.
> Is this standard? (CLHS doesn't seem to say, but I may not be reading
> it correctly.)

I didn't check ACL to see if what you say is true. I also didn't check
the spec to see if it was 100% clear. However, this doesn't sound like
what I would expect.

> Side question: Symbolics had a compiler-let which maybe would solve
> this problem? Is there an equivalent in ANSI CL?

COMPILER-LET was part of CLTL1, but was removed in ANSI CL because of
the substantial confusions about evaluation environment that I cited
in the first paragraph of my reply above. It was often implemented as
a mere special-bind in intrpreted code, and this often caused people
to asssure mistakenly that it would be a binding that would be
available when the code was compiled. Since CL tries to offer
semantics that is immune to changes that occur between interpreted and
compiled code, this created a lot of confusion. There are a few kinds
of programs that are hard to rewrite, but overall most people feel the
change was worth it. One person, notably, who is bugged by the change
is Dick Waters because his SERIES stuff (similar to your stuff here in
its use of macros) gets tripped up by it. It was written in CLTL1 days
and it is a pain to rewrite with the new scheme (I know--I've spent days
trying).

There is a rewrite that works for simpler cases, and *might* help you.
See X3J13 issue COMPILER-LET-CONFUSION in the HyperSpec for details.

William Deakin

unread,
Jul 5, 2000, 3:00:00 AM7/5/00
to
l...@healy.washington.dc.us wrote:
> Side question: ... Since recursive macros are disallowed, wouldn't it be
> more useful to not include the definitions; ...
As a side point (to your side question :) recursive macros are not
disallowed merely `hard'[1] (IIRC Paul Graham in `On Lisp' deals with
this).

Best Regards,

:)will

[1] This is a language lawyer point: I use `hard' advisedly, and in a
sense similar to `I find it hard to get up in mornings.'

Paolo Amoroso

unread,
Jul 5, 2000, 3:00:00 AM7/5/00
to
On Tue, 4 Jul 2000 20:31:50 GMT, Kent M Pitman <pit...@world.std.com>
wrote:

> is Dick Waters because his SERIES stuff (similar to your stuff here in
> its use of macros) gets tripped up by it. It was written in CLTL1 days
> and it is a pain to rewrite with the new scheme (I know--I've spent days
> trying).

By the way, SERIES is currently maintained by Fernando Mato Mira:

http://series.sourceforge.net/


Paolo
--
EncyCMUCLopedia * Extensive collection of CMU Common Lisp documentation
http://cvs2.cons.org:8000/cmucl/doc/EncyCMUCLopedia/

Pekka P. Pirinen

unread,
Jul 6, 2000, 3:00:00 AM7/6/00
to
Kent M Pitman <pit...@world.std.com> writes:

> l...@healy.washington.dc.us writes:
> > Side question: In ACL, the scope of a macroletted name encompasses the
> > macro definitions themselves, rather like a labels instead of a flet.
> > Is this standard? (CLHS doesn't seem to say, but I may not be reading
> > it correctly.)
>
> I didn't check ACL to see if what you say is true. I also didn't check
> the spec to see if it was 100% clear. However, this doesn't sound like
> what I would expect.

Kent is right, as usual. The definition of MACROLET says "The
macro-expansion functions defined by MACROLET are defined in the
lexical environment in which the MACROLET form appears.", so it
definitely does not include the names defined by the MACROLET form
itself.

A MACROLABELS doesn't seem very useful, either. Recursive
macroexpansions are sometimes neat, e.g.,
(let* ((foo ...) (bar ...))) => (let ((foo ...)) (let* ((bar ...))))
but you can do that with MACROLET. True recursive macros don't really
work in CL.
--
Pekka P. Pirinen, Adaptive Memory Management Group, Harlequin Limited
(format t "~@?" "(format t \"~~@?\" ~:*~S)")

l...@healy.washington.dc.us

unread,
Jul 6, 2000, 3:00:00 AM7/6/00
to
>>>>> "Pekka" == Pekka P Pirinen <pe...@harlequin.co.uk> writes:

Pekka> Kent M Pitman <pit...@world.std.com> writes:
>> l...@healy.washington.dc.us writes:
>> > Side question: In ACL, the scope of a macroletted name encompasses the
>> > macro definitions themselves, rather like a labels instead of a flet.
>> > Is this standard? (CLHS doesn't seem to say, but I may not be reading
>> > it correctly.)
>>
>> I didn't check ACL to see if what you say is true. I also didn't check
>> the spec to see if it was 100% clear. However, this doesn't sound like
>> what I would expect.

Pekka> Kent is right, as usual. The definition of MACROLET says "The
Pekka> macro-expansion functions defined by MACROLET are defined in the
Pekka> lexical environment in which the MACROLET form appears.", so it
Pekka> definitely does not include the names defined by the MACROLET form
Pekka> itself.

Then should
(macrolet ((list (&rest args) `(list 1 ,@args))) (list 'a 'b))
return (1 a b) as
(macrolet ((lost (&rest args) `(list 1 ,@args))) (lost 'a 'b))
does?

I tried ACL 5.0.1, CMUCL 2.4.19, CLISP 1999-07-22 and they all appear to
get hung, I assume in infinite recursion. If I interrupt ACL and
look at the backtrace, there is
(LIST (LIST 1 1 1 1 1 1 1 1 1 ...)
(:EVALUATION-ENVIRONMENT
((LIST EXCL::MACRO #<Interpreted Function LIST @ #x204b2352>))))
which certainly looks like it's recursively applying the macroletted
list.

Are these consistent with the sentence quoted from the spec? I'm
stumped. If it were one implementation I'd say it was a bug, but
three agree, so are we misinterpreting the spec? It says the
macro-expansion functions defined are in the lexical environment of
the macrolet, does that imply that the macros defined exclude their
own definitions?

Liam

Barry Margolin

unread,
Jul 7, 2000, 3:00:00 AM7/7/00
to
In article <51em56l...@zip.local>, <l...@healy.washington.dc.us> wrote:
>>>>>> "Pekka" == Pekka P Pirinen <pe...@harlequin.co.uk> writes:
>
> Pekka> Kent M Pitman <pit...@world.std.com> writes:
> >> l...@healy.washington.dc.us writes:
> >> > Side question: In ACL, the scope of a macroletted name encompasses the
> >> > macro definitions themselves, rather like a labels instead of a flet.
> >> > Is this standard? (CLHS doesn't seem to say, but I may not be reading
> >> > it correctly.)
> >>
> >> I didn't check ACL to see if what you say is true. I also didn't check
> >> the spec to see if it was 100% clear. However, this doesn't sound like
> >> what I would expect.
>
> Pekka> Kent is right, as usual. The definition of MACROLET says "The
> Pekka> macro-expansion functions defined by MACROLET are defined in the
> Pekka> lexical environment in which the MACROLET form appears.", so it
> Pekka> definitely does not include the names defined by the MACROLET form
> Pekka> itself.
>
>Then should
> (macrolet ((list (&rest args) `(list 1 ,@args))) (list 'a 'b))
>return (1 a b) as
> (macrolet ((lost (&rest args) `(list 1 ,@args))) (lost 'a 'b))
>does?

No.

>I tried ACL 5.0.1, CMUCL 2.4.19, CLISP 1999-07-22 and they all appear to
>get hung, I assume in infinite recursion. If I interrupt ACL and
>look at the backtrace, there is
>(LIST (LIST 1 1 1 1 1 1 1 1 1 ...)
> (:EVALUATION-ENVIRONMENT
> ((LIST EXCL::MACRO #<Interpreted Function LIST @ #x204b2352>))))
>which certainly looks like it's recursively applying the macroletted
>list.

This is correct.

>Are these consistent with the sentence quoted from the spec? I'm
>stumped. If it were one implementation I'd say it was a bug, but
>three agree, so are we misinterpreting the spec? It says the
>macro-expansion functions defined are in the lexical environment of
>the macrolet, does that imply that the macros defined exclude their
>own definitions?

It's referring to the environment in which the body of the macro function
is evaluated, not the environment in which the expansion is evaluated. In
other words:

(let ((lost (&rest args) ;1
(apply #'list args)))
(macrolet ((lost (arg) ;2
(lost 'cons 1 arg))) ;3
(lost 3))) ;4

will not result in infinite recursion. The use of LOST on line 3 refers to
the LOST binding on line 1, the use on line 4 refers to the binding on line
2.

--
Barry Margolin, bar...@genuity.net
Genuity, Burlington, 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.

l...@healy.washington.dc.us

unread,
Jul 8, 2000, 3:00:00 AM7/8/00
to
>>>>> "Barry" == Barry Margolin <bar...@genuity.net> writes:

...


Pekka> Kent is right, as usual. The definition of MACROLET says "The
Pekka> macro-expansion functions defined by MACROLET are defined in the
Pekka> lexical environment in which the MACROLET form appears.", so it
Pekka> definitely does not include the names defined by the MACROLET form
Pekka> itself.
>>
>> Then should
>> (macrolet ((list (&rest args) `(list 1 ,@args))) (list 'a 'b))
>> return (1 a b) as
>> (macrolet ((lost (&rest args) `(list 1 ,@args))) (lost 'a 'b))
>> does?

Barry> No.

>> I tried ACL 5.0.1, CMUCL 2.4.19, CLISP 1999-07-22 and they all appear to
>> get hung, I assume in infinite recursion. If I interrupt ACL and
>> look at the backtrace, there is
>> (LIST (LIST 1 1 1 1 1 1 1 1 1 ...)
>> (:EVALUATION-ENVIRONMENT
>> ((LIST EXCL::MACRO #<Interpreted Function LIST @ #x204b2352>))))
>> which certainly looks like it's recursively applying the macroletted
>> list.

Barry> This is correct.

>> Are these consistent with the sentence quoted from the spec? I'm
>> stumped. If it were one implementation I'd say it was a bug, but
>> three agree, so are we misinterpreting the spec? It says the
>> macro-expansion functions defined are in the lexical environment of
>> the macrolet, does that imply that the macros defined exclude their
>> own definitions?

Barry> It's referring to the environment in which the body of the macro function
Barry> is evaluated, not the environment in which the expansion is evaluated. In
Barry> other words:

Barry> (let ((lost (&rest args) ;1
Barry> (apply #'list args)))
Barry> (macrolet ((lost (arg) ;2
Barry> (lost 'cons 1 arg))) ;3
Barry> (lost 3))) ;4

Barry> will not result in infinite recursion. The use of LOST on line 3 refers to
Barry> the LOST binding on line 1, the use on line 4 refers to the binding on line
Barry> 2.

let on line 1 should be macrolet?

In any case: not to beat a dead horse, but -
we agree the sentence quoted in the standard makes no statement about
whether the scope of a macrolet definition encompasses the defintions
themselves, and I can't find anything in CLHS one way or the other on
this, yet in three implementations I tested all do include the
definitions. Is this a de facto standard? Why? I can see a reason for
excluding the definitions, so that an existing definition may be
shadowed but still use the original definition as with my list example
above. I can't see a reason for including them other than the
convenience of successive definitions (e.g. let* vs. let).

Liam

Erik Naggum

unread,
Jul 8, 2000, 3:00:00 AM7/8/00
to
* Pekka P. Pirinen
| The definition of MACROLET says "The macro-expansion functions
| defined by MACROLET are defined in the lexical environment in which
| the MACROLET form appears.", so it definitely does not include the
| names defined by the MACROLET form itself.

I've been reading this over several times, and I cannot fathom how
you conclude what you do. Without the "not", it seems abundantly
clear, however. I read it to state that macrolet does not create a
_new_ lexical environment, but rather to use the lexical environment
the macrolet appears in, which means that all the macros are
available simultaneously at _expansion_ time.

I'm not sure what the confusion is, but there is clearly a confusion
when referring to "recursive" macros. Let me illustrate what I mean:

(macrolet ((foo (x) `(progn (bar x) (first ,x)))
(bar (x) (ignore-errors (foo x)))
(zot (x) `(foo ,x)))
(let ((y (list 1 2 3)))
(zot y)))

A call to bar yields an error, as expected, but a call to zot
expands to (progn nil (first y)), also as expected, which means that
(1) the call to bar from foo happens at macro-expansion time, when
bar is defined, (2) the call to foo from bar refers to a foo outside
the lexical scope of the macrolet which doesn't exist, and (3) the
call to foo from zot happens at macro-expansion time.

I find all of this eminently supported by the standard, so I'm not
even sure what Pekka is arguing against, either.

0 new messages