Learn how quote, syntax quote, unquote, unquote-splicing, and auto-gensyms work together to build readable Clojure macro templates without variable capture.
Quoting controls what is treated as code and what is treated as data. In macro writing, this is the core skill: build a code template, then insert caller forms only where they belong.
For Java engineers, think of syntax quote as a safer source-template mechanism. It builds code, resolves many symbols, and lets you explicitly mark holes with unquote.
| Tool | Meaning |
|---|---|
'form |
Quote the form as data without evaluating it. |
`form |
Syntax-quote the form, usually for macro templates. |
~form |
Unquote one form inside syntax quote. |
~@forms |
Splice a sequence of forms into the surrounding form. |
Use regular quote when you want literal data. Use syntax quote when you are generating code.
1'(+ 1 2)
2;; => (+ 1 2)
3
4(+ 1 2)
5;; => 3
The first form is a list. The second form is a function call. Macros work because Clojure can represent source as ordinary data structures.
1`(println "hello")
2;; => (clojure.core/println "hello")
Syntax quote resolves println to clojure.core/println, which makes generated code less dependent on the caller’s local namespace.
1(defmacro log-value
2 [label expr]
3 `(let [value# ~expr]
4 (println ~label value#)
5 value#))
~expr inserts the caller’s expression into the generated let. ~label inserts the label expression. value# becomes a generated local name.
Expansion makes the template visible:
1(macroexpand-1
2 '(log-value "total:" (+ 1 2)))
3
4;; roughly:
5(let [value__auto__ (+ 1 2)]
6 (clojure.core/println "total:" value__auto__)
7 value__auto__)
Use ~@ when the caller passes a body of multiple forms:
1(defmacro do-with-message
2 [message & body]
3 `(do
4 (println ~message)
5 ~@body))
Without splicing, the body forms would be inserted as one list-like value rather than several forms in sequence.
| Mistake | Safer habit |
|---|---|
| Using regular quote for generated code | Use syntax quote so core symbols resolve predictably. |
Forgetting ~ |
The template contains a literal symbol instead of the caller form. |
Using ~ where ~@ is needed |
Multiple body forms are inserted as one collection-like form. |
| Naming generated locals manually | Use name# or gensym to avoid caller collisions. |
| Expanding only in your head | Use macroexpand-1 at the REPL. |
Before shipping a macro template, ask:
| Check | Why it matters |
|---|---|
| Which parts are fixed template? | Fixed code should be readable ordinary Clojure. |
| Which parts come from the caller? | Caller forms should be inserted exactly where intended. |
| Are body forms spliced? | Multiple forms need ~@ inside a surrounding do or similar form. |
| Are generated locals unique? | Auto-gensyms prevent accidental variable capture. |