[racket] macros: nested templates

From: Jon Rafkind (rafkind at cs.utah.edu)
Date: Wed Jul 28 16:44:02 EDT 2010

  On 07/28/2010 02:39 PM, Eric Tanter wrote:
> Hi,
>
> I have a problem with nested templates in macros.
>
> Consider:
> (define-syntax FOO
>    (lambda (stx)
>      (syntax-case stx ()
>        ((FOO x)
>         #'(let-syntax
>               ((test (λ (stx)
>                        (syntax-case stx ()
>                          ((test a)
>                           #'(list a))))))
>             x)))))
>
>> (FOO (test 2))
> (2)
>
> Now, I want the `test' macro to match on a variable number of arguments so as to be able to write, eg: (FOO (test 1 2 3 4))
>
> (define-syntax FOO
>    (lambda (stx)
>      (syntax-case stx ()
>        ((FOO x)
>         #'(let-syntax
>               ((test (λ (stx)
>                        (syntax-case stx ()
>                          ((test a ...)
>                           #'(list a ...))))))
>             x)))))
>
> This fails with:
> syntax: no pattern variables before ellipses in template in: ...
>
> I understand that the error comes from the fact that the '...' are treated as part of the first macro and therefore do not correspond to any pattern variable, but, they do correspond to a pattern variable in the generated macro (test).
>
> Is there a clean solution to this issue?
>
You have to escape ellipses like so (... ...). You're pattern would be
(syntax-case stx ()
   [(test a (... ...)) #'(list a (... ...))])

Ellipses mean something in a template, and you're pattern is the entire 
`let-syntax' expression. When a new `syntax-case' expression occurs, its 
not that the code inside it is hidden from the outer template. The code 
inside the `syntax-case' expression is treated just the same as any 
other code. It might be helpful to remember that #' is just a shorthand 
for (syntax ...) so that
#'(list a ...) is just (syntax list a ...) and therefore `a' needs to be 
a pattern variable in the template where the ellipses is bound. Using 
just one ellipses means the variable has to be bound in the outer-most 
template.

Anyway, I don't think what you are doing would work anyway becuase you 
are trying to unhygienically introduce the `test' macro to expressions 
inside the body of `FOO'. Your choices are:
a) use `syntax-local-introduce' to unhygienically add `test'
b) make the user of `FOO' pass in the `test' name to the macro: (FOO 
test (test 1 2 3)) and then the macro can bind test in such a way that 
the body can use it.


Posted on the users mailing list.