AtlatestRepositorysigil-web-styles
1
2# Macros
3
4> Syntax transformers with define-syntax, syntax-rules, and syntax-case.
5
6## Overview
7
8Macros transform code at compile time. They receive syntax (code as data) and return new syntax to be compiled.
9
10## define-syntax
12Define a macro transformer.
14```scheme
15(define-syntax macro-name transformer)
16```
18The transformer is typically created with `syntax-rules` or `syntax-case`.
20## syntax-rules
22Pattern-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.
24```scheme
25(define-syntax when
26 (syntax-rules ()
27 ((when test body ...)
28 (if test (begin body ...) #f))))
30(when (> x 0)
31 (display "positive")
32 (newline))
33; Expands to:
34; (if (> x 0) (begin (display "positive") (newline)) #f)
35```
37### Pattern Syntax
39- `_` - Matches anything, not bound
40- `name` - Matches anything, bound to `name`
41- `(pattern ...)` - Matches zero or more
42- `(pattern . rest)` - Matches list with rest
43- `literal` - Matches exact symbol (if in literals list)
45### Literals
47Symbols in the literals list match exactly, not as pattern variables.
49```scheme
50(define-syntax my-cond
51 (syntax-rules (else =>)
52 ((my-cond (else result ...))
53 (begin result ...))
54 ((my-cond (test => proc))
55 (let ((temp test))
56 (if temp (proc temp) #f)))
57 ((my-cond (test result ...))
58 (if test (begin result ...) #f))
59 ((my-cond (test result ...) clause ...)
60 (if test
61 (begin result ...)
62 (my-cond clause ...)))))
63```
65### Multiple Clauses
67```scheme
68(define-syntax my-or
69 (syntax-rules ()
70 ((my-or) #f)
71 ((my-or e) e)
72 ((my-or e1 e2 ...)
73 (let ((temp e1))
74 (if temp temp (my-or e2 ...))))))
75```
77## syntax-case
79Procedural macros with full Scheme power. More flexible than `syntax-rules`. Macros defined with `syntax-case` are also hygienic.
81```scheme
82(define-syntax my-let
83 (lambda (stx)
84 (syntax-case stx ()
85 ((_ ((var val) ...) body ...)
86 #'((lambda (var ...) body ...) val ...)))))
87```
89### Syntax Objects
91- `#'template` - Create syntax from template (like quasiquote for syntax)
92- `#`template` - Quasisyntax, allows `#,` for unquote
93- `syntax->datum` - Convert syntax to plain data
94- `datum->syntax` - Convert data to syntax (with lexical context)
96### Guards
98Add conditions to pattern clauses.
100```scheme
101(define-syntax assert-symbol
102 (lambda (stx)
103 (syntax-case stx ()
104 ((_ x)
105 (identifier? #'x)
106 #'(quote x))
107 ((_ x)
108 (syntax-error "expected identifier" #'x)))))
109```
111### Building Syntax Programmatically
113```scheme
114(define-syntax define-predicates
115 (lambda (stx)
116 (syntax-case stx ()
117 ((_ name ...)
118 (with-syntax (((pred ...)
119 (map (lambda (n)
120 (datum->syntax n
121 (string->symbol
122 (string-append
123 (symbol->string (syntax->datum n))
124 "?"))))
125 #'(name ...))))
126 #'(begin
127 (define (pred x) (eq? x 'name))
128 ...))))))
130(define-predicates red green blue)
131; Defines red?, green?, blue?
132```
134## Common Patterns
136### Binding Forms
138```scheme
139(define-syntax let1
140 (syntax-rules ()
141 ((let1 var val body ...)
142 (let ((var val)) body ...))))
143```
145### Anaphoric Macros
147To intentionally inject identifiers into user scope, use `datum->syntax` with the user's syntax object:
149```scheme
150(define-syntax aif
151 (lambda (stx)
152 (syntax-case stx ()
153 ((_ test then else)
154 (with-syntax ((it (datum->syntax stx 'it)))
155 #'(let ((it test))
156 (if it then else)))))))
158(aif (find-user id)
159 (display (user-name it))
160 (display "not found"))
161```
163### Loop Constructs
165```scheme
166(define-syntax while
167 (syntax-rules ()
168 ((while test body ...)
169 (let loop ()
170 (when test
171 body ...
172 (loop))))))
173```
175### Delayed Evaluation
177```scheme
178(define-syntax delay
179 (syntax-rules ()
180 ((delay expr)
181 (lambda () expr))))
183(define-syntax force
184 (syntax-rules ()
185 ((force promise)
186 (promise))))
187```
189## Hygiene
191Macros defined with `syntax-rules` and `syntax-case` are hygienic by default. Introduced bindings don't capture user names.
193```scheme
194(define-syntax swap!
195 (syntax-rules ()
196 ((swap! a b)
197 (let ((temp a))
198 (set! a b)
199 (set! b temp)))))
201(let ((temp 1)
202 (x 2))
203 (swap! temp x)
204 (list temp x))
205; => (2 1)
206; The macro's temp doesn't interfere with user's temp
207```
209Plain transformer procedures (raw lambdas without `syntax-case`) are not automatically hygienic.