[plt-scheme] Non-hygenic Macro Revelation
There are a few ways to do this. I prefer the option of always having
an explicit version of the macro, where every name is given in the
input, and other macros should expand into that. I find it annoying
that, for instance, define-struct has no such option.
Another is to always have an identifier that is explicitly documented
to provide the context for new definitions, and use that one.
Yet another is to specify that the context of the whole term is used,
and thus any macro expanding into your macro must explicitly use
datum->syntax to pass along context for the top-level term.
I consider that list to be in descending order of preference; YMMV.
Carl Eastlund
On Thu, Mar 25, 2010 at 2:41 PM, Jay McCarthy <jay.mccarthy at gmail.com> wrote:
> 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