If we first parsed completely before doing macro expansion, could we not replace all occurrences of names by pointers to symbol table entries and thereby eliminate the capturing problem? The answer is no. One of the most important uses of macros is to provide syntactically convenient binding abstractions. The distinction between binding occurrences and other uses of identifiers should be determined by a particular syntactic abstraction, not predetermined by the parser.I've puzzled over this statement for quite some time now. The only instance I have thought of where binding relationships can't be predetermined is in higher-order macros, where an operator can be passed to a macro as an argument that may then be used to determine the binding relationships of other arguments:
If we apply this macro to a normal expression, var does not get bound:(define-syntax apply-syntax
(syntax-rules ()
[(_ operator var)
(operator var)]))
If we apply it to a macro that binds its argument, var does get bound:(apply-syntax add1 x)
=> (add1 x)
If we apply it to quote, var gets treated as a symbolic literal:(apply-syntax make-abstraction x)
=> (lambda (x) (+ x 17))
With syntactic closures, you have to commit to all the binding relationships up front in the definition of apply-syntax, which means you can't pass it arguments that don't adhere to those binding relationships.(apply-syntax quote x)
=> (quote x)
How important is higher-order macro programming? Particularly, how important is this "binding-polymorphic" style of higher-order macro programming? I'm not sure, but I have a feeling that syntactic closures may be more useful than it's given credit for.
No comments:
Post a Comment