Writing Your First Macro in Clojure

Learn a practical first macro workflow in Clojure: choose a problem that truly needs compile-time transformation, write the expansion, inspect it with macroexpand, and keep the generated code boring.

Macro expansion: The process where a macro receives a source form and returns a new form for the compiler to continue processing.

The best first macro is not the cleverest one. It is the smallest one that demonstrates why a function would be insufficient. That usually means:

  • control over evaluation
  • syntax that reads better than nested core forms
  • a declarative wrapper that still expands into plain Clojure

For learning purposes, an unless macro is still a good example even though real code would usually just use when-not.

Start with the Expansion You Want

Before writing defmacro, decide what ordinary form you want the caller’s code to become.

Desired source:

1(unless (pos? balance)
2  (println "Balance is zero or negative"))

Desired expansion:

1(if (not (pos? balance))
2  (do (println "Balance is zero or negative")))

Once the expansion is clear, the macro is mostly a form-construction problem.

Write the Smallest Useful Macro

1(defmacro unless [test & body]
2  `(if (not ~test)
3     (do ~@body)))

Important parts:

  • defmacro defines a macro rather than a function
  • test receives the original test form, not its runtime result
  • & body captures the remaining forms
  • syntax-quote builds the output form
  • ~ inserts one value
  • ~@ splices a sequence of forms into the output

That is enough for a solid first macro.

Inspect the Expansion Immediately

Macro writing gets easier the moment you stop guessing about the emitted code.

1(macroexpand-1
2  '(unless (pos? balance)
3     (println "Balance is zero or negative")))

If the expansion is wrong, fix the macro before thinking about runtime behavior. Macro debugging usually starts at expansion time, not at execution time.

Keep the Emitted Code Boring

One of the healthiest macro habits is to prefer expansions into small core forms:

  • if
  • let
  • do
  • loop
  • function calls

The macro itself can feel powerful, but the output should still be unsurprising. If the expansion looks like a compiler puzzle, the abstraction is probably too ambitious for a first macro.

Evaluate Arguments Exactly as Many Times as Intended

The classic beginner macro bug is evaluating an expression more than once. If the input form has side effects or is expensive, duplicate evaluation changes semantics.

That is why larger macros often introduce temporary bindings using generated symbols. You do not need that in every first example, but you do need the habit of checking for it.

A Macro Is Successful When the Caller Forgets It Is a Macro

The caller should mostly experience:

  • cleaner source code
  • predictable behavior
  • ordinary error boundaries after expansion

If users of the macro constantly need to think about expansion internals, the abstraction has probably leaked too much.

Common Failure Modes

Writing a Macro for a Problem a Function Could Solve

That adds phase complexity without real gain.

Never Inspecting Expansion

Macro bugs are much easier to catch before runtime.

Emitting Overly Clever Code

Generated forms should usually be simpler than the abstraction that produced them.

Forgetting About Repeated Evaluation

One input form may accidentally run multiple times unless the macro binds it carefully.

Practical Heuristics

Choose a tiny problem that genuinely needs syntax transformation, write down the desired expansion first, implement the macro with syntax-quote, and inspect it with macroexpand-1 immediately. Keep the output boring, and be suspicious of any macro that evaluates a form more than once without meaning to. In Clojure, the best first macro is one whose expansion you can explain line by line.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026