Search code examples
macrosrackethygiene

syntax-parameter pointing to a syntax-pattern-variable?


The syntax-parameterize is commonly used in conjunction with make-rename-transformer so that the syntax-parameter p behaves as an alias for another identifier:

#lang racket

(require racket/stxparam)
(define-syntax-parameter p #f)

(define-syntax (test-1-p stx)
  (syntax-case stx ()
    [(_ body)
     #'(let ([tmp 123])
         (syntax-parameterize ([p (make-rename-transformer #'tmp)])
           body))]))
(test-1-p p) ;; prints 123

The code above works well, as tmp is an identifier bound by let. If however I try to make p an alias for the pattern variable tmp bound by with-syntax, then it does not work as expected:

#lang racket

(require racket/stxparam)
(define-syntax-parameter p #f)

(define-syntax (test-2-p stx)
  (syntax-case stx ()
    [(_ body)
     #'(with-syntax ([tmp #'(foo 123)])
         (syntax-parameterize ([p (make-rename-transformer #'tmp)])
           body))]))
(test-2-p #'p) ;; gives #'p, instead of #'(foo 123)

If instead I declare a p-unhygienic syntax, and bind it to (make-rename-transformer #'tmp), then it works fine:

#lang racket

(define-syntax (test-3-p stx)
  (syntax-case stx ()
    [(_ body)
     #`(with-syntax ([tmp #'(foo 123)])
         (define-syntax #,(syntax-local-introduce #'p-unhygienic)
           (make-rename-transformer #'tmp))
         body)]))
(test-3-p #'p-unhygienic) ;; gives #'(foo 123), as expected

How can I create a hygienic alias for a pattern variable using syntax-parameterize ?


Solution

  • This is an interesting case, I expected the same thing you did. This might be either a bug somewhere or a limitation; I'm not sure. Either way, the define-rename-transformer-parameter form is provided as a workaround.

    The initial value of a rename-transformer-parameter must be a rename-transformer, so you can use a p-init identifier with #f as the transformer binding.

    (define-syntax p-init #f)
    (define-rename-transformer-parameter p (make-rename-transformer #'p-init))
    

    In context:

    #lang racket
    
    (require racket/stxparam)
    (define-syntax p-init #f)
    (define-rename-transformer-parameter p (make-rename-transformer #'p-init))
    
    (define-syntax (test-2-p stx)
      (syntax-case stx ()
        [(_ body)
         #'(with-syntax ([tmp #'(foo 123)])
             (syntax-parameterize ([p (make-rename-transformer #'tmp)])
               body))]))
    (test-2-p #'p) ; #<syntax:10:28 (foo 123)>