Template Macros, Initial Release

:: announcement, Racket, meta-programming

Template Macros are a new Racket library that dramatically simplifies the generation of meta-program code.

Template macros infiltrate the spaces underserved by Racket’s existing pattern-based and procedural macros, such as the insides of literal data,

> (with-template ([X 1]
                  [Y 2])
    (values XY3 "YX0" #rx"^X+Y$"))

123

"210"

#rx"^1+2$"

or in the various quoted forms;

> (begin-template
    #'(untemplate (build-list 10 values)))

#<syntax:eval:73:16 (0 1 2 3 4 5 6 7 8 9)>

and when one template macro is used by another, the results are spliced in place automatically.

> (begin-template
    (list (for/template ([K (in-range 10)]) K)))

'(0 1 2 3 4 5 6 7 8 9)

Template macros eliminate common technical barriers to advanced Racket meta-programming techniques by preempting the default macro expander. The current API offers a lot more than I could jam into a release announcement, and it’s still evolving!

In the coming weeks and months, I’ll try to post some topic-focused tutorials that highlight the wealth of functionality template macros provide to the practicing language-oriented Racket programmer.

But the next time you trip on a missing syntax-local-introduce,

> (define-template (urlang-js modpath)
    (let ()
      (local-require (only-in modpath the-javascript))
      (the-javascript)))

or find yourself facing a wall of format-ids,

> (for*/template ([M (in-range 1 6)]
                  [N (in-range 1 6)])
    (define idMxN
      (list (for/template ([I (in-range 1 (add1 M))])
              `(vector ,@(list (for/template ([J (in-range 1 (add1 N))])
                                 (if-template (= I J) 1 0)))))))
    (when-template (= M N)
      (define idM idMxN)))
> id5

'((vector 1 0 0 0 0)

  (vector 0 1 0 0 0)

  (vector 0 0 1 0 0)

  (vector 0 0 0 1 0)

  (vector 0 0 0 0 1))

or struggle to interleave a medley of functions with “syntax” in their names,

> (define-template (hyphen-define* Names Args Body)
    (define
      #,(string->symbol (string-join (map symbol->string 'Names) "-"))
      (λ Args Body)))
> (hyphen-define* (foo bar baz) (v) (* 2 v))
> (foo-bar-baz 50)

100

give template macros a try and let me know how it goes!