Template Macros
1 Overview
1.1 The ‘$’ Convention
2 Primitives
with-template
semiwith-template
quote-template
semiquote-template
3 Constructors
template
semitemplate
quoted-template
semiquoted-template
templates
semitemplates
quoted-templates
semiquoted-templates
untemplate
untemplate-splicing
4 Combiners
begin-template
begin0-template
if-template
cond-template
when-template
unless-template
for/  template
for*/  template
5 Binding Forms
define-template
define-templates
let-template
letrec-template
splicing-let-template
splicing-letrec-template
6 Module Templates
load-template
template-module-begin
7 Developer Tools
debug-template
debug-template/  scopes
reset-debug-scopes
7.6

Template Macros

Eric Griffis <dedbox@gmail.com>

 (require template) package: template

1 Overview

Template macros are similar to pattern-based macros, but with four important differences:

The template API splits ordinary macro expansion into two distinct stages: template expansion time and macro expansion time. Always, template variables are resolved and sub-template forms are expanded before ordinary macros are expanded.

1.1 The ‘$’ Convention

Throughout this manual, the names of template variables start with a ‘$’. Although the template API imposes no such restriction on variable names, beware:

Template macro variable resolution is finer-grained than ordinary variable resolution. Poorly chosen variable names can lead to bizarre syntax errors.

Examples:
> (with-template ([e X]) (define e 123))

dXfinX: undefined;

 cannot reference an identifier before its definition

  in module: 'program

