AtlatestRepositorysigil-web-styles
sigil-web-styles / tree / build / dev / lib / _pkg / sigil-stdlib / languagemacros.md
2
# Macros4
> Syntax transformers with define-syntax, syntax-rules, and syntax-case.6
## Overview8
Macros transform code at compile time. They receive syntax (code as data) and return new syntax to be compiled.10
## define-syntax12
Define a macro transformer.14
```scheme15
(define-syntax macro-name transformer)16
```18
The transformer is typically created with `syntax-rules` or `syntax-case`.20
## syntax-rules22
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.24
```scheme25
(define-syntax when26
(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 Syntax39
- `_` - Matches anything, not bound40
- `name` - Matches anything, bound to `name`41
- `(pattern ...)` - Matches zero or more42
- `(pattern . rest)` - Matches list with rest43
- `literal` - Matches exact symbol (if in literals list)45
### Literals47
Symbols in the literals list match exactly, not as pattern variables.49
```scheme50
(define-syntax my-cond51
(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 test61
(begin result ...)62
(my-cond clause ...)))))63
```65
### Multiple Clauses67
```scheme68
(define-syntax my-or69
(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-case79
Procedural macros with full Scheme power. More flexible than `syntax-rules`. Macros defined with `syntax-case` are also hygienic.81
```scheme82
(define-syntax my-let83
(lambda (stx)84
(syntax-case stx ()85
((_ ((var val) ...) body ...)86
#'((lambda (var ...) body ...) val ...)))))87
```89
### Syntax Objects91
- `#'template` - Create syntax from template (like quasiquote for syntax)92
- `#`template` - Quasisyntax, allows `#,` for unquote93
- `syntax->datum` - Convert syntax to plain data94
- `datum->syntax` - Convert data to syntax (with lexical context)96
### Guards98
Add conditions to pattern clauses.100
```scheme101
(define-syntax assert-symbol102
(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 Programmatically113
```scheme114
(define-syntax define-predicates115
(lambda (stx)116
(syntax-case stx ()117
((_ name ...)118
(with-syntax (((pred ...)119
(map (lambda (n)120
(datum->syntax n121
(string->symbol122
(string-append123
(symbol->string (syntax->datum n))124
"?"))))125
#'(name ...))))126
#'(begin127
(define (pred x) (eq? x 'name))128
...))))))130
(define-predicates red green blue)131
; Defines red?, green?, blue?132
```134
## Common Patterns136
### Binding Forms138
```scheme139
(define-syntax let1140
(syntax-rules ()141
((let1 var val body ...)142
(let ((var val)) body ...))))143
```145
### Anaphoric Macros147
To intentionally inject identifiers into user scope, use `datum->syntax` with the user's syntax object:149
```scheme150
(define-syntax aif151
(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 Constructs165
```scheme166
(define-syntax while167
(syntax-rules ()168
((while test body ...)169
(let loop ()170
(when test171
body ...172
(loop))))))173
```175
### Delayed Evaluation177
```scheme178
(define-syntax delay179
(syntax-rules ()180
((delay expr)181
(lambda () expr))))183
(define-syntax force184
(syntax-rules ()185
((force promise)186
(promise))))187
```189
## Hygiene191
Macros defined with `syntax-rules` and `syntax-case` are hygienic by default. Introduced bindings don't capture user names.193
```scheme194
(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 temp207
```209
Plain transformer procedures (raw lambdas without `syntax-case`) are not automatically hygienic.