[plt-scheme] Non-hygenic Macro Revelation
It's a matter of the documented behavior of the macro. Sometimes you want it to work one way, sometimes another. Here's my standard example. Imagine that you implement `while' to implicitly bind `break'. Let's pretend that `break' is implicitly bound here:
(define-syntax-rule (while test e ...)
(let/ec break
(let loop ()
(when test
e ...
(loop)))))
Now imagine you want to implement `for' on top of this:
(define-syntax-rule (for lo-e hi-e e ...)
(let ([i lo-e]
[hi hi-e])
(while (< i hi)
e ...
(set! i (add1 i)))))
In this case, you want part of the public interface of `for' to be that it binds `break'. But now imagine you want to use `while' internally as part of an implementation:
(define-syntax-rule (do-100x e)
(let ([i 0])
(while (< i 100)
e
(set! i (add1 i)))))
Then you might *not* want the binding of `break' to be exposed. So there's no "right" behavior; it depends on what you want your macro to do. And you definitely have to inform users of the behavior.
Dave
----- Original Message -----
From: "Jay McCarthy" <jay.mccarthy at gmail.com>
To: "PLT-Scheme List" <plt-scheme at list.cs.brown.edu>
Sent: Thursday, March 25, 2010 11:41:58 AM GMT -08:00 US/Canada Pacific
Subject: [plt-scheme] Non-hygenic Macro Revelation
I've always used the pattern
(datum->syntax stx (string->symbol (format "~a?" (syntax->datum #'id))))
when defining, for example, predicates of inputs to macros. (stx is
the syntax object given to the transformer.)
This gives the new identifier the same lexical scope as the call to
the macro. Yesterday I noticed that this causes a problem with trivial
macro combinations.
(define-syntax (define-pred stx)
(syntax-case stx ()
[(_ id)
(with-syntax ([id? (datum->syntax stx (string->symbol (format
"~a?" (syntax->datum #'id))))])
(syntax/loc stx
(define (id? x) #t)))]))
(define-syntax-rule (define-pred-wrap i)
(define-pred i))
(define-pred-wrap x)
In this example x? is unbound, because it gets the lexical context of
the define-pred-wrap macro, not the user's program. This can be easily
fixed by using
(datum->syntax #'id (string->symbol (format "~a?" (syntax->datum #'id))))
instead. In this case the predicate gets the same context as the
identifier used to construct it.
I'm not sure when it is appropriate to do one versus the other. One
idea is to use stx when the new identifier is based on multiple
inputs, but otherwise use the context of the base identifier.
(However, that rule would have still caused the problem that
instigated this.) If there was a way to compare contexts, I think I
would want (if (context=? #'id1 #'id2 ...) #'id1 stx), so I got the
shared identifier context or the macro invocation context. However, I
don't think such an operation exists. You could probably make one by
creating a new identifier per input identifier, then use
bound-identifier=? to see if they are bound in the same context.
Is there a convention I don't know about?
Jay
[cross-posted on my blog:
http://jay-mccarthy.blogspot.com/2010/03/non-hygenic-macro-revelation.html]
--
Jay McCarthy <jay at cs.byu.edu>
Assistant Professor / Brigham Young University
http://teammccarthy.org/jay
"The glory of God is Intelligence" - D&C 93
_________________________________________________
For list-related administrative tasks:
http://list.cs.brown.edu/mailman/listinfo/plt-scheme