Browse Learn Clojure Foundations as a Java Developer

Define Clojure Macros with defmacro

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.

The Shape of defmacro

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.

Expand Before Trusting It

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.

Macro Arguments Are Forms

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.

Avoid Multiple Evaluation

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-To-Clojure Translation

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.

Review Checklist

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.

Knowledge Check

### What does `defmacro` define? - [x] A source transformation that returns Clojure code. - [ ] A faster version of `defn`. - [ ] A Java annotation processor. - [ ] A runtime reflection helper. > **Explanation:** A macro runs during expansion and returns a form. The returned form becomes the code Clojure compiles or evaluates. ### Why is `bad-debug` unsafe? - [x] It splices the caller expression into the expansion twice. - [ ] It uses `println`. - [ ] It has no docstring. - [ ] It returns a keyword. > **Explanation:** Repeating `~expr` can evaluate the caller's expression twice. Binding the result once avoids duplicated side effects and work. ### What should you inspect before relying on a macro? - [x] The generated form from `macroexpand-1`. - [ ] Only the macro's name. - [ ] JVM heap size. - [ ] The number of files in the namespace. > **Explanation:** Expansion review shows the actual code shape. It is the macro equivalent of reviewing generated source.
Revised on Saturday, May 23, 2026