Ticket #394: ir-macros-documentation.patch

File ir-macros-documentation.patch, 6.6 KB (added by sjamaan, 14 years ago)
  • manual/Macros

    diff --git a/manual/Macros b/manual/Macros
    index 5c40447..796e189 100644
    a b as 
    149149                  (else `(,(rename 'if)
    150150                          ,test
    151151                           (,(rename 'begin) ,@(cdr first))
    152                            (cond ,@rest))))))))
     152                           (,(r 'cond) ,@rest))))))))
    153153
    154154In this example the identifier {{else}} is renamed before being passed
    155155to the comparison predicate, so the comparison will be true if and
    as {{else}} denotes in the syntactic environment in which the {{cond}} 
    159159macro was defined.  If {{else}} were not renamed before being passed to
    160160the comparison predicate, then it would match a local variable that
    161161happened to be named {{else}}, and the macro would not be hygienic.
     162The final recursive call to {{cond}} also needs to be renamed because
     163someone might create an alias for this macro and use it in a {{let}}
     164where {{cond}} is an ordinary variable.
    162165
    163166Some macros are non-hygienic by design.  For example, the
    164167following defines a {{loop}} macro that implicitly binds {{exit}} to an
    not hygienic. Like {{loop}}, it must be written using procedurally: 
    196199          (,(r 'if) (,(r 'not) ,test) (exit #f))
    197200          ,@body))))
    198201
     202Think about it: If we ''did'' rename {{exit}}, it would refer to an
     203{{exit}} procedure existing in the context of the macro's definition.
     204That one [[Unit library#exit|actually exists]]; it is the procedure
     205that exits the Scheme interpreter.  Definitely ''not'' the one we want :)
     206So now we make it refer to an {{exit}} that's locally bound in the
     207environment where the macro is expanded.
     208
    199209Note: this implementation of explicit-renaming macros allows passing
    200210arbitrary expressions to the renaming and comparison procedures. When
    201211being renamed, a fresh copy of the expression will be produced, with all
    202212identifiers renamed appropriately. Comparison also supports arbitrary
    203213expressions as arguments.
    204214
     215=== Implicit renaming macros
     216
     217Explicit renaming macros generally require the user to perform quite a
     218few renames, because most identifiers that aren't taken from the input
     219expression should generally be inserted hygienically.  It would make
     220more sense to give the output expression as-is, and only explicitly
     221convert those identifiers that you want to treat as ''unhygienic''.
     222
     223This can be done with implicit renaming macros.  They just swap the
     224default insertion "mode" from unhygienic to hygienic, so to speak.
     225Here's the {{cond}} example from the previous section as an ir-macro:
     226
     227
     228  (ir-macro-transformer
     229    (lambda (exp inject compare)
     230      (let ((clauses (cdr exp)))
     231        (if (null? clauses)
     232            `(quote unspecified)
     233            (let* ((first (car clauses))
     234                   (rest (cdr clauses))
     235                   (test (car first)))
     236              (cond ((and (symbol? test)
     237                          (compare test 'else))
     238                     `(begin ,@(cdr first)))
     239                    (else `(if ,test
     240                               (begin ,@(cdr first))
     241                               (cond ,@rest)))))))))
     242
     243In this example the identifier {{else}} does ''not'' need to be renamed
     244before being passed to the comparison predicate because it is already
     245''implicitly'' renamed.  This comparison will also be true if and
     246only if the test expression is an identifier that denotes the same
     247thing in the syntactic environment of the expression being transformed
     248as {{else}} denotes in the syntactic environment in which the {{cond}}
     249macro was defined.  If {{else}} were not renamed before being passed to
     250the comparison predicate, then it would match a local variable that
     251happened to be named {{else}}, and the macro would not be hygienic.
     252
     253As you can see, the code is a lot clearer because it isn't obscured
     254by excessive renaming.
     255
     256Here's the {{loop}} macro so you can see how hygiene can be broken
     257with implicit renaming macros:
     258
     259  (define-syntax loop
     260    (ir-macro-transformer
     261      (lambda (expr inject compare)
     262        (let ((body (cdr expr)))
     263          `(call-with-current-continuation
     264            (lambda (,(inject 'exit))
     265             (let f () ,@body (f))))))))
     266
     267The {{while}} macro is a little trickier: do we inject the call to
     268{{exit}} or not?  Just like the explicit renaming macro version
     269did ''not'' rename it, we must inject it to allow it to be captured
     270by the {{loop}} macro:
     271
     272  (define-syntax while
     273    (ir-macro-transformer
     274      (lambda (expr inject compare)
     275        (let ((test (cadr expr))
     276              (body (cddr expr)))
     277          `(loop
     278            (if (not ,test) (,(inject 'exit) #f))
     279            ,@body)))))
     280
     281Note: Just like explicit renaming macros, this implementation of
     282implicit renaming macros allow passing arbitrary expressions to
     283the injection and comparison procedures.  The injection procedure
     284also return fresh copies of its input.
    205285
    206286---
    207287Previous: [[Non-standard macros and special forms]]
  • manual/Unit

    diff --git a/manual/Unit expand b/manual/Unit expand
    index 61fbceb..b9b6f41 100644
    a b This procedure does nothing and is available for writing low-level 
    4343macros in a more portable fashion, without hard-coding the signature
    4444of a transformer procedure.
    4545
     46==== ir-macro-transformer
     47
     48<procedure>(ir-macro-transformer TRANSFORMER)</procedure>
     49
     50This procedure accepts a ''reverse'' syntax transformer, also known as
     51an ''implicit renaming macro transformer''.  This is a transformer which
     52works almost like er-macro-transformer, except the rename and compare
     53procedures it receives work a little differently.
     54
     55The rename procedure is now called {{inject}} and instead of renaming
     56the identifier to be resolved in the macro's definition environment,
     57it will explicitly ''inject'' the identifier to be resolved in the
     58expansion environment.  Any non-injected identifiers in the output
     59expression produced by the transformer will be implicitly renamed to
     60refer to the macro's environment instead.  All identifiers in the
     61input expression are of course implicitly injected just like with
     62explicit renaming macros.
     63
     64To compare an input identifier you can generally compare to the bare
     65symbol and only free identifiers will match.  In practice, this means
     66that when you would call e.g.  {{(compare (cadr expression) (rename 'x))}}
     67in an ER macro, you simply call {{(compare (cadr expression) 'x)}} in the
     68IR macro.  Likewise, an ''unhygienic'' ER macro's comparison
     69{{(compare sym 'abc)}} should be written as {{(compare sym (inject 'abc))}}
     70in an IR macro.
    4671
    4772---
    4873Previous: [[Unit library]]