A Scheme program can define new expression types, called macros. A macro use has the shape of a list that starts with a syntactic keyword, and a transformer rewrites it into other code before it is evaluated. R7RS-small (section 4.3) defines one transformer language, syntax-rules; R6RS adds syntax-case; implementations add others.
Hygiene
Macros defined with the pattern language are hygienic and referentially transparent, so they keep Scheme’s lexical scoping:
- if a transformer inserts a binding for an identifier, the identifier is renamed throughout its scope, so it cannot capture a variable of the code that uses the macro;
- if a transformer inserts a free reference to an identifier, it refers to the binding visible where the macro was defined, whatever bindings surround the use.
The syntactic keyword of a macro can shadow a variable, and a local variable can shadow a keyword. Implementations may offer other kinds of macro.
Defining and binding keywords
| Form | Meaning |
|---|---|
(define-syntax keyword transformer-spec) | Binds the keyword. At the top level it extends the global syntactic environment (earlier expansions are unchanged). In a body it is an internal syntax definition, local to the body. A use before the definition is an error, and R7RS allows syntax definitions wherever variable definitions are |
(let-syntax ((keyword spec) ...) body) | Binds keywords for the body; the specs are evaluated in the outer scope, so a macro cannot call itself |
(letrec-syntax ((keyword spec) ...) body) | Like letrec: the keywords are visible in the specs, so macros can be recursive or mutually recursive |
(syntax-rules (literal ...) rule ...) | A transformer spec made of rewrite rules, below |
(syntax-rules ellipsis (literal ...) rule ...) | R7RS: the same, with a chosen ellipsis identifier in place of ... |
(syntax-error "message" arg ...) | R7RS: signals an error as soon as it is expanded, so a rule for an invalid use can say what is wrong. Programs cannot count on catching it with a handler |
The pattern language
A syntax-rules transformer holds rules, each (pattern template). A macro use is matched against the patterns from the first rule to the last, and the first that matches is rewritten by its template. The pattern is a list; its first element is the keyword, which takes no part in the match and is neither a pattern variable nor a literal.
Patterns
| Pattern | Matches |
|---|---|
| an identifier that is a pattern variable | any single datum, and binds the variable to it. A variable may appear only once in a pattern |
| an identifier listed as a literal | that identifier only, if both have the same binding, or neither has a binding |
_ | any datum, without binding it; it can appear many times. If _ is listed as a literal, it matches itself |
| a constant (a number, string, character or boolean) | an equal constant |
(p1 ... pn) | a list of exactly those elements |
(p1 p2 ... . pn) | an improper list, the last pattern matching what is left over |
(p1 ... pk pe ellipsis pm+1 ... pn) | a list in which pe matches zero or more elements and the patterns after the ellipsis match the elements at the end; a variable under an ellipsis is bound to the sequence of what it matched. R7RS allows patterns after the ellipsis, R5RS did not |
(p1 ... pe ellipsis pm+1 ... . px) | the same with an improper tail |
#(p1 ... pn), #(p1 ... pe ellipsis pm+1 ...) | a vector, with or without an ellipsis |
Templates
| Template | Produces |
|---|---|
| a pattern variable | what it matched |
| another identifier | that identifier, free if it refers to a binding where the macro is defined, renamed if it is bound by the template |
| a constant | itself |
(e1 e2 ... . t) | a list (or improper list); an element may be followed by one or more ellipses to repeat it for each match of the variables inside it. Variables under n ellipses in the pattern must be under at least n in the template |
(ellipsis template), i.e. (... ...) | the template with its ellipses taken as ordinary identifiers; (... ...) produces one ellipsis. This lets a macro expand into a macro |
#(e1 ...) | a vector |
(define-syntax swap!
(syntax-rules ()
((_ a b)
(let ((tmp a))
(set! a b)
(set! b tmp)))))
(define-syntax my-or
(syntax-rules ()
((_) #f)
((_ e) e)
((_ e r ...)
(let ((t e)) (if t t (my-or r ...))))))
(define-syntax be-like-begin
(syntax-rules ()
((be-like-begin name)
(define-syntax name
(syntax-rules ()
((name expr (... ...))
(begin expr (... ...))))))))
(be-like-begin sequence)
(sequence 1 2 3 4) ; 4
or whose t cannot capture the caller’s t, and a macro that defines a macro (R7RS section 4.3.2).Hygiene decides the case below: a local variable named => is not the keyword => that cond looks for, so cond treats the clause as an ordinary test and expression.
(let ((=> #f))
(cond (#t => 'ok))) ; ok
An identifier in a pattern is a pattern variable unless it is _, a literal, or the ellipsis. In R7RS a custom ellipsis lets a macro that generates macros avoid (... ...): (syntax-rules ::: () ...) uses :::, and the identifier ... is then an ordinary one; if the ellipsis is also listed among the literals it matches itself.
In the viewer the pattern variables are coloured as variables, ... and _ as syntax, the literals as syntax, and the template as code, so that a macro that expands into a let shows the let as syntax.
syntax-case (R6RS)
R6RS adds the procedural macro system of (rnrs syntax-case). A transformer is a procedure from a syntax object to a syntax object, and syntax-case takes it apart by pattern while ordinary Scheme code builds the result. The patterns are those of syntax-rules, and a clause may have a fender between the pattern and the template, an expression that must be true for the clause to be chosen.
| Form or procedure | Meaning |
|---|---|
(syntax-case expr (literal ...) clause ...) | Matches the syntax object expr. A clause is (pattern output-expression) or (pattern fender output-expression) |
(syntax template), #'template | Builds a syntax object from a template in which pattern variables are replaced |
(quasisyntax template), #`template, with unsyntax #, and unsyntax-splicing #,@ | The same with holes that are evaluated, like quasiquote |
(with-syntax ((pattern expr) ...) body) | Binds pattern variables to the result of an expression, for use in templates in the body |
(identifier-syntax template) or (identifier-syntax (id template) ((set! id e) template)) | A transformer for an identifier that expands to an expression, optionally with a different expansion when it is assigned |
(make-variable-transformer proc) | Makes a transformer that is also used when its keyword is the target of set! |
syntax->datum, datum->syntax | Convert between syntax objects and data; (datum->syntax template-id datum) gives the datum the lexical context of an identifier, which is how a macro introduces a name that the user’s code can see (deliberately unhygienic) |
identifier?, bound-identifier=?, free-identifier=? | Test syntax objects |
generate-temporaries | Makes a list of fresh identifiers |
(syntax-violation who message form) | Reports a bad macro use; also syntax-violation-form and syntax-violation-subform |
(define-syntax my-if
(lambda (stx)
(syntax-case stx ()
((_ c t e) #'(cond (c t) (else e))))))
(define-syntax loop
(lambda (x)
(syntax-case x ()
((k e ...)
(with-syntax ((break (datum->syntax #'k 'break)))
#'(call-with-current-continuation
(lambda (break)
(let f () e ... (f)))))))))
syntax-case macro, and the usual unhygienic loop that binds break for its user.R6RS programs and libraries run the code of a macro transformer at expansion time, in a separate phase from run time. A library imports the bindings that a transformer needs with a (for (library) expand) import set (see Libraries and modules). R7RS-small has no phases and no procedural macros. The viewer reads the patterns of syntax-case clauses as it reads those of syntax-rules and the templates as code.
Macro systems of the implementations
| System | What it is | Where |
|---|---|---|
syntax-case | The R6RS system above, from Dybvig, Hieb and Bruggeman’s work on syntactic abstraction | Chez Scheme, Guile, Racket, Larceny, Ikarus, Gauche (partly) and others |
er-macro-transformer, ir-macro-transformer | Explicit-renaming and implicit-renaming procedural macros: the transformer receives the form, a renaming procedure and a comparison procedure (explicit), or is hygienic by default and says what to capture (implicit) | CHICKEN, Chibi-Scheme, Gauche, Sagittarius, MIT/GNU Scheme and others |
sc-macro-transformer, rsc-macro-transformer | Syntactic-closure macros: the transformer receives the form and the environment of the use; rsc (reversed) leaves the output in the environment of the use | MIT/GNU Scheme, Chibi-Scheme, Gauche and others |
define-macro, defmacro | Non-hygienic, Lisp-style macros: a procedure from a form to a form. Simple and unhygienic | Guile, Gauche, Gambit and others |
define-syntax-rule | A one-rule shorthand: (define-syntax-rule (name pattern ...) template) | Guile, Racket |
syntax-parse | Racket’s pattern matcher for macros, with syntax classes and error reporting; not Scheme | Racket |
| SRFI 46, 72, 93, 139, 147, 148, 149, 211, 212, 213 | Extensions to syntax-rules and other proposals; see SRFI | various |
Macros and the reader
A macro sees data, not text: it cannot tell #t from #true or [ from (, since the reader has already turned them into the same object. A definition may expand into several definitions through begin. A macro cannot change how a file is read; some implementations (Guile, Racket, Gambit) let a program change the reader, which the viewer does not follow.
Not described
Known, not yet covered
- Reference: the matching and expansion algorithms of the reports (what “matches” means for every pattern, in R7RS section 4.3.2 and the R6RS syntax-case chapter) are summarised in the tables and not described step by step.
- Reference: the macro transformers of the implementations are named in the table; their procedure interfaces are not described. The table was written from memory of their manuals, which were not read, and which implementations support which system should be checked before relying on it.
- Reference: the SRFIs named in the table were not read; their titles are in the SRFI page, from the index.
- Viewer: pattern variables of
er-macro-transformer-style transformers are procedure parameters, and the viewer colours them as such; the input form is not coloured as data.
See also
- Expressions and special forms – the forms macros commonly rebuild
- Libraries and modules – phases and exporting macros
- SRFI – macro-related SRFIs