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.

Illustration for Clojure
Lesson 1 is on the house.

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.

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)))))
Match this when you type.

Quiz

What does v# expand to?

Quiz

How many times does lookup run?

Quiz

When do you need an explicit (gensym)?

Quiz

What does &env give a macro?

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.