> (with-template ([e X]) 'e)

quotX: undefined;

 cannot reference an identifier before its definition

  in module: 'program

2 Primitives

syntax

(with-template ([var-id val-expr] ...) tpl ...)

Expands the sub-templates of the tpls and substitutes var-ids with val-exprs while retaining the lexical information, source-location information, and syntax properties of the originating template source.

Example:
> (with-template ([$x a]
                  [$y b])
    (define $xs '($x $x $x))
    (displayln `($xs = ,@$xs)))

(as = a a a)

When a with-template form appears at the top level, at module level, or in an internal-definition position (before any expression in the internal-definition sequence), it is equivalent to splicing the expanded tpls into the enclosing context.

Examples:
> (with-template ([$x a]
                  [$y b])
    (define ($x-$y? obj)
      (equal? obj '($x $y))))
> (a-b? '(a b))

#t

> (a-b? '(c d))

#f

> (list
   (with-template ([$x a]) '$x)
   (with-template ([$x b]) '$x))

'(a b)

syntax

(semiwith-template ([var-id val-expr] ...) tpl ...)

Like with-template, except literals generated from template variables inherit the lexical information, source-location information, and syntax properties of the val-expr bound to the first var-id used.

Example:
> (semiwith-template ([$where here]) #'$where)

#<syntax:eval:162:28 here>

> (with-template ([$where not-here]) #'$where)

#<syntax:eval:166:37 not-here>

syntax

(quote-template ([var-id val-expr] ...) tpl ...)

Like semiwith-template, except sub-templates are not expanded.

Example:
> (quote-template ([$where not-here]) #'(untemplate '$where))

#<syntax:eval:176:38 (untemplate (quote not-here))>

> (semiwith-template ([$where there]) #'(untemplate '$where))

#<syntax:eval:180:50 there>

syntax

(semiquote-template ([var-id val-expr] ...) tpl ...)

Like quote-template, except literals generated from template variables inherit the lexical information, source-location information, and syntax properties of the val-expr bound to the first var-id used.

Example:
> (semiquote-template ([$where not-here]) #'(untemplate '$where))

#<syntax:eval:193:42 (untemplate (quote not-here))>

> (quote-template ([$where not-here]) #'(untemplate '$where))

#<syntax:eval:197:38 (untemplate (quote not-here))>

3 Constructors

syntax

(template (var-id ...) tpl ...)

syntax

(semitemplate (var-id ...) tpl ...)

syntax

(quoted-template (var-id ...) tpl ...)

syntax

(semiquoted-template (var-id ...) tpl ...)

Produces a template macro procedure; a syntax transformer for a macro that accepts an argument for each var-id and substitutes them into the tpls.

Example:
> (define-syntax iterate-with
    (template ($for)
      ($for/list ([x (in-range 3)]
                  [y (in-range 3)])
        (+ y (* x 3)))))
> (iterate-with for)

'(0 4 8)

> (iterate-with for*)

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

syntax

(templates [(var-id ...) tpl ...] ...)

syntax

(semitemplates [(var-id ...) tpl ...] ...)

syntax

(quoted-templates [(var-id ...) tpl ...] ...)

syntax

(semiquoted-templates [(var-id ...) tpl ...] ...)

Produces a template macro procedure. Each [(var-id ...) tpl ...] clause is analogous to a single template, semitemplate, quoted-template, or semiquoted-template procedure; applying the templates-generated procedure is the same as applying a procedure that corresponds to one of the clauses—the first procedure that accepts the given number of arguments. If no corresponding procedure accepts the given number of arguments, a syntax error is raised.

Example:
> (define-syntax f
    (templates [() 0]
               [($x) '$x]
               [($x $y) '$x-$y]))
> (list (f) (f one) (f one two))

'(0 one one-two)

> (f one two three)

eval:251.12: f: bad syntax

  in: (f one two three)

syntax

(untemplate expr)

syntax

(untemplate-splicing expr)

Escapes from an expanding template and replaces itself with the result of expr, an expression at phase level 1 relative to the surrounding context.

Examples:
> (let-syntax ([x #'(+ 2 3)])
    (begin-template
      (+ 1 (untemplate (syntax-local-value #'x)))))

6

> (begin-template
    '(1 (untemplate-splicing '(2 3))))

'(1 2 3)

If expr does not evaluate to a syntax object, the result is wrapped in the lexical information, source-location information, and syntax properties of expr.

Examples:
> (begin-template #'(untemplate 'here))

#<syntax:eval:279:30 here>

> (begin-template #'(untemplate (datum->syntax #f 'nowhere)))

#<syntax nowhere>

4 Combiners

syntax

(begin-template tpl ...)

Equivalent to (with-template () tpl ...).

Example:
> (begin-template 1 2 3)

3

syntax

(begin0-template ([var-id val-expr] ...) tpl ...)

Like begin-template, except the result of the first tpl is the result of the begin0-template form.

Example:
> (begin0-template 1 2 3)

1

syntax

(if-template test-expr then-tpl else-tpl)

Evaluates test-expr, which is an expression at phase level 1 relative to the surrounding context. If it produces any value other than #f, then then-tpl takes its place. Otherwise, else-tpl takes its place.

Examples:
> (begin-template
    `(if-template (positive? -5) ,(error "doesn't get here") >>))

'>>

> (begin-template
    `(if-template (positive? 5) << ,(error "doesn't get here")))

'<<

> (let-syntax ([x 'we-have-no-bananas])
    (with-template ([$+ yes]
                    [$- no])
      (if-template (syntax-local-value #'x) "$+" "$-")))

"yes"

> (let-syntax ([x #f])
    (with-template ([$+ yes]
                    [$- no])
      (if-template (syntax-local-value #'x) "$+" "$-")))

"no"

syntax

(cond-template [test-expr tpl ...] ... maybe-else-clause)

 
maybe-else-clause = 
  | [else tpl ...]
A clause that starts with else must be the last clause.

If no clauses are present, (void) takes the place of the cond-template form.

If the first clause does not start with else and its test-expr, which is an expression at phase level 1 relative to the surrounding context, produces #f, then the result is the same as a cond-template form with the remaining clauses. Otherwise, the tpls take the place of the cond-template form.

Examples:
> (cond-template)
> (cond-template [else 5])

5

> (let-syntax ([x #f]
               [y #t])
    (begin-template
      `(cond-template
         [(positive? -5) ,(error "doesn't get here")]
         [(syntax-local-value #'x) ,(error "doesn't get here, either")]
         [(syntax-local-value #'y) here])))

'here

syntax

(when-template test-expr tpl ...)

Evaluates test-expr, which is an expression at phase level 1 relative to the surrounding context. If the result is not #f, then the tpls takes the place of the when-template form. Otherwise, (void) takes its place.

Examples:
> (displayln (when-template #f 'hi))

#<void>

> (begin-template
    (list (when-template #t 'hey 'there)))

'(hey there)

syntax

(unless-template test-expr tpl ...)

Equivalent to (when-template (not test-expr) tpl ...).

Examples:
> (displayln (unless-template #f 'hi))

hi

> (begin-template
    (list (unless-template #t 'hey 'there)))

'()

syntax

(for/template ([var-id seq-expr] ...) tpl ...)

Iterates like for/list, but results are accumulated into a begin-template instead of a list.

Example:
> (for/template ([$x (in-syntax #'(A B C))]
                 [$n (in-naturals)])
    (define $x (add1 $n)))
> (list A B C)

'(1 2 3)

When for/template is used in an expression context inside a template, the results are spliced into the enclosing context.

Example:
> (begin-template
    (list (for/template ([$n (in-range 10)]) $n)))

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

When used outside any other template, the results are wrapped with begin.

Example:
> (list (for/template ([$n 10]) (add1 $n)))

'(10)

syntax

(for*/template ([var-id seq-expr] ...) tpl ...)

Like for/template, but with all of its sequence iterations nested.

Examples:
> (begin-template
    (list (for*/template ([$m (in-range 3)]
                          [$n (in-range 3)])
            (+ $n (* $m 3)))))

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

5 Binding Forms

syntax

(define-template (id var-id ...) tpl ...)

Creates a transformer binding of id to (template (var-id ...) tpl ...).

Example:
> (define-template (iterate-with $for)
    ($for/list ([x (in-range 3)]
                [y (in-range 3)])
      (+ y (* 3 x))))
> (iterate-with for)

'(0 4 8)

> (iterate-with for*)

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

syntax

(define-templates [(id var-id ...) tpl ...] ...)

Like define-template, but creates a transformer binding for each id.

Example:
> (define-templates
    [(show $obj) (displayln "$obj")]
    [(many $objs) (for/template ([$obj '$objs]) (show $obj))])
> (many (one two three))

one

two

three

syntax

(let-template ([(id var-id ...) tpl ...] ...) body-tpl ...)

Creates a transformer binding of each id with (template (var-id ...) tpl ...). Each id is bound in the body-tpls, and not in other tpls.

Example:
> (let-template ([(fwd $x $y) $x$y]
                 [(rev $x $y) $y$x])
    '((fwd a b) (rev a b)))

'(ab ba)

syntax

(letrec-template ([(id var-id ...) tpl ...] ...) body-tpl ...)

Like let-template, except that each var-id is also bound within all tpls.

Example:
> (letrec-template
      ([(E? $n) (if-template (zero? $n) #t (O? #,(sub1 $n)))]
       [(O? $n) #,(not (E? $n))])
    '((E? 10) (E? 11)))

'(#t #f)

syntax

(splicing-let-template ([(id var-id ...) tpl ...] ...) body-tpl ...)

syntax

(splicing-letrec-template ([(id var-id ...) tpl ...] ...) body-tpl ...)

Like let-template and letrec-template, except that in a definition context, the body-tpls are spliced into the enclosing definition context (in the same way as for with-template).

Examples:
> (splicing-let-template ([(one) 1])
    (define o (one)))
> o

1

> (one)

one: undefined;

 cannot reference an identifier before its definition

  in module: 'program

> (splicing-letrec-template
      ([(E? $n) (if-template (zero? $n) #t (O? #,(sub1 $n)))]
       [(O? $n) #,(not (E? $n))])
    (define is-11-even? (E? 11))
    (define is-10-even? (E? 10)))
> (list is-11-even? is-10-even?)

'(#f #t)

6 Module Templates

In template/tests/lang-template.rkt:

#lang template ($x $n)
 
(require (for-syntax racket/base))
 
(define $xs '((for/template ([$_ (in-range 1 (add1 $n))]) $x)))
 
(for/template ([$k (in-range 1 (add1 $n))])
  (define $x$k $k))
 

syntax

(load-template mod-path id)

Binds id to the template macro provided by mod-path.

Example:
> (load-template template/scribblings/lang-template tpl)
> (tpl a 4)
> as

'(a a a a)

> a4

4

syntax

(template-module-begin (var-id ...) tpl ...)

Exports a binding of the-template to (template (var-id ...) tpl ...).

Example:
> (module my-template-mod template/lang
    ($x)
    (define $xs '($x $x $x $x)))
> (require 'my-template-mod)
> (the-template b)
> bs

'(b b b b)

7 Developer Tools

syntax

(debug-template tpl)

Displays the expanded form of tpl, then evaluates the result.

Examples:
> (debug-template (for/template ([$n 10]) $n))

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

9

> (debug-template (begin-template (list (for/template ([$n 10]) $n))))

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

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

Displays the expanded form of tpl, annotated with superscripts indicating the scopes present on them, then evaluates the result.

Example:
> (debug-template/scopes
    (for/template ([$x (in-syntax #'(A B C D E))]
                   [$n (in-range 1 6)])
      #'$x$n))

(begin⁰˙˙²

 (syntax⁰˙˙² A1²˙˙³)

 (syntax⁰˙˙² B2²˙˙³)

 (syntax⁰˙˙² C3²˙˙³)

 (syntax⁰˙˙² D4²˙˙³)

 (syntax⁰˙˙² E5²˙˙³))

0 module main      -94640

1 module           -94636

2 module top-level 94173

3 module           0

#<syntax:eval:599:6 E5>

Resets the internal counter used by debug-template/scopes to print a summary table.