Changeset 38048 in project


Ignore:
Timestamp:
01/02/20 14:21:41 (3 months ago)
Author:
juergen
Message:

tutorial on explicit-renaming-macros updated

File:
1 edited

Legend:

Unmodified
Added
Removed
  • wiki/explicit-renaming-macros

    r37488 r38048  
    3737</enscript>
    3838
    39 and you have to destructure the form argument, which is the whole
     39You have to destructure the form argument, which is the whole
    4040macro-from, (swap! x y) in the example, to fill in the ellipsis above.
    4141That is
     
    7373Here is, where renaming comes into the play. You don't need gensym, use
    7474rename instead.  And you needn't bother, which symbol to rename, rename
    75 all.
     75all. This is the brute-force-method.
    7676
    7777What does that mean, "all"? It's easy in the present example, everything
     
    147147operator.
    148148
    149 The last version is easy to write, because the macro is small. There are
    150 only three symbols to be renamed.
    151 
    152 But what if you have a giant macro with dozens of symbols to be renamed?
    153 It's not a problem to prefix them all with ,% in the replacement text,
    154 but then you have to add a giant let defining the renamed symbols.  And
    155 this is cumbersome indeed.
    156 
    157 Wouldn't it be nice, if this additional let could be added
    158 automatically?
    159 
    160 Well, there is a trick to achieve this. It's based on
    161 read-macros.  And, I've written a macro named define-er-macro which
    162 does it as well. But first, here is the trick with a read-macro.
    163 
    164 You can add a read-macro with (set-read-syntax! chr proc) just before
    165 the macro definition and remove it with (set-read-syntax! chr #f) after
    166 the definition. But if chr were already set to read-syntax before, the
    167 former call would override and hence destroy it. So a better way is to
    168 save and restore the whole read-table, which is done with the parameter
    169 current-read-table, which stores all read-macros.
    170 
    171 So here is a version of swap! using this trick.
    172 
    173 <enscript highlight="scheme">
    174 ;; save read-table
    175 (define old-crt (copy-read-table (current-read-table)))
    176 
    177 ;; define read-macro
    178 (set-read-syntax! #\%
    179                   (lambda (port)
    180                     (let ((xpr (read port)))
    181                       (if (symbol? xpr)
    182                         `(rename ',xpr)
    183                         xpr))))
    184 
    185 ;; define macro
    186 (define-syntax swap!
    187   (er-macro-transformer
    188     (lambda (form rename compare?)
    189       (let ((x (cadr form))
    190             (y (caddr form)))
    191         `(,%let ((,%tmp ,x))
    192            (,%set! ,x ,y)
    193            (,%set! ,y ,%tmp))))))
    194 
    195 ;; restore read-table
    196 (current-read-table old-crt)
    197 </enscript>
    198 
    199 You can convince yourself that renaming has taken place without being
    200 defined explicitly by simply issuing (pp (expand '(swap! x y))) as above.
    201 
    202 And here is the much easier solution with define-er-macro from the
    203 procedural-macros egg.
    204 
    205 <enscript highlight="scheme">
    206 (define-er-macro (swap! form % compare?)
    207   (let ((x (cadr form))
    208         (y (caddr form)))
    209     `(,%let ((,%tmp ,x))
    210        (,%set! ,x ,y)
    211        (,%set! ,y ,%tmp))))
    212 </enscript>
    213 
    214 Note, that instead of the rename parameter, you must provide a
    215 rename-prefix, % in our example. The let with the renamed symbols is
    216 generated automatically, as you can see again with
    217 (pp (expand '(swap! x y)))
     149What happens, if you forget to rename a symbol in the expansion
     150code? Then the macro is not hygienic, which might be intended by the
     151author. For example, anaphoric macros export a symbol on purpose, mostly
     152named it or self. That's impossible with syntax-rules macros!
    218153
    219154We havn't used the compare? argument in the transformer in this example.
     
    299234
    300235But we still aren't satisfied. Ok, destructuring can be simplyfied with
    301 bind, but what about renaming/injecting and additional keywords?
    302 
    303 Well, the procedural-macros module, will help with solving these
    304 problems.  For example, we've provided a procedural variant of
    305 syntax-rules, named macro-rules, which cares for all three arguments of
    306 the macro-transformer, and -- based on it -- a hygienic version of
    307 define-macro.
     236bind, but that can be done transparently behind the scene. We've done
     237this in the procedural-macros egg. In particular, you'll find there two
     238macros, define-macro and macro-rules. The latter is a procedural variant
     239of syntax-rules, which cares for all three arguments of the transformer
     240routine, the former a hygienic version of our good old chicken-3 macro.
     241
     242The form argument of the transformer is handled automatically by bind,
     243the rename argument can be replaced by a list of the renamed symbols,
     244but the compare? argument must be retained. So we can write
     245
     246<enscript highlight="scheme">
     247(define-macro (swap! x y)
     248  (with-explicit-renaming (compare? %let %tmp %set!)
     249    `(,%let ((,%tmp ,x))
     250       (,%set! ,x ,y)
     251       (,%set! ,y ,%tmp))))
     252</enscript>
     253
     254or -- to avoid renaming at all --
     255
     256<enscript highlight="scheme">
     257(define-macro (swap! x y)
     258  (with-implicit-renaming (compare?)
     259    `(let ((tmp ,x))
     260       (set! ,x ,y)
     261       (set! ,y tmp))))
     262</enscript>
     263
     264Note, that there is neither a symbol to inject, nor one to compare.
     265Because this happens very often, we can write in this case simply
    308266
    309267<enscript highlight="scheme">
     
    314272</enscript>
    315273
    316 This is now hygienic!
     274and so we have completed a full circle, resulting in a hygienic macro
     275now!
    317276
    318277The following is an example for using maro-rules, a verbose if, whith
     
    325284    ((_ test (then . xprs) (else . yprs))
    326285     `(if ,test
    327                                 (begin ,@xprs)
    328                                 (begin ,@yprs)))))
     286        (begin ,@xprs)
     287        (begin ,@yprs)))))
     288</enscript>
     289
     290And this is an example of an anaphoric macro, a version of lambda, which
     291can refer to itself, and hence can be used as unnamed recursive
     292routine.
     293
     294<enscript highlight="scheme">
     295(import-for-syntax (only procedural-macros macro-rules))
     296(define-syntax alambda
     297  (macro-rules self ()
     298    ((_ args xpr . xprs)
     299     `(letrec ((,self (lambda ,args ,xpr ,@xprs)))
     300        ,self))))
    329301</enscript>
    330302
     
    356328Nov 01, 2017
    357329
    358 == and again for chicken-5
     330== and again and again for chicken-5
    359331
    360332Mar 29, 2019
    361333
     334Jan 02, 2020
Note: See TracChangeset for help on using the changeset viewer.