Clojure Macros Guide — Macro Writing, Syntax Quoting, and Code Generation
In this tutorial, you will learn about Clojure Macros Guide. We cover key concepts, practical examples, and best practices to help you master this topic.
Clojure macros are compile-time functions that transform code using Clojure's homoiconicity (code is data) -- with defmacro for definition, backtick (`) for template, ~ for unquoting, and ~@ for splicing sequences into the generated code.
Simple Macros
;; Infix macro (turn (2 + 3) into (+ 2 3))
(defmacro infix [[left op right]]
(list op left right))
(infix (2 + 3)) ;; 5
;; Unless macro
(defmacro unless [condition & body]
`(if (not ~condition)
(do ~@body)))
(unless false
(println "This runs"))
Syntax Quoting
;; ` (syntax-quote) - quote with namespace resolution
;; ~ (unquote) - evaluate inside syntax-quote
;; ~@ (splicing) - splice a list into the template
(defmacro my-or [a b]
`(let [or-result ~a]
(if or-result or-result ~b)))
;; Syntax-quote ensures symbol resolution in the macro's namespace
(defmacro time-it [expr]
`(let [start# (System/nanoTime)]
(let [result# ~expr]
(println "Elapsed:" (/ (- (System/nanoTime) start#) 1000000.0) "ms")
result#)))
Macro Utilities
;; Threading macros
(defmacro -> [x & forms]
(loop [result x, forms forms]
(if (empty? forms)
result
(let [form (first forms)]
(if (sequential? form)
(recur (list* (first form) result (rest form)) (rest forms))
(recur (list form result) (rest forms)))))))
;; Same as -> from clojure.core
(-> [1 2 3]
(conj 4)
(map inc)
(filter odd?))
Hygiene
;; Auto-gensym (#) for unique symbols
(defmacro with-temp [bindings & body]
(let [pairs (partition 2 bindings)
vars (map (fn [[sym _]]
(gensym (str (name sym) "__"))) pairs)]
`(let [~@(mapcat (fn [v [sym init]]
[v (or init nil)]) vars pairs)]
(try
(let [~@(mapcat (fn [v [sym _]]
[sym v]) vars pairs)]
~@body)
(finally
~@(map (fn [v [sym _]]
`(close ~v)) vars pairs))))))
Common Mistakes
1. Double evaluation
Evaluating an expression twice: (defmacro bad [x] (list println x x)) calls x twice. Use let to capture.
2. Symbol capture
Unquoted symbols in macros capture variables from the caller's scope. Use gensym or auto-gensym (# suffix).
3. Not using syntax-quote
Manual (list ...) building is error-prone. Use backtick (...) with ~ and ~@ for readable macro templates.
Practice Questions
1. How do you define a macro?
(defmacro name [args] body). The body returns a form that replaces the macro call.
2. What does ~ do in a syntax-quoted form? Unquotes: evaluates the expression and inserts the result into the template.
3. What does ~@ do? Splicing unquote: takes a list and splices its elements into the enclosing form.
FAQ
{{< faq question="Why are macros needed when we have functions?" >}} Macros can control evaluation (short-circuit), manipulate syntax (new control structures), and transform code at compile time. Functions evaluate all arguments first. {{< /faq >}}
{{< faq question="What is macro expansion?" >}}
The Process of replacing macro calls with their expanded forms. (macroexpand '(my-macro args)) shows the expansion.
{{< /faq >}}
{{< faq question="Can macros be recursive?" >}}
Yes, but Recursion happens at compile time. Use macroexpand to verify the expansion is correct.
{{< /faq >}}
What's Next
Now learn about state management in Clojure.
| Topic | Description | Link |
|---|---|---|
| State | Atoms, Refs, and Agents | {{< ref "08-state" >}} |
| Namespaces | Namespaces and requires | {{< ref "09-namespaces" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro