Learn how defmacro receives unevaluated forms, returns generated Clojure code, and should be reviewed with macroexpand before Java teams rely on a custom syntax form.
defmacro defines a macro: a named source transformation that receives unevaluated forms and returns Clojure code. That returned code is what later compiles or evaluates.
Use defmacro only when a function cannot express the call shape. The macro author owns the generated code, not only the compact syntax at the call site.
1(defmacro macro-name
2 "Optional docstring."
3 [arguments]
4 generated-code)
The body runs during macro expansion. It should usually build code with syntax quote:
1(defmacro unless
2 "Runs body when condition is false."
3 [condition & body]
4 `(when-not ~condition
5 ~@body))
This macro receives a condition form and zero or more body forms. It generates a when-not form.
Use macroexpand-1 to inspect one expansion step:
1(macroexpand-1
2 '(unless (:enabled? config)
3 (println "disabled")
4 :skipped))
5
6;; roughly:
7(clojure.core/when-not (:enabled? config)
8 (println "disabled")
9 :skipped)
The expansion is the code to review. If you cannot explain it in plain Clojure terms, do not add another feature to the macro.
| Call-site expression | What the macro receives |
|---|---|
(:enabled? config) |
A list form, not the Boolean result. |
(println "disabled") |
A form that may or may not appear in generated code. |
:skipped |
A literal keyword form. |
config |
A symbol, not the value bound to that symbol. |
This is the reason macros can control evaluation. It is also the reason they can surprise callers.
A bad macro can accidentally evaluate the same caller form twice:
1(defmacro bad-debug
2 [expr]
3 `(do
4 (println "value:" ~expr)
5 ~expr))
If expr sends an email, changes an atom, or calls a database, it runs twice. Bind the result once with an auto-gensym:
1(defmacro dbg
2 [expr]
3 `(let [result# ~expr]
4 (println "Debug:" '~expr "=" result#)
5 result#))
The result# symbol becomes a generated unique name, avoiding collisions with caller locals.
| Java habit | Clojure macro discipline |
|---|---|
| Trust generated code because the annotation is familiar. | Inspect expansion because the macro is the generator. |
| Hide boilerplate behind reflection. | Decide whether runtime discovery or compile-time syntax is actually needed. |
| Use templates for repeated source patterns. | Prefer functions first; use macros only for syntax/evaluation shape. |
| Debug generated code after failure. | Expand representative calls before publishing the macro. |
| Check | Good answer |
|---|---|
| Why not a function? | The macro controls evaluation or creates a binding/control form. |
| How many times does each caller expression evaluate? | Exactly the number intended, usually once. |
| Are generated locals safe? | Auto-gensyms or explicit gensyms prevent capture. |
| Is expansion readable? | Yes, it looks like ordinary Clojure a teammate can review. |