Monday, February 21, 2005

Higher-order macros

One of the benefits of Macros that Work over syntactic closures as a macro system is the ability to discover the binding relationships of macro arguments after they get expanded, not before.
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:
(define-syntax apply-syntax
(syntax-rules ()
[(_ operator var)
(operator var)]))
If we apply this macro to a normal expression, var does not get bound:
(apply-syntax add1 x)
=> (add1 x)
If we apply it to a macro that binds its argument, var does get bound:
(apply-syntax make-abstraction x)
=> (lambda (x) (+ x 17))
If we apply it to quote, var gets treated as a symbolic literal:
(apply-syntax quote x)
=> (quote x)
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.
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: