[plt-scheme] composing syntax-case macros?
YC wrote:
> On Fri, Jan 16, 2009 at 3:52 PM, Ryan Culpepper <ryanc at ccs.neu.edu> wrote:
>
>> ;; the non-working cond-it
>>> (define-syntax (cond-it stx)
>>> (syntax-case stx (else)
>>> ((cond-it (else exp exp2 ...))
>>> #'(begin exp exp2 ...))
>>> ((cond-it (test? exp exp2 ...))
>>> #'(when-it test? exp exp2 ...))
>>> ((cond-it (test? exp exp2 ...) cond1 cond2 ...)
>>> #'(if-it test? (begin exp exp2 ...)
>>> (cond-it cond1 cond2 ...)))))
>>>
>> When 'cond-it' expands and produces an 'if-it' expression, the 'if-it' is
>> marked by the macro expander as coming from a macro. That means its lexical
>> context is different from the 'it' variables in the branches. That means
>> that the 'it' variable binding produced by 'if-it' does not capture the 'it'
>> references in the branches.
>>
>
> I see - based on this I tried to see if I can mark `if-it` to be the same
> lexical context but i get a compiler warning instead...
>
> (define-syntax (cond-it stx)
> (syntax-case stx (else)
> ((~) #'(void))
> ((~ (else exp exp2 ...))
> #'(begin exp exp2 ...))
> ((~ (test? exp exp2 ...) cond1 ...)
> (with-syntax ((if-it (datum->syntax #'~ 'if-it)))
> #'(if-it test? (begin exp exp2 ...)
> (cond-it cond1 ...))))))
>
>> (cond-it (#f 'hello) ("test me" it) (else 'nothing))
> compile: identifier used out of context in: if-it
>
> I guess the app position cannot not be "captured" this way?
That approach has a flaw that has nothing to do with the error you got.
Suppose that you write a module that imports just 'cond-it', not
'if-it'. Or suppose that your module renames them or prefixes their
names when you import them. Then when your macro creates the 'if-it'
identifier, it will have the context of that module, where 'if-it' isn't
bound. You're trying to use 'if-it' to carry the lexical context for the
'it' binding it introduces, but its lexical context is already doing
work---it tells the macro expander where to look up the 'if-it' macro.
This is why macros like 'if-it' are much more problematic than macros
like 'define-struct', which is also unhygienic. The 'define-struct'
macro also creates bindings of names it generates with 'datum->syntax';
for example,
(define-struct circle (radius color))
defines 'make-circle', 'circle-radius', etc. But the lexical context for
those names comes from the struct name ('circle'), which isn't acting as
a reference. If the lexical context for the introduced names came from
the 'define-struct' keyword instead, it would have all the same problems
as 'if-it'.
> The macro stepper will show you this using colors. Try an example. The
>> original code is in black. The parts introduced by 'cond-it' are in a
>> different color (like red or blue).
>>
>
> I'm working in emacs so I don't have macro stepper handy at the moment - is
> there a way to call macro stepper (a cmdline or repl version) from mzscheme?
Yes, although it's not nearly as nice as the one in DrScheme. Search the
help desk for 'expand/step-text'.
> The problem comes from having macros introduce unhygienic bindings of 'it'.
>> A better way to do it would be to bind 'it' to a syntax parameter and update
>> the meaning of 'it' in the expansion of an 'if-it' expression (using
>> 'syntax-parameterize').
>
>
> This is macro voodoo! ;) I'll have to learn more about syntax parameters
> and transformers. Is there some sort of guide docs that talks about how and
> why (usages) on these capabilites?
I wrote a blog post about syntax parameters a couple years ago:
http://macrologist.blogspot.com/2006/04/macros-parameters-binding-and.html
Other than that, the Guide and the Reference.
I'm also working on a collection of macro techniques and design
patterns. I hope to post a draft of it sometime in the near future.
> Thansks for demonstrating the cool technique and the clear explanation.
Glad to help.
Ryan