Changeset 35470 in project


Ignore:
Timestamp:
04/29/18 13:24:53 (4 months ago)
Author:
sjamaan
Message:

Merge "Macros" explanatory manual section into "Module (chicken syntax)" reference, and add notes about our extensions to the standard to the "Module scheme" page

Location:
wiki/man/5
Files:
1 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • wiki/man/5/Module (chicken syntax)

    r35281 r35470  
    1414other s-expressions.  Only use these when you need to break hygiene in
    1515a controlled way; for many use cases {{syntax-rules}} is more
    16 appropriate, as it offers stronger guarantees of hygiene and is more
    17 high-level.
    18 
    19 ==== er-macro-transformer
     16appropriate, as it offers stronger guarantees of hygiene, is more
     17high-level and is standard R5RS Scheme.
     18
     19For those situations where you need more control, however, CHICKEN
     20supports two kinds of low-level macros: so-called explicit renaming
     21and implicit renaming macros.
     22
     23==== Explicit renaming macros
     24
     25The low-level macro facility that CHICKEN provides is called "explicit
     26renaming" and allows writing hygienic or non-hygienic macros
     27procedurally.  When given a the return value of the one of the
     28procedures {{er-macro-transformer}} or {{ir-macro-transformer}}
     29instead of a {{syntax-rules}} form, {{define-syntax}} evaluates the
     30procedure in a distinct expansion environment (initially having access
     31to the exported identifiers of the {{scheme}} module). The procedure
     32takes an expression and two other arguments and returns a transformed
     33expression.
     34
     35For example, the transformation procedure for a {{call}} macro such
     36that {{(call proc arg ...)}} expands into {{(proc arg ...)}} can be
     37written as
     38
     39  (er-macro-transformer
     40    (lambda (exp rename compare)
     41      (cdr exp)))
     42
     43Expressions are represented as lists in the traditional manner,
     44except that identifiers are represented as special uninterned symbols.
     45
     46The second argument to a transformation procedure is a renaming procedure that
     47takes the representation of an identifier as its argument and returns the
     48representation of a fresh identifier that occurs nowhere else in the
     49program.  For example, the transformation procedure for a simplified
     50version of the {{let}} macro might be written as
     51
     52  (er-macro-transformer
     53    (lambda (exp rename compare)
     54      (let ((vars (map car (cadr exp)))
     55            (inits (map cadr (cadr exp)))
     56            (body (cddr exp)))
     57        `((lambda ,vars ,@body)
     58          ,@inits))))
     59
     60This would not be hygienic, however.  A hygienic {{let}} macro must
     61rename the identifier {{lambda}} to protect it from being captured by
     62a local binding.  The renaming effectively creates a fresh alias for
     63{{lambda}}, one that cannot be captured by any subsequent binding:
     64
     65  (er-macro-transformer
     66    (lambda (exp rename compare)
     67      (let ((vars (map car (cadr exp)))
     68            (inits (map cadr (cadr exp)))
     69            (body (cddr exp)))
     70        `((,(rename 'lambda) ,vars ,@body)
     71          ,@inits))))
     72
     73The expression returned by the transformation procedure will be
     74expanded in the syntactic environment obtained from the syntactic
     75environment of the macro application by binding any fresh identifiers
     76generated by the renaming procedure to the denotations of the original
     77identifiers in the syntactic environment in which the macro was
     78defined.  This means that a renamed identifier will denote the same
     79thing as the original identifier unless the transformation procedure
     80that renamed the identifier placed an occurrence of it in a binding
     81position.
     82
     83Identifiers obtained from any two calls to the renaming procedure with
     84the same argument will necessarily be the same, but will denote the
     85same syntactical binding. It is an error if the renaming procedure is
     86called after the transformation procedure has returned.
     87
     88The third argument to a transformation procedure is a comparison
     89predicate that takes the representations of two identifiers as its
     90arguments and returns true if and only if they denote the same thing
     91in the syntactic environment that will be used to expand the
     92transformed macro application.  For example, the transformation
     93procedure for a simplified version of the {{cond}} macro can be written
     94as
     95
     96  (er-macro-transformer
     97    (lambda (exp rename compare)
     98      (let ((clauses (cdr exp)))
     99        (if (null? clauses)
     100            `(,(rename 'quote) unspecified)
     101            (let* ((first (car clauses))
     102                   (rest (cdr clauses))
     103                   (test (car first)))
     104              (cond ((and (symbol? test)
     105                          (compare test (rename 'else)))
     106                     `(,(rename 'begin) ,@(cdr first)))
     107                    (else `(,(rename 'if)
     108                            ,test
     109                             (,(rename 'begin) ,@(cdr first))
     110                             (,(r 'cond) ,@rest)))))))))
     111
     112In this example the identifier {{else}} is renamed before being passed
     113to the comparison predicate, so the comparison will be true if and
     114only if the test expression is an identifier that denotes the same
     115thing in the syntactic environment of the expression being transformed
     116as {{else}} denotes in the syntactic environment in which the {{cond}}
     117macro was defined.  If {{else}} were not renamed before being passed to
     118the comparison predicate, then it would match a local variable that
     119happened to be named {{else}}, and the macro would not be hygienic.
     120The final recursive call to {{cond}} also needs to be renamed because
     121someone might create an alias for this macro and use it in a {{let}}
     122where {{cond}} is an ordinary variable.
     123
     124Some macros are non-hygienic by design.  For example, the
     125following defines a {{loop}} macro that implicitly binds {{exit}} to an
     126escape procedure.  The binding of {{exit}} is intended to capture free
     127references to {{exit}} in the body of the loop, so {{exit}} is not
     128renamed.
     129
     130  (define-syntax loop
     131    (er-macro-transformer
     132      (lambda (x r c)
     133        (let ((body (cdr x)))
     134          `(,(r 'call-with-current-continuation)
     135            (,(r 'lambda) (exit)
     136             (,(r 'let) ,(r 'f) () ,@body (,(r 'f)))))))))
     137
     138Suppose a {{while}} macro is implemented using {{loop}}, with the intent
     139that {{exit}} may be used to escape from the {{while}} loop.  The {{while}}
     140macro cannot be written as
     141
     142  (define-syntax while
     143    (syntax-rules ()
     144      ((while test body ...)
     145       (loop (if (not test) (exit #f))
     146             body ...))))
     147
     148because the reference to {{exit}} that is inserted by the {{while}} macro
     149is intended to be captured by the binding of {{exit}} that will be
     150inserted by the {{loop}} macro.  In other words, this {{while}} macro is
     151not hygienic.  Like {{loop}}, it must be written using procedurally:
     152
     153  (define-syntax while
     154    (er-macro-transformer
     155      (lambda (x r c)
     156        (let ((test (cadr x))
     157              (body (cddr x)))
     158          `(,(r 'loop)
     159            (,(r 'if) (,(r 'not) ,test) (exit #f))
     160            ,@body)))))
     161
     162Think about it: If we ''did'' rename {{exit}}, it would refer to an
     163{{exit}} procedure existing in the context of the macro's definition.
     164That one [[Unit library#exit|actually exists]]; it is the procedure
     165that exits the Scheme interpreter.  Definitely ''not'' the one we want :)
     166So now we make it refer to an {{exit}} that's locally bound in the
     167environment where the macro is expanded.
     168
     169Note: this implementation of explicit-renaming macros allows passing
     170arbitrary expressions to the renaming and comparison procedures. When
     171being renamed, a fresh copy of the expression will be produced, with all
     172identifiers renamed appropriately. Comparison also supports arbitrary
     173expressions as arguments.
     174
     175
     176===== er-macro-transformer
    20177
    21178<procedure>(er-macro-transformer TRANSFORMER)</procedure>
     
    24181the procedural macro body {{TRANSFORMER}}, which is a procedure of
    25182three arguments.
     183
     184This procedure will be called on expansion with the complete
     185s-expression of the macro invocation, a rename procedure that
     186hygienically renames identifiers and a comparison procedure that
     187compares (possibly renamed) identifiers (see the section "Explicit
     188renaming macros" below for a detailed explanation on non-R5RS macros).
    26189
    27190Implementation note: this procedure currently just returns its
     
    30193transformer procedure.
    31194
    32 ==== ir-macro-transformer
     195=== Implicit renaming macros
     196
     197Explicit renaming macros generally require the user to perform quite a
     198few renames, because most identifiers that aren't taken from the input
     199expression should generally be inserted hygienically.  It would make
     200more sense to give the output expression as-is, and only explicitly
     201convert those identifiers that you want to treat as ''unhygienic''.
     202
     203This can be done with implicit renaming macros.  They just swap the
     204default insertion "mode" from unhygienic to hygienic, so to speak.
     205Here's the {{cond}} example from the previous section as an ir-macro:
     206
     207
     208  (ir-macro-transformer
     209    (lambda (exp inject compare)
     210      (let ((clauses (cdr exp)))
     211        (if (null? clauses)
     212            `(quote unspecified)
     213            (let* ((first (car clauses))
     214                   (rest (cdr clauses))
     215                   (test (car first)))
     216              (cond ((and (symbol? test)
     217                          (compare test 'else))
     218                     `(begin ,@(cdr first)))
     219                    (else `(if ,test
     220                               (begin ,@(cdr first))
     221                               (cond ,@rest)))))))))
     222
     223In this example the identifier {{else}} does ''not'' need to be renamed
     224before being passed to the comparison predicate because it is already
     225''implicitly'' renamed.  This comparison will also be true if and
     226only if the test expression is an identifier that denotes the same
     227thing in the syntactic environment of the expression being transformed
     228as {{else}} denotes in the syntactic environment in which the {{cond}}
     229macro was defined.  If {{else}} were not renamed before being passed to
     230the comparison predicate, then it would match a local variable that
     231happened to be named {{else}}, and the macro would not be hygienic.
     232
     233As you can see, the code is a lot clearer because it isn't obscured
     234by excessive renaming.
     235
     236Here's the {{loop}} macro so you can see how hygiene can be broken
     237with implicit renaming macros:
     238
     239  (define-syntax loop
     240    (ir-macro-transformer
     241      (lambda (expr inject compare)
     242        (let ((body (cdr expr)))
     243          `(call-with-current-continuation
     244            (lambda (,(inject 'exit))
     245             (let f () ,@body (f))))))))
     246
     247The {{while}} macro is a little trickier: do we inject the call to
     248{{exit}} or not?  Just like the explicit renaming macro version
     249did ''not'' rename it, we must inject it to allow it to be captured
     250by the {{loop}} macro:
     251
     252  (define-syntax while
     253    (ir-macro-transformer
     254      (lambda (expr inject compare)
     255        (let ((test (cadr expr))
     256              (body (cddr expr)))
     257          `(loop
     258            (if (not ,test) (,(inject 'exit) #f))
     259            ,@body)))))
     260
     261Note: Just like explicit renaming macros, this implementation of
     262implicit renaming macros allow passing arbitrary expressions to
     263the injection and comparison procedures.  The injection procedure
     264also return fresh copies of its input.
     265
     266
     267===== ir-macro-transformer
    33268
    34269<procedure>(ir-macro-transformer TRANSFORMER)</procedure>
     
    46281refer to the macro's environment instead.  All identifiers in the
    47282input expression are of course implicitly injected just like with
    48 explicit renaming macros.
     283explicit renaming macros.  See the section above for a more complete
     284explanation.
    49285
    50286To compare an input identifier you can generally compare to the bare
  • wiki/man/5/Module scheme

    r35116 r35470  
    10921092<program>. They have the following form:
    10931093
    1094  (define-syntax <keyword> <transformer spec>)
    1095 
    1096 <Keyword> is an identifier, and the <transformer spec> should be an
    1097 instance of syntax-rules. The top-level syntactic environment is
    1098 extended by binding the <keyword> to the specified transformer.
    1099 
    1100 There is no define-syntax analogue of internal definitions.
     1094<macro>(define-syntax <keyword> <transformer spec>)</macro>
     1095
     1096{{<Keyword>}} is an identifier, and the {{<transformer spec>}} should
     1097be an instance of {{syntax-rules}}.  Note that CHICKEN also supports
     1098{{er-macro-transformer}} and {{ir-macro-transformer}} here.  For more
     1099information see [[Module (chicken syntax)|the (chicken syntax) module]].
     1100
     1101The top-level syntactic environment is extended by binding the
     1102<keyword> to the specified transformer.
     1103
     1104In standard Scheme, there is no define-syntax analogue of internal
     1105definitions in, but CHICKEN allows these as an extension to the
     1106standard.  This means {{define-syntax}} may be used to define local
     1107macros that are visible throughout the rest of the body in which the
     1108definition occurred, i.e.
     1109
     1110  (let ()
     1111    ...
     1112    (define-syntax foo ...)
     1113    (define-syntax bar ...)
     1114    ...)
     1115
     1116is expanded into
     1117
     1118  (let ()
     1119    ...
     1120    (letrec-syntax ((foo ...) (bar ...))
     1121      ...) )
     1122
     1123{{syntax-rules}} supports [[http://srfi.schemers.org/srfi-46/|SRFI-46]]
     1124in allowing the ellipsis identifier to be user-defined by passing it as the first
     1125argument to the {{syntax-rules}} form. Also, "tail" patterns of the form
     1126
     1127  (syntax-rules ()
     1128    ((_ (a b ... c)
     1129      ...
     1130
     1131are supported.
     1132
     1133The effect of destructively modifying the s-expression passed to a
     1134transformer procedure is undefined.
    11011135
    11021136Although macros may expand into definitions and syntax definitions in
Note: See TracChangeset for help on using the changeset viewer.