Macros
Syntax transformers with define-syntax, syntax-rules, and syntax-case.
Overview
Macros transform code at compile time. They receive syntax (code as data) and return new syntax to be compiled.
define-syntax
Define a macro transformer.
(define-syntax macro-name transformer)The transformer is typically created with syntax-rules or syntax-case.
syntax-rules
Pattern-based macros. Each clause matches a pattern and produces output. Macros defined with syntax-rules are hygienic—introduced identifiers don't accidentally capture user bindings.
(define-syntax when
(syntax-rules ()
((when test body ...)
(if test (begin body ...) #f))))
(when (> x 0)
(display "positive")
(newline))
; Expands to:
; (if (> x 0) (begin (display "positive") (newline)) #f)Pattern Syntax
_- Matches anything, not boundname- Matches anything, bound toname(pattern ...)- Matches zero or more(pattern . rest)- Matches list with restliteral- Matches exact symbol (if in literals list)
Literals
Symbols in the literals list match exactly, not as pattern variables.
(define-syntax my-cond
(syntax-rules (else =>)
((my-cond (else result ...))
(begin result ...))
((my-cond (test => proc))
(let ((temp test))
(if temp (proc temp) #f)))
((my-cond (test result ...))
(if test (begin result ...) #f))
((my-cond (test result ...) clause ...)
(if test
(begin result ...)
(my-cond clause ...)))))Multiple Clauses
(define-syntax my-or
(syntax-rules ()
((my-or) #f)
((my-or e) e)
((my-or e1 e2 ...)
(let ((temp e1))
(if temp temp (my-or e2 ...))))))syntax-case
Procedural macros with full Scheme power. More flexible than syntax-rules. Macros defined with syntax-case are also hygienic.
(define-syntax my-let
(lambda (stx)
(syntax-case stx ()
((_ ((var val) ...) body ...)
#'((lambda (var ...) body ...) val ...)))))Syntax Objects
#'template- Create syntax from template (like quasiquote for syntax)#template- Quasisyntax, allows#,` for unquotesyntax->datum- Convert syntax to plain datadatum->syntax- Convert data to syntax (with lexical context)
Guards
Add conditions to pattern clauses.
(define-syntax assert-symbol
(lambda (stx)
(syntax-case stx ()
((_ x)
(identifier? #'x)
#'(quote x))
((_ x)
(syntax-error "expected identifier" #'x)))))Building Syntax Programmatically
(define-syntax define-predicates
(lambda (stx)
(syntax-case stx ()
((_ name ...)
(with-syntax (((pred ...)
(map (lambda (n)
(datum->syntax n
(string->symbol
(string-append
(symbol->string (syntax->datum n))
"?"))))
#'(name ...))))
#'(begin
(define (pred x) (eq? x 'name))
...))))))
(define-predicates red green blue)
; Defines red?, green?, blue?Common Patterns
Binding Forms
(define-syntax let1
(syntax-rules ()
((let1 var val body ...)
(let ((var val)) body ...))))Anaphoric Macros
To intentionally inject identifiers into user scope, use datum->syntax with the user's syntax object:
(define-syntax aif
(lambda (stx)
(syntax-case stx ()
((_ test then else)
(with-syntax ((it (datum->syntax stx 'it)))
#'(let ((it test))
(if it then else)))))))
(aif (find-user id)
(display (user-name it))
(display "not found"))Loop Constructs
(define-syntax while
(syntax-rules ()
((while test body ...)
(let loop ()
(when test
body ...
(loop))))))Delayed Evaluation
(define-syntax delay
(syntax-rules ()
((delay expr)
(lambda () expr))))
(define-syntax force
(syntax-rules ()
((force promise)
(promise))))Hygiene
Macros defined with syntax-rules and syntax-case are hygienic by default. Introduced bindings don't capture user names.
(define-syntax swap!
(syntax-rules ()
((swap! a b)
(let ((temp a))
(set! a b)
(set! b temp)))))
(let ((temp 1)
(x 2))
(swap! temp x)
(list temp x))
; => (2 1)
; The macro's temp doesn't interfere with user's tempPlain transformer procedures (raw lambdas without syntax-case) are not automatically hygienic.