source: project/wiki/man/5/TODO/Macros @ 35323

Last change on this file since 35323 was 35323, checked in by felix winkelmann, 2 years ago

manual: more links changed, added R5RS page (or should it be R7RS?)

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