Traditionally, one of the biggest potential problems in writing macros is generating code that interacts with other code improperly. Clojure has safeguards in place that other Lisps lack, but there is still potential for error.
Code generated by a macro will often be embedded in other code, and often will have user-defined code embedded within it. In either case, some set of symbols is likely already bound to values by the user of the macro. It’s possible for a macro to set up its own bindings that collide with those of the outer or inner context of the macro-users code, which can create bugs that are very difficult to identify. Macros that avoid these sorts of issues are called hygienic macros.
Consider a macro with an implementation that requires a let-bound
value. The name we choose is irrelevant to the user of our macro and
should be invisible to him, but we do have to choose
some name. Naively, we might try x
:
(defmacro unhygienic [& body] `(let [x :oops] ~@body)) ;= #'user/unhygenic (unhygienic (println "x:" x)) ;= #<CompilerException java.lang.RuntimeException: ;= Can't let qualified name: user/x, compiling:(NO_SOURCE_PATH:1)>
Clojure is smart enough not to let this code compile. As we explored
in quote Versus syntax-quote, all bare symbols are
namespace-qualified within a syntax-quote
d form. We can see the impact in
this case if we check the expansion of our macro:
(macroexpand-1 `(unhygienic (println "x:" x))) ;= (clojure.core/let [user/x :oops] ;= (clojure.core/println ...
No credit card required