Another thing about this, if the definition produced by the macro is instead 
generated by the `make-transformer' macro then it will have the wrong number of 
marks on it when everything is finally expanded. `syntax-local-introduce' fixes 
this by essentially removing that extra mark.

#lang racket

(require (for-meta 0 racket/splicing)
         (for-meta 1 syntax/parse)
         (for-meta 2 syntax/parse
                     racket/base))

(begin-for-syntax
  (define-syntax (make-transformer stx)
    (syntax-parse stx
      [(_ name (pattern ...) action)
       #'(quote-syntax (define-syntax name
             (lambda (stx) ;; HERE change to #'(quote-syntax
               (syntax-parse stx
                 [(_ pattern ...) action]))))])))

(define-syntax (macro-generator stx)
  (syntax-parse stx
    [(_ name pattern action)
     #'(splicing-let-syntax
         ([make (lambda (stx)
                  (syntax-parse stx
                    [(_)
                     (syntax-local-introduce
                       (make-transformer name pattern action))]))])
         (make))]))

(macro-generator foo (x ...) #'(list x ...))
(foo 1 2 3)


The `name' passed as an argument to `make-transformer' will not have the mark 
applied to it by the transformer in the splicing-let-syntax, so the resulting 
define-syntax form will use a different 'foo' from the one passed to 
`macro-generator'. Using `syntax-local-introduce' applies the mark that was 
used when `(make)' was expanded thus removing all marks from the final 
expansion.

On 06/01/2012 12:16 PM, Jon Rafkind wrote:
> Today's macro PSA is about macro-generating-macro forms. If you use a macro 
> to generate a transformer then you must be careful about nesting `syntax' 
> forms otherwise you will interpolate templates too many times. Specifically 
> #'#'x will interpolate x twice. Given the following bindings:
>
>   chicken = (syntax (egg ...))
>   pattern = (syntax chicken)
>
> The problem is if an additional `syntax' is wrapped around `pattern'. After 
> one interpolation of `syntax' we will have
>
>   (syntax (egg ...))
>
> At which point `egg' had better be bound as a pattern variable otherwise 
> interpolation will fail.
>
> To work around this problem the `quote-syntax' form should be used:
>
>   #'(quote-syntax pattern)
>
> which prevents the (syntax (egg ...)) layer from being interpolated because 
> `quote-syntax' does not do interpolation.
>
> The following example demonstrates the issue. On the line 'HERE' the inner 
> syntax should be `quote-syntax' otherwise the following error is produced
>
>   x.rkt:27:24: syntax: no pattern variables before ellipses in template at: 
> ... in: (_ x ...)
>
> To be fair this issue only comes up because I used `make-transformer' in the 
> body of a phase 2 function so the output of `make-transformer' had to have 
> two levels of syntax, because its a macro that should ultimately evaluate to 
> a syntax form. A simpler solution would have been to do
>
>   #'(define-syntax name (make-transformer pattern action))
>
> So that `make-transformer' only had to return one level of syntax. But anyway 
> I got into this situation for other reasons.
>
> #lang racket
>
> (require (for-meta 0 racket/splicing)
>          (for-meta 1 syntax/parse)
>          (for-meta 2 syntax/parse
>                      racket/base))
>
> (begin-for-syntax
>   (define-syntax (make-transformer stx)
>     (syntax-parse stx
>       [(_ (pattern ...) action)
>        #'#'(lambda (stx) ;; HERE change to #'(quote-syntax
>              (syntax-parse stx
>                [(_ pattern ...) action]))])))
>
> (define-syntax (macro-generator stx)
>   (syntax-parse stx
>     [(_ name pattern action)
>      #'(splicing-let-syntax
>          ([make (lambda (stx)
>                   (syntax-parse stx
>                     [(_ name)
>                      (with-syntax ([transformer (make-transformer pattern 
> action)])
>                        #'(define-syntax name transformer))]))])
>          (make name))]))
>
> (macro-generator foo (x ...) #'(list x ...))
> (foo 1 2 3)
>
>
> ____________________
>   Racket Users list:
>   http://lists.racket-lang.org/users

____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Reply via email to