Search code examples
macrosracketvariadictopleveldefine-syntax

Racket macro that defines multiple top-level forms?


I found myself defining syntax parameters with identical definitions except for their name so I decided to write a macro to make this simpler:

(define-syntax (test-case-parameter stx)
  (syntax-parse stx
    [(_ parameter:id)
     #'(define-syntax-parameter parameter
         (lambda (stx)
           (raise-syntax-error stx "Can only be used inside test-case.")))]))

(test-case-parameter a)
(test-case-parameter b)
(test-case-parameter c)

However instead of having to repeat the macro name I would like to be able to just write:

(test-case-parameter a b c)

But I don't see how to do this using the normal ellipses syntax, because I would need to wrap everything in a begin which would create a new scope, and I want all of the syntax parameters as if I had written them each of the top level. What's the right way to accomplish this?


Solution

  • The answer is to use begin. begin is weird, because it has different behavior at the top-level than it does in an expression context. At the top-level, it has the kind of splicing behavior you would want for this macro, but in an expression context is has the scoping behavior you're referring to.

    So you can define your macro like this:

    #lang racket
    (require racket/stxparam (for-syntax syntax/parse))
    
    (define-syntax (define-test-case-parameters stx)
      (syntax-parse stx
        [(_ parameter:id ...)
         #'(begin
             (define-syntax-parameter parameter
               (lambda (stx)
                 (raise-syntax-error stx "Can only be used inside test-case.")))
             ...)]))
    
    (define-test-case-parameters a b c)
    

    You can see how the begin top-level splicing works in the Macro Stepper in DrRacket:

    splicing-begin-macro-stepper