Factor out parts of syntax templates

I tried all four solutions and I learned a lot of new things about Racket macros :slight_smile: I decided to go with quasisyntax because it seems more basic and easier to comprehend, and because my actual use case is simple enough.

I put here the solutions for the record.

Helper macros

(require (for-syntax syntax/parse))

(begin-for-syntax
  (require racket (for-syntax syntax/parse))
  (define-syntax (helper stx)
    (syntax-parse stx
      [(_ stuff ...)
       #'#'(list stuff ...)])))

(define-syntax (main-macro stx)
  (syntax-parse stx
    [(_ stuff:expr ...)
     (helper stuff ...)]))

(main-macro 1 2 3)

This worked nicely and is rather intuitive. Note that I used a double #' in helper, which yields a macro expanding to a syntax template.

Template metafunctions

(require (for-syntax syntax/parse))

(begin-for-syntax
  (require syntax/parse/experimental/template)

  (define-template-metafunction (helper stx)
    (syntax-parse stx
      [(_ stuff ...)
       #'(list stuff ...)])))

(define-syntax (main-macro stx)
  (syntax-parse stx
    [(_ stuff:expr ...)
     #'(helper stuff ...)]))

(main-macro 1 2 3)

define-template-metafunction is really powerful! However, I couldn't easily make it work with Typed Racket, because some type annotations were missing, and I didn't investigate further.

Syntax classes

(require (for-syntax syntax/parse))

(begin-for-syntax
  (define-syntax-class helper
    (pattern (stuff:expr ...))))

(define-syntax (main-macro stx)
  (syntax-parse stx
    [(_ a:helper)
     #'(list a.stuff ...)]))

(main-macro (1 2 3))

In this case, the syntax class allows capturing different parts which can then be assembled into the final template. Syntax classes are very useful, but I don't think I can make meaningful use of them in my concrete situation.

Quasisyntax and quasisyntax-splicing

(require (for-syntax syntax/parse))

(define-for-syntax (helper stx-fragment)
  #`(list #,@stx-fragment))

(define-syntax (main-macro stx)
  (syntax-parse stx
    [(_ stuff:expr ...)
     (helper #'(stuff ...))]))

(main-macro 1 2 3)

This is just a copy of @SamPhillips's answer which I put here for the record. I find that this version is very useful to somebody who is still learning about macros, because syntax transformations are explicitly visible.

5 Likes