source: project/wiki/man/4/Macros @ 36626

Last change on this file since 36626 was 36626, checked in by svnwiki, 17 months ago

Anonymous wiki edit for IP [187.26.82.110]: Fix typo

File size: 11.9 KB
Line 
1[[tags: manual]]
2[[toc:]]
3
4
5== Macros
6
7
8CHICKEN supports standard R5RS {{syntax-rules}} macros and a low-level
9macro system based on ''explicit renaming''.
10
11
12=== Macro definitions
13
14==== define-syntax
15
16<macro>(define-syntax IDENTIFIER TRANSFORMER)</macro>
17
18Defines a macro named {{IDENTIFIER}} that will transform an expression
19with {{IDENTIFIER}} in operator position according to {{TRANSFORMER}}.
20The transformer expression must the result of a call
21to{{er-macro-transformer}} or {{ir-macro-transformer}}, or it must be
22a {{syntax-rules}} form. If {{syntax-rules}} is used, the usual R5RS
23semantics apply. If {{TRANSFORMER}} is a transformer, then its
24transformer procedure will be called on expansion with the complete
25s-expression of the macro invocation, a rename procedure that
26hygienically renames identifiers and a comparison procedure that
27compares (possibly renamed) identifiers (see the section "Explicit
28renaming macros" below for a detailed explanation on non-R5RS macros).
29
30{{define-syntax}} may be used to define local macros that are visible
31throughout the rest of the body in which the definition occurred, i.e.
32
33  (let ()
34    ...
35    (define-syntax foo ...)
36    (define-syntax bar ...)
37    ...)
38
39is expanded into
40
41  (let ()
42    ...
43    (letrec-syntax ((foo ...) (bar ...))
44      ...) )
45
46{{syntax-rules}} supports [[http://srfi.schemers.org/srfi-46/|SRFI-46]]
47in allowing the ellipsis identifier to be user-defined by passing it as the first
48argument to the {{syntax-rules}} form. Also, "tail" patterns of the form
49
50  (syntax-rules ()
51    ((_ (a b ... c)
52      ...
53
54are supported.
55
56The effect of destructively modifying the s-expression passed to a
57transformer procedure is undefined.
58
59
60==== er-macro-transformer
61
62<procedure>(er-macro-transformer PROCEDURE)</procedure>
63
64Returns an explicit-renaming transformer object wrapping the
65syntax-transformer procedure {{PROCEDURE}}. The procedure will be
66called with the form to be expanded and rename and compare procedures
67and perform explicit renaming to maintain hygiene. See below for
68more information about explicit renaming macros.
69
70
71==== ir-macro-transformer
72
73<procedure>(ir-macro-transformer PROCEDURE)</procedure>
74
75Returns a implicit-renaming transformer object wrapping the
76syntax-transformer procedure {{PROCEDURE}}. The procedure will be
77called with the form to be expanded and an inject and compare
78procedure and perform implicit renaming to maintain hygiene.  See
79below for more information about implicit renaming macros.
80
81
82==== strip-syntax
83
84<procedure>(strip-syntax EXPRESSION)</procedure>
85
86Strips all syntactical information from {{EXPRESSION}}, returning a new expression
87where symbols have all context-information removed.
88
89
90=== Explicit renaming macros
91
92The low-level macro facility that CHICKEN provides is called "explicit
93renaming" and allows writing hygienic or non-hygienic macros
94procedurally.  When given a the return value of the one of the
95procedures {{er-macro-transformer}} or {{ir-macro-transformer}}
96instead of a {{syntax-rules}} form, {{define-syntax}} evaluates the
97procedure in a distinct expansion environment (initially having access
98to the exported identifiers of the {{scheme}} module). The procedure
99takes an expression and two other arguments and returns a transformed
100expression.
101
102For example, the transformation procedure for a {{call}} macro such
103that {{(call proc arg ...)}} expands into {{(proc arg ...)}} can be
104written as
105
106  (er-macro-transformer
107    (lambda (exp rename compare)
108      (cdr exp)))
109
110Expressions are represented as lists in the traditional manner,
111except that identifiers are represented as special uninterned symbols.
112
113The second argument to a transformation procedure is a renaming procedure that
114takes the representation of an identifier as its argument and returns the
115representation of a fresh identifier that occurs nowhere else in the
116program.  For example, the transformation procedure for a simplified
117version of the {{let}} macro might be written as
118
119  (er-macro-transformer
120    (lambda (exp rename compare)
121      (let ((vars (map car (cadr exp)))
122            (inits (map cadr (cadr exp)))
123            (body (cddr exp)))
124        `((lambda ,vars ,@body)
125          ,@inits))))
126
127This would not be hygienic, however.  A hygienic {{let}} macro must
128rename the identifier {{lambda}} to protect it from being captured by
129a local binding.  The renaming effectively creates a fresh alias for
130{{lambda}}, one that cannot be captured by any subsequent binding:
131
132  (er-macro-transformer
133    (lambda (exp rename compare)
134      (let ((vars (map car (cadr exp)))
135            (inits (map cadr (cadr exp)))
136            (body (cddr exp)))
137        `((,(rename 'lambda) ,vars ,@body)
138          ,@inits))))
139
140The expression returned by the transformation procedure will be
141expanded in the syntactic environment obtained from the syntactic
142environment of the macro application by binding any fresh identifiers
143generated by the renaming procedure to the denotations of the original
144identifiers in the syntactic environment in which the macro was
145defined.  This means that a renamed identifier will denote the same
146thing as the original identifier unless the transformation procedure
147that renamed the identifier placed an occurrence of it in a binding
148position.
149
150Identifiers obtained from any two calls to the renaming procedure with
151the same argument will necessarily be the same, but will denote the
152same syntactical binding. It is an error if the renaming procedure is
153called after the transformation procedure has returned.
154
155The third argument to a transformation procedure is a comparison
156predicate that takes the representations of two identifiers as its
157arguments and returns true if and only if they denote the same thing
158in the syntactic environment that will be used to expand the
159transformed macro application.  For example, the transformation
160procedure for a simplified version of the {{cond}} macro can be written
161as
162
163  (er-macro-transformer
164    (lambda (exp rename compare)
165      (let ((clauses (cdr exp)))
166        (if (null? clauses)
167            `(,(rename 'quote) unspecified)
168            (let* ((first (car clauses))
169                   (rest (cdr clauses))
170                   (test (car first)))
171              (cond ((and (symbol? test)
172                          (compare test (rename 'else)))
173                     `(,(rename 'begin) ,@(cdr first)))
174                    (else `(,(rename 'if)
175                            ,test
176                             (,(rename 'begin) ,@(cdr first))
177                             (,(rename 'cond) ,@rest)))))))))
178
179In this example the identifier {{else}} is renamed before being passed
180to the comparison predicate, so the comparison will be true if and
181only if the test expression is an identifier that denotes the same
182thing in the syntactic environment of the expression being transformed
183as {{else}} denotes in the syntactic environment in which the {{cond}}
184macro was defined.  If {{else}} were not renamed before being passed to
185the comparison predicate, then it would match a local variable that
186happened to be named {{else}}, and the macro would not be hygienic.
187The final recursive call to {{cond}} also needs to be renamed because
188someone might create an alias for this macro and use it in a {{let}}
189where {{cond}} is an ordinary variable.
190
191Some macros are non-hygienic by design.  For example, the
192following defines a {{loop}} macro that implicitly binds {{exit}} to an
193escape procedure.  The binding of {{exit}} is intended to capture free
194references to {{exit}} in the body of the loop, so {{exit}} is not
195renamed.
196
197  (define-syntax loop
198    (er-macro-transformer
199      (lambda (x r c)
200        (let ((body (cdr x)))
201          `(,(r 'call-with-current-continuation)
202            (,(r 'lambda) (exit)
203             (,(r 'let) ,(r 'f) () ,@body (,(r 'f)))))))))
204
205Suppose a {{while}} macro is implemented using {{loop}}, with the intent
206that {{exit}} may be used to escape from the {{while}} loop.  The {{while}}
207macro cannot be written as
208
209  (define-syntax while
210    (syntax-rules ()
211      ((while test body ...)
212       (loop (if (not test) (exit #f))
213             body ...))))
214
215because the reference to {{exit}} that is inserted by the {{while}} macro
216is intended to be captured by the binding of {{exit}} that will be
217inserted by the {{loop}} macro.  In other words, this {{while}} macro is
218not hygienic.  Like {{loop}}, it must be written using procedurally:
219
220  (define-syntax while
221    (er-macro-transformer
222      (lambda (x r c)
223        (let ((test (cadr x))
224              (body (cddr x)))
225          `(,(r 'loop)
226            (,(r 'if) (,(r 'not) ,test) (exit #f))
227            ,@body)))))
228
229Think about it: If we ''did'' rename {{exit}}, it would refer to an
230{{exit}} procedure existing in the context of the macro's definition.
231That one [[Unit library#exit|actually exists]]; it is the procedure
232that exits the Scheme interpreter.  Definitely ''not'' the one we want :)
233So now we make it refer to an {{exit}} that's locally bound in the
234environment where the macro is expanded.
235
236Note: this implementation of explicit-renaming macros allows passing
237arbitrary expressions to the renaming and comparison procedures. When
238being renamed, a fresh copy of the expression will be produced, with all
239identifiers renamed appropriately. Comparison also supports arbitrary
240expressions as arguments.
241
242=== Implicit renaming macros
243
244Explicit renaming macros generally require the user to perform quite a
245few renames, because most identifiers that aren't taken from the input
246expression should generally be inserted hygienically.  It would make
247more sense to give the output expression as-is, and only explicitly
248convert those identifiers that you want to treat as ''unhygienic''.
249
250This can be done with implicit renaming macros.  They just swap the
251default insertion "mode" from unhygienic to hygienic, so to speak.
252Here's the {{cond}} example from the previous section as an ir-macro:
253
254
255  (ir-macro-transformer
256    (lambda (exp inject compare)
257      (let ((clauses (cdr exp)))
258        (if (null? clauses)
259            `(quote unspecified)
260            (let* ((first (car clauses))
261                   (rest (cdr clauses))
262                   (test (car first)))
263              (cond ((and (symbol? test)
264                          (compare test 'else))
265                     `(begin ,@(cdr first)))
266                    (else `(if ,test
267                               (begin ,@(cdr first))
268                               (cond ,@rest)))))))))
269
270In this example the identifier {{else}} does ''not'' need to be renamed
271before being passed to the comparison predicate because it is already
272''implicitly'' renamed.  This comparison will also be true if and
273only if the test expression is an identifier that denotes the same
274thing in the syntactic environment of the expression being transformed
275as {{else}} denotes in the syntactic environment in which the {{cond}}
276macro was defined.  If {{else}} were not renamed before being passed to
277the comparison predicate, then it would match a local variable that
278happened to be named {{else}}, and the macro would not be hygienic.
279
280As you can see, the code is a lot clearer because it isn't obscured
281by excessive renaming.
282
283Here's the {{loop}} macro so you can see how hygiene can be broken
284with implicit renaming macros:
285
286  (define-syntax loop
287    (ir-macro-transformer
288      (lambda (expr inject compare)
289        (let ((body (cdr expr)))
290          `(call-with-current-continuation
291            (lambda (,(inject 'exit))
292             (let f () ,@body (f))))))))
293
294The {{while}} macro is a little trickier: do we inject the call to
295{{exit}} or not?  Just like the explicit renaming macro version
296did ''not'' rename it, we must inject it to allow it to be captured
297by the {{loop}} macro:
298
299  (define-syntax while
300    (ir-macro-transformer
301      (lambda (expr inject compare)
302        (let ((test (cadr expr))
303              (body (cddr expr)))
304          `(loop
305            (if (not ,test) (,(inject 'exit) #f))
306            ,@body)))))
307
308Note: Just like explicit renaming macros, this implementation of
309implicit renaming macros allow passing arbitrary expressions to
310the injection and comparison procedures.  The injection procedure
311also return fresh copies of its input.
312
313---
314Previous: [[Non-standard macros and special forms]]
315
316Next: [[Modules]]
Note: See TracBrowser for help on using the repository browser.