Syntax-quote qualifies symbols (let becomes clojure.core/let), so a macro cannot accidentally refer to a caller's local named let. But locals the macro introduces can still clash with the caller's names.
Clojure Expert · Lesson 1 · On the house
Macro hygiene
Symbol capture, auto-gensym x#, gensym, and evaluating arguments once. Lesson 1 is free — on the house.
Lessons · Clojure Expert · Lesson 1 of 10 · On the house
Macro hygiene
Symbol capture, auto-gensym x#, gensym, and evaluating arguments once.

Auto-gensym fixes this: inside one syntax-quote, tmp# expands to a unique symbol such as tmp__1234__auto__ everywhere it appears. Clojure refuses to compile a plain unqualified local inside syntax-quote.
When the symbol must be shared across several syntax-quotes, create it with (gensym "tmp") in the macro body and splice it with ~.
Bind each argument once: `(let [v# ~expr] (if v# v# ~default)) evaluates expr a single time even though v# is used twice.
&form (the whole call) and &env (the locals in scope) are implicit macro arguments; &env lets a macro see which symbols are local at the call site.
auto-gensym
(defmacro or-default [expr default]
`(let [v# ~expr]
(if (some? v#) v# ~default)))
(def hits (atom 0))
(defn lookup [] (swap! hits inc) nil)
(println (or-default (lookup) :none))
(println @hits)
(println (first (second (macroexpand-1 '(or-default x 1)))))
Quiz
Quiz
Quiz
Quiz
Check
Match the still for: Macro hygiene.
Open-book: the answers are on this page. Pass every quiz and check (5) to mark the lesson done. This visit: 0/5.