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

macrolet like flet

28 views
Skip to first unread message

rocco...@gmail.com

unread,
Apr 13, 2008, 6:38:35 AM4/13/08
to
I read in Paul Graham's Ansi Common Lisp that MACROLET behaves (in
analogy) somewhat like FLET in the sense that the local macros may not
call one another. But what if the desired behavior were more like
LABELS, where not only could they call one another, but you could have
recursive macro calls as well, for instance.

Is there some other special form for doing that? Or can it be achieved
at all?

Thank you.

Matthias Benkard

unread,
Apr 13, 2008, 6:49:53 AM4/13/08
to
Hi,

I'm not sure I mean the same by `calling' in this case as PG does, but
if I do, it wouldn't make any sense at all, because macros are called
at compile-time, at this point they have to have been compiled
already.

Note that the following does work, because the local macro FOO expands
to (rather than calling) itself, so it's called from within the body
of the macrolet form, not from within the definition of FOO:

CL-USER> (macrolet ((foo (n)
(if (plusp n)
`(progn
(foo ,(1- n))
(print ,n)
(values))
nil)))
(foo 3))

1
2
3
; No value

Hope this helps,
~ Matthias

Alex Mizrahi

unread,
Apr 13, 2008, 7:16:58 AM4/13/08
to
rr> I read in Paul Graham's Ansi Common Lisp that MACROLET behaves (in
rr> analogy) somewhat like FLET in the sense that the local macros may not
rr> call one another. But what if the desired behavior were more like
rr> LABELS, where not only could they call one another, but you could have
rr> recursive macro calls as well, for instance.

either you're missing the way how macros work -- they do not _call_ each
other, but just code generated by one macro might contain references to
another one, so it just needs one more round of macroexpansion.
or you're doing something impressive like macro-defining-macros, then we'd
appreciated if you share this stuff with us!

for example,

(macrolet ((n-times-repeater (n x) ``(progn ,@(loop repeat ,n collect ,x))))
(macrolet ((repeat-3-times (y) (n-times-repeater 3 y)))
(repeat-3-times (print "recursive macros are mind-boggling"))))

this won't work in single macrolet -- it will complain about
n-times-repeater being not defined.
i'm afraid i'm not able to invent anything that can't be handled with nested
macrolets..


rocco...@gmail.com

unread,
Apr 13, 2008, 7:36:35 AM4/13/08
to
On Apr 13, 1:16 pm, "Alex Mizrahi" <udode...@users.sourceforge.net>
wrote:

Well, to tell you the truth, Alex, concerning the "calling" part, I
was merely repeating Graham's words (quote):

"(macrolet ((symbol parameters . body)*)
Special Operator
declaration* expression*)
Evaluates its body with each symbol defined locally to be the
corresponding
macro. The expansion functions are defined in the lexical
environment where
the macrolet expression occurs. Like f l e t , the local macros
may not call
one another."

As you can see, he explicitly says " ... the local macros may not call
one another." So I guess he's got this calling business all wrong,
perhaps ;)

I gather, from what you're saying, that the only way to do what I was
asking is through the use of nested MACROLETs. Am I correct?

Thanks.

Alex Mizrahi

unread,
Apr 13, 2008, 8:09:15 AM4/13/08
to
rr> Well, to tell you the truth, Alex, concerning the "calling" part, I
rr> was merely repeating Graham's words (quote):

rr> "(macrolet ((symbol parameters . body)*)
rr> Special Operator
rr> declaration* expression*)
rr> Evaluates its body with each symbol defined locally to be the
rr> corresponding
rr> macro. The expansion functions are defined in the lexical
rr> environment where
rr> the macrolet expression occurs. Like f l e t , the local macros
rr> may not call
rr> one another."

rr> As you can see, he explicitly says " ... the local macros may not call
rr> one another."

that is correct. but calling local macros from each other is not what people
want in 99.9% cases.
they might macroexpand recursively, that that just works.

rr> I gather, from what you're saying, that the only way to do what I was
rr> asking is through the use of nested MACROLETs. Am I correct?

i still haven't seen what exactly are you doing, so i don't know whether it
should be handled with single macrolet, two nested ones, or it's not
possible to in lisp.
my best guess is that you just need recursive macroexpansion, and this just
works with single macrolet


Lars Rune Nøstdal

unread,
Apr 13, 2008, 8:16:16 AM4/13/08
to

..create a MACROLET* macro that does the same thing LET* or LABELS does
(nests for you) .. something like this:

(defmacro macrolet* (definitions &body body)
(if (null definitions)
`(progn ,@body)
`(macrolet (,(first definitions))
(macrolet* ,(rest definitions)
,@body))))


--
Lars Rune Nøstdal
http://nostdal.org/

Alan Crowe

unread,
Apr 13, 2008, 8:53:54 AM4/13/08
to
rocco...@gmail.com writes:

I was puzzled by the idea that macrolet works like flet,
because I've been burned by macro expansions going into
infinite loops. Very sore.

Once a macro has been expanded, the form that resulted is
resubmitted for possible further expansion. It is
resubmitted to the same lexical environment in which it was
first expanded. There is no marking of the macrolet which
expanded it as `done' or 'used'. So this works

CL-USER> (macrolet ((cover (&rest stuff)
(if (endp stuff)
''the-end
`(list (quote ,(first stuff))
(cover ,@(rest stuff))))))
(cover a b c))

(A (B (C THE-END)))

(cover a b c) expands to (list 'a (cover b c))
then (list 'a (list 'b (cover c))
then (list 'a (list 'b (list 'c (cover))))
then (list 'a (list 'b (list 'c 'the-end)))

But we can elaborate on Alex's example by using COVER in
both the expansion and the code that generates it.

CL-USER> (macrolet ((cover (&rest stuff)
(format *debug-io* "~&Outer (cover ~A)" stuff)
(length stuff)))
(macrolet ((cover (&rest stuff)
(format *debug-io* "~&Inner (cover ~A)" stuff)
(if (endp stuff)
(cover x y z)
`(list ,(cover stuff)
(cover ,@(rest stuff))))))
(cover a b c)))

Outer (cover (X Y Z))
Outer (cover (STUFF))
Inner (cover (A B C))
Inner (cover (B C))
Inner (cover (C))
Inner (cover NIL)

(1 (1 (1 3)))

Notice CMUCL being quite eager about macroexpanding
macroexpanders. If we quote the call of COVER so it is just
data and doesn't get expanded, the Outer calls are still
made.

Alan Crowe
Edinburgh
Scotland

rocco...@gmail.com

unread,
Apr 13, 2008, 8:57:28 AM4/13/08
to
On Apr 13, 2:16 pm, Lars Rune Nøstdal <larsnost...@gmail.com> wrote:

That is so cool! Just goes to show the power of macros :)

Thank you Lars.

One last thing. I've just been going over Paul Graham's book, and I
simply can't manage to find a confirmation of his statement. Namely,
".. Like FLET, the local macros may not call one another."

Are we sure this is correct in the first place???

I wonder if Mr. Pitman would be so kind as to illuminate us on this
matter.

Pascal Costanza

unread,
Apr 13, 2008, 8:59:12 AM4/13/08
to

Indeed, he should have said something like "invoke each other."
"Calling" macros creates the wrong intuition.

> I gather, from what you're saying, that the only way to do what I was
> asking is through the use of nested MACROLETs. Am I correct?

It's rare that you think of macros invoking each other in the same way
that functions recursively call each other. Of course, a macro may just
use (invoke) other macros in their definitions, but that's not a very
special thing to do. Indeed, it happens more often than you might be
aware of.

What's more interesting, though, is that the process of macro expansion
may be recursive. That doesn't happen so often, but is an interesting case.

Here is an example:

(defmacro my-let* ((&rest bindings) &body body)
(if bindings
`(let (,(first bindings))
(my-let* ,(rest bindings) ,@body))
`(progn ,@body)))

Here, my-let* expands into a use of itself. You have the same
requirements as for recursive functions: You need a base case, and in
each recursive step, whatever the macro does should be a step closer to
that base case, in order to guarantee that expansion process terminates.


Pascal

--
1st European Lisp Symposium (ELS'08)
http://prog.vub.ac.be/~pcostanza/els08/

My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

rocco...@gmail.com

unread,
Apr 13, 2008, 8:59:44 AM4/13/08
to

Sorry, I meant to say I can't find a confirmation of Graham's
statement in the HyperSpec.

Alex Mizrahi

unread,
Apr 13, 2008, 9:32:21 AM4/13/08
to
LRN> ..create a MACROLET* macro that does the same thing LET* or LABELS
LRN> does (nests for you) .. something like this:

LABELS does more than nesting, it's not _that_ easy to emulate it -- you
need sort of Y combinator to do this.
see here: http://home.pipeline.com/~hbaker1/MetaCircular.html


Ron Garret

unread,
Apr 13, 2008, 11:31:12 AM4/13/08
to
In article
<417286d4-f254-4068...@8g2000hse.googlegroups.com>,
rocco...@gmail.com wrote:

It is correct, but potentially misleading. If you have:

(macrolet ((m1 ...) (m2 ...)) ...)

the m1 may not use m2 in its macroexpander, but m2 may appear in the
expansion of m1 (and vice versa). So, for example, this works:

(macrolet ((oddm (n) (if (= n 0) nil (if (= n 1) t `(evenm ,(1- n)))))
(evenm (n) (if (= n 0) t (if (= n 1) nil `(oddm ,(1- n))))))
...)

but this doesn't:

(macrolet ((oddm (n) (if (= n 0) nil (if (= n 1) t (evenm (1- n)))))
(evenm (n) (if (= n 0) t (if (= n 1) nil (oddm (1- n))))))
...)

rg

Kent M Pitman

unread,
Apr 13, 2008, 6:49:13 PM4/13/08
to
Ron Garret <rNOS...@flownet.com> writes:

> [...]


> > One last thing. I've just been going over Paul Graham's book, and I
> > simply can't manage to find a confirmation of his statement. Namely,
> > ".. Like FLET, the local macros may not call one another."
> >
> > Are we sure this is correct in the first place???

Note that call one another does not mean reference one another in an
expansion. The expansion is processed after you're into the body,
and at that point all of the names are established as bindings.

Only the body of the _expander_ can be relevant, since only in the
_expander_ is there any issue of what is yet scoped when you're halfway
through the bindings.

I believe Ron has it right here:

> It is correct, but potentially misleading. If you have:
>
> (macrolet ((m1 ...) (m2 ...)) ...)
>
> the m1 may not use m2 in its macroexpander, but m2 may appear in the
> expansion of m1 (and vice versa). So, for example, this works:
>
> (macrolet ((oddm (n) (if (= n 0) nil (if (= n 1) t `(evenm ,(1- n)))))
> (evenm (n) (if (= n 0) t (if (= n 1) nil `(oddm ,(1- n))))))
> ...)
>
> but this doesn't:
>
> (macrolet ((oddm (n) (if (= n 0) nil (if (= n 1) t (evenm (1- n)))))
> (evenm (n) (if (= n 0) t (if (= n 1) nil (oddm (1- n))))))
> ...)

A related example:

(macrolet ((foo (x) ``(expanded-foo ,,x)))
(macrolet ((bar (x) (print (foo x)) x))
(bar (foo (+ 1 1)))))

(EXPANDED-FOO (FOO (+ 1 1)))
=> (EXPANDED-FOO 2)

(macrolet ((foo (x) ``(expanded-foo ,,x))
(bar (x) (print (foo x)) x))
(bar (foo (+ 1 1))))
Error: Undefined function FOO called with arguments ((FOO (+ 1 1))).

Also, btw, an even more obscure example where you might think this all
mattered would be when calling MACROEXPAND in the expander, since it
sort of appears to happen "in" the expander, might be the following.
But note again that really it doesn't happen "in" the expander, it
happens in function calls, using the env which is generated relative
to the body form, not the form in the expander. So by that time the
bindings are established and you don't get snagged by the
not-yet-defined-ness that happens when processing the binding
definitions themselves.

(macrolet ((foo (x) ``(expanded-foo ,,x))
(bar (x &environment env)
;; I wouldn't ordinarily do this this way,
;; and don't recommend this programming style
;; but I wanted to make a point about what
;; things one can rely on here and what one
;; can't... and, in fact, EVAL is pretty much
;; what top-level macros are doing after the
;; lexical phase (macro expansion) is dismissed.
;; (In the truly general case, you'd have to worry
;; about references to macros in the macro body,
;; but my example handwaves away that scenario, so
;; squeak by without a recursive MACROEXPAND-ALL.)
(progv (list 'x) (list x)
(print (eval `(locally
(declare (special x))
,(macroexpand '(foo x) env)))))
(macroexpand x env)))
(bar (foo (+ 1 1))))

(EXPANDED-FOO (FOO (+ 1 1)))
=> (EXPANDED-FOO 2)

Then again, all in all, I don't generally recommend writing macros
that need to do this. So I'm not sure any of this matters much.

- - - -

Footnote (really apropos another thread where this came up recently,
but it rears its ugly head again here as a tangential element):

I really don't like it when implementations make me put in (locally
(declare (special x)) ...) but I put that there for people who like
strict conformance. It's true the spec requires it, but the
rationale for not making free variables defaultly special wasn't to
allow implementations to randomly hit people's knuckles with a ruler,
it was to allow them to add global lexicals compatibly, by reserving
them space to do that. For implementations who've gone to the
trouble to do that, I applaud them and will be happy to adjust my
programs if/when I'm actually using those implementations. But since
I've seen none of them do it and certainly no one promote it as
style everyone should adopt, I don't see the point of the ones who
think there are no variables other than specials telling me I've done
something ambiguous by not specifying. The market was intended to sort
out that gray area, and my personal marketplace consumer behavior is
to prefer implementations that don't make me do gratuitous work for
nothing. I like being able to just SETQ toplevel variables and know I
am setting the symbol value and I like being able to eval forms with
free variable references and know I'm getting symbol values.

(Frankly, even if I were adding lexicals, I'd probably just make
deflexical do an override of the individual variable's default behavior
and I'd still leave undeclared variables defaulting to special,
in that way encouraging people to declare their free variables, and
at the same time making legacy behavior fully compatible without all those
silly declarations.)

rocco...@gmail.com

unread,
Apr 14, 2008, 5:07:37 AM4/14/08
to
On Apr 14, 12:49 am, Kent M Pitman <pit...@nhplace.com> wrote:
> Ron Garret <rNOSPA...@flownet.com> writes:
> > In article
> > <417286d4-f254-4068-8ec3-13bdd76ef...@8g2000hse.googlegroups.com>,

Thanks for the very clear response Mr. Pitman (and to Ron Garret as
well). It was much more than I was even asking for :) Just one last
thing. I couldn't actually pinpoint the place in the HyperSpec where
it says that MACROLET has the above mentioned characteristic (a la
FLET so to speak). Other than Graham's book, there's no other place
where I've seen it clearly stated. Could you indicate where that
specific detail is located?

Thanks again.

Kent M Pitman

unread,
Apr 14, 2008, 9:04:22 AM4/14/08
to
rocco...@gmail.com writes:

> Thanks for the very clear response Mr. Pitman (and to Ron Garret as
> well). It was much more than I was even asking for :) Just one last
> thing. I couldn't actually pinpoint the place in the HyperSpec where
> it says that MACROLET has the above mentioned characteristic (a la
> FLET so to speak). Other than Graham's book, there's no other place
> where I've seen it clearly stated. Could you indicate where that
> specific detail is located?

I think it's probably the implication of this statement from the
penultimate paragraph of the macrolet dictionary entry, though I will
be the first to criticize the wording as lacking in perspicuity:

The macro-expansion functions defined by macrolet are defined
in the lexical environment in which the macrolet form appears.

rocco...@gmail.com

unread,
Apr 14, 2008, 9:34:12 AM4/14/08
to
On Apr 14, 3:04 pm, Kent M Pitman <pit...@nhplace.com> wrote:

OK. Thanks again.

Ron Garret

unread,
Apr 14, 2008, 11:38:18 AM4/14/08
to
In article <ud4ot9...@nhplace.com>,

Kent M Pitman <pit...@nhplace.com> wrote:

That would exclude the possibility of doing deferred bindings on global
lexicals, which IMHO is a handy dandy feature. IMHO a better solution
would be to have some kind of user-manipulable
free-variable-reference-hook or some such thing (along with an
undefined-function-reference-hook). But since the spec is about as
likely to change as George Bush is to withdraw from Iraq this is all
moot anyway.

rg

Ron Garret

unread,
Apr 14, 2008, 11:43:18 AM4/14/08
to
In article
<8372e1fe-c040-46b8...@a22g2000hsc.googlegroups.com>,
rocco...@gmail.com wrote:

> I couldn't actually pinpoint the place in the HyperSpec where
> it says that MACROLET has the above mentioned characteristic (a la
> FLET so to speak). Other than Graham's book, there's no other place
> where I've seen it clearly stated. Could you indicate where that
> specific detail is located?

There isn't "a place" where this behavior is described. It's an
"emergent property" of the definition of macrolet (in section 5.3) and
the rules for how macroexpansion is performed (in section 3.1.2.1.2.2).

rg

Thomas A. Russ

unread,
Apr 15, 2008, 6:10:39 PM4/15/08
to
rocco...@gmail.com writes:
> One last thing. I've just been going over Paul Graham's book, and I
> simply can't manage to find a confirmation of his statement. Namely,
> ".. Like FLET, the local macros may not call one another."
>
> Are we sure this is correct in the first place???

Well, it could be confusing. It depends on what one meas by "call one
another".

That is because, the way I see it, there are really two parts to a
macro. There is the expanded code that the macro produces (the
expansion) and there is the computation that arrives at that
computation.

Each macro form in the body of the MACROLET is EXPANDED, and then
subsequently processed recursively. Since this execution is in the body
of the MACROLET, it means that any references to macros in that body use
the local binding of the macro expansion. Note that this really isn't
at all "hygenic". There is an evil example in the HyperSpec that shows
the capture of the inner macro expansion using a globally-defined macro
that expands into code that refers to the name bound by the MACROLET.

Since macros expand to code, they don't really have any internal scope
in the expansion. That leaves open the question of whether you could
use a MACROLET macro in the code that computes the expansion. Since the
specification says that the change in the expansion function is only
active in the body of the macrolet (rather than also in the definition
forms), that means you can't use a MACROLET macro to compute the
expansion of any sibling macros.

--
Thomas A. Russ, USC/Information Sciences Institute

0 new messages