Learn how macros fit into Clojure metaprogramming, where they beat functions, and how to review the generated code before treating syntax as an abstraction.
Macros are one metaprogramming tool, not the whole story. Use them when you need to transform source forms into different source forms before evaluation.
The most useful mental model is:
1caller form -> macro expansion -> ordinary Clojure form -> runtime behavior
1(defmacro unless
2 [condition & body]
3 `(when-not ~condition
4 ~@body))
The macro receives the condition and body forms unevaluated. It returns a form that uses ordinary Clojure.
1(macroexpand-1
2 '(unless (:ready? job)
3 (println "not ready")
4 :wait))
5
6;; roughly:
7(clojure.core/when-not (:ready? job)
8 (println "not ready")
9 :wait)
Review the expansion as generated source. If the expansion is not readable, the macro is not ready.
| Requirement | Prefer |
|---|---|
| Transform values already computed at runtime | Function |
| Accept caller behavior as a callback | Higher-order function |
| Describe configuration | Data |
| Delay, skip, or splice caller forms | Macro |
| Create new binding/control syntax | Macro |
| Shape | Example | Review focus |
|---|---|---|
| Control form | unless, with-retry |
Evaluation order and body count. |
| Binding form | with-open, with-resource |
Scope, cleanup, generated locals. |
| Definition generator | defroutes, defevent |
Names, generated vars, error messages. |
| Small DSL | Query or routing syntax | Predictable expansion and limited vocabulary. |
| Java habit | Clojure macro lesson |
|---|---|
| Annotation hides generated methods | Inspect macro expansion before trusting call syntax. |
| Reflection adapts at runtime | Use macros only when source shape must change. |
| Builder creates runtime configuration | Prefer Clojure data unless syntax is the point. |
Before merging a macro:
| Check | Good answer |
|---|---|
| Why not a function? | The macro controls syntax or evaluation. |
| What does one representative expansion look like? | It is short and ordinary. |
| Are caller expressions evaluated the intended number of times? | Yes, usually once. |
| Are generated locals hygienic? | Yes, auto-gensyms or explicit gensyms. |