Ticket #394: ir-macros-documentation.patch
File ir-macros-documentation.patch, 6.6 KB (added by , 14 years ago) |
---|
-
manual/Macros
diff --git a/manual/Macros b/manual/Macros index 5c40447..796e189 100644
a b as 149 149 (else `(,(rename 'if) 150 150 ,test 151 151 (,(rename 'begin) ,@(cdr first)) 152 ( cond,@rest))))))))152 (,(r 'cond) ,@rest)))))))) 153 153 154 154 In this example the identifier {{else}} is renamed before being passed 155 155 to the comparison predicate, so the comparison will be true if and … … as {{else}} denotes in the syntactic environment in which the {{cond}} 159 159 macro was defined. If {{else}} were not renamed before being passed to 160 160 the comparison predicate, then it would match a local variable that 161 161 happened to be named {{else}}, and the macro would not be hygienic. 162 The final recursive call to {{cond}} also needs to be renamed because 163 someone might create an alias for this macro and use it in a {{let}} 164 where {{cond}} is an ordinary variable. 162 165 163 166 Some macros are non-hygienic by design. For example, the 164 167 following defines a {{loop}} macro that implicitly binds {{exit}} to an … … not hygienic. Like {{loop}}, it must be written using procedurally: 196 199 (,(r 'if) (,(r 'not) ,test) (exit #f)) 197 200 ,@body)))) 198 201 202 Think about it: If we ''did'' rename {{exit}}, it would refer to an 203 {{exit}} procedure existing in the context of the macro's definition. 204 That one [[Unit library#exit|actually exists]]; it is the procedure 205 that exits the Scheme interpreter. Definitely ''not'' the one we want :) 206 So now we make it refer to an {{exit}} that's locally bound in the 207 environment where the macro is expanded. 208 199 209 Note: this implementation of explicit-renaming macros allows passing 200 210 arbitrary expressions to the renaming and comparison procedures. When 201 211 being renamed, a fresh copy of the expression will be produced, with all 202 212 identifiers renamed appropriately. Comparison also supports arbitrary 203 213 expressions as arguments. 204 214 215 === Implicit renaming macros 216 217 Explicit renaming macros generally require the user to perform quite a 218 few renames, because most identifiers that aren't taken from the input 219 expression should generally be inserted hygienically. It would make 220 more sense to give the output expression as-is, and only explicitly 221 convert those identifiers that you want to treat as ''unhygienic''. 222 223 This can be done with implicit renaming macros. They just swap the 224 default insertion "mode" from unhygienic to hygienic, so to speak. 225 Here'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 243 In this example the identifier {{else}} does ''not'' need to be renamed 244 before being passed to the comparison predicate because it is already 245 ''implicitly'' renamed. This comparison will also be true if and 246 only if the test expression is an identifier that denotes the same 247 thing in the syntactic environment of the expression being transformed 248 as {{else}} denotes in the syntactic environment in which the {{cond}} 249 macro was defined. If {{else}} were not renamed before being passed to 250 the comparison predicate, then it would match a local variable that 251 happened to be named {{else}}, and the macro would not be hygienic. 252 253 As you can see, the code is a lot clearer because it isn't obscured 254 by excessive renaming. 255 256 Here's the {{loop}} macro so you can see how hygiene can be broken 257 with 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 267 The {{while}} macro is a little trickier: do we inject the call to 268 {{exit}} or not? Just like the explicit renaming macro version 269 did ''not'' rename it, we must inject it to allow it to be captured 270 by 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 281 Note: Just like explicit renaming macros, this implementation of 282 implicit renaming macros allow passing arbitrary expressions to 283 the injection and comparison procedures. The injection procedure 284 also return fresh copies of its input. 205 285 206 286 --- 207 287 Previous: [[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 43 43 macros in a more portable fashion, without hard-coding the signature 44 44 of a transformer procedure. 45 45 46 ==== ir-macro-transformer 47 48 <procedure>(ir-macro-transformer TRANSFORMER)</procedure> 49 50 This procedure accepts a ''reverse'' syntax transformer, also known as 51 an ''implicit renaming macro transformer''. This is a transformer which 52 works almost like er-macro-transformer, except the rename and compare 53 procedures it receives work a little differently. 54 55 The rename procedure is now called {{inject}} and instead of renaming 56 the identifier to be resolved in the macro's definition environment, 57 it will explicitly ''inject'' the identifier to be resolved in the 58 expansion environment. Any non-injected identifiers in the output 59 expression produced by the transformer will be implicitly renamed to 60 refer to the macro's environment instead. All identifiers in the 61 input expression are of course implicitly injected just like with 62 explicit renaming macros. 63 64 To compare an input identifier you can generally compare to the bare 65 symbol and only free identifiers will match. In practice, this means 66 that when you would call e.g. {{(compare (cadr expression) (rename 'x))}} 67 in an ER macro, you simply call {{(compare (cadr expression) 'x)}} in the 68 IR macro. Likewise, an ''unhygienic'' ER macro's comparison 69 {{(compare sym 'abc)}} should be written as {{(compare sym (inject 'abc))}} 70 in an IR macro. 46 71 47 72 --- 48 73 Previous: [[Unit library]]