Hygienic macro
From Wikipedia, the free encyclopedia
Hygienic macros are macros whose expansion is guaranteed not to cause collisions with existing symbol definitions. They are a feature of programming languages such as Scheme and Dylan.
[edit] The hygiene problem
In a programming language that has unhygienic macros, it is possible for existing variable bindings to be hidden from a macro by variable bindings that are created during its expansion. In C, this problem can be illustrated by the following fragment:
#define INCI(i) {int a=0; ++i;} int main() { int a = 0, b = 0; INCI(a); INCI(b); printf("a is now %d, b is now %d\n", a, b); }
Running the above through the C preprocessor produces:
int main() { int a = 0, b = 0; {int a=0; ++a;}; {int a=0; ++b;}; printf("a is now %d, b is now %d\n", a, b); }
So the variable `a' declared in the top scope is never altered by the execution of the program, as the output of the compiled program shows:
a is now 0, b is now 1
Note that some C compilers, such as gcc, have an option like `-Wshadow' that warns when a local variable shadows a global variable, which would have caught the above problem.
The "hygiene problem" can extend beyond variable bindings. Consider this Common Lisp macro:
(defmacro my-unless (condition &body body) `(if (not ,condition) (progn ,@body)))
While there are no references to variables in this macro, it assumes the symbols "if", "not", and "progn" are all bound to their usual function definitions. If, however the above macro is used in the following code:
(flet ((not (x) x)) (my-unless t (format t "This should not be printed!")))
Because the definition of "not" has been locally altered, the message "This should not be printed!" will be printed, which is probably not the intended behaviour.
[edit] Strategies
In some languages such as Common Lisp, Scheme and others of the Lisp programming language family, macros provide a powerful means of extending the language. Here the lack of hygiene in conventional macros is resolved by several strategies.
- Obfuscation. If the programmer needs to use temporary storage during the expansion of a macro, he can use one with an unusual name and hope that the same name will never be used in a program that uses his macro.
- Temporary symbol creation. In some programming languages it is possible for a new variable name, or symbol, to be generated and bound to a temporary location. The language processing system ensures that this never clashes with another name or location in the execution environment. The responsibility for choosing to use this feature within the body of a macro definition is left to the programmer. This method was used in MacLisp, where a function (gensym) could be used to generate a new symbol name. Similar functions exist in many lisp-like languages, usually named gensym.
- Hygienic transformation. The processor responsible for transforming the patterns of the input form into an output form detects symbol clashes and resolves them by temporarily changing the names of symbols. This kind of processing is supported by Scheme's "let-syntax" and "define-syntax" macro creation systems.
[edit] References
- Richard Kelsey, William Clinger, Jonathan Rees (eds.), Revised5 Report on the Algorithmic Language Scheme (R5RS), 4.3 Macros