Clojure Macros Cheat Sheet
Covers defmacro, quote/unquote/splice, gensym for hygiene, macroexpand for debugging, and common macro-writing patterns.
Defining a Basic Macro
Macros receive unevaluated code (forms) and must return code to be evaluated.
(defmacro unless [test then else] `(if (not ~test) ~then ~else))(unless false (println "runs") (println "does not run"));; expands to:;; (if (not false) (println "runs") (println "does not run"))
Quote, Unquote & Splice
`` ` `` (syntax-quote), `~` (unquote), and `~@` (unquote-splice) are the core tools for building code templates.
(def x 10)'(1 2 x) ;; => (1 2 x) — quote: no evaluation`(1 2 ~x) ;; => (1 2 10) — syntax-quote + unquote evaluates x`(1 2 ~@[3 4 5]) ;; => (1 2 3 4 5) — splices a sequence into the list(defmacro my-and ([] true) ([x] x) ([x & rest] `(if ~x (my-and ~@rest) ~x)))
Hygiene with `gensym`/`#`
Avoid variable capture by generating unique symbol names inside syntax-quote.
;; Auto-gensym: any symbol ending in # inside `...` gets a unique suffix(defmacro my-or [a b] `(let [val# ~a] (if val# val# ~b)))(my-or false 42) ;; => 42, and `val#` can't collide with a caller's `val`;; Manual gensym for more control(defmacro my-swap! [a b] (let [tmp (gensym "tmp")] `(let [~tmp ~a] (reset! ~a ~b) (reset! ~b ~tmp))))
Debugging with `macroexpand`
Always inspect what your macro actually generates before trusting it.
(macroexpand-1 '(unless false (println "a") (println "b")));; => (if (clojure.core/not false) (println "a") (println "b"))(macroexpand '(my-or false 42));; fully expands nested macros too, useful for threading/-> style macros;; clojure.walk/macroexpand-all expands everything recursively(require '[clojure.walk :as walk])(walk/macroexpand-all '(my-or false (my-or nil 1)))
Macro-Writing Toolkit
The core forms/functions you'll reach for.
- defmacro- defines a macro; body returns a form (code), not a value
- ` (syntax-quote)- quotes a form while fully-qualifying symbols and enabling ~/~@
- ~ (unquote)- evaluates an expression inside a syntax-quoted form
- ~@ (unquote-splice)- splices a sequence's elements into the surrounding form
- gensym / symbol#- produces a unique symbol to avoid variable capture (hygiene)
- macroexpand-1 / macroexpand- shows the expansion, essential for debugging macros
- &form / &env- implicit args available in defmacro bodies for advanced metaprogramming
Write the macro's desired expansion as plain code first, get it working, then wrap it in `defmacro` and syntax-quote it — trying to write macro-generating logic and the target logic simultaneously is the most common source of Clojure macro bugs.
Explore Topics
Professional Web Designing Services
- Responsive Websites
- E-commerce Solutions
- SEO Friendly Design
- Fast & Secure
- Support & Maintenance