Clojure
Share your thoughts in the 2024 State of Clojure Survey!

Macros

Table of Contents

Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.

Some macros produce simple combinations of primitive forms. For example, when combines if and do:

user=> (macroexpand '(when (pos? a) (println "positive") (/ b a)))
(if (pos? a) (do (println "positive") (/ b a)))

Other macros re-arrange forms in useful ways, like the -> macro, which recursively inserts each expression as the first argument of the next expression:

user=> (-> {} (assoc :a 1) (assoc :b 2))
{:b 2, :a 1}
user=> (macroexpand '(-> {} (assoc :a 1) (assoc :b 2)))
(assoc (assoc {} :a 1) :b 2)

Special variables

Two special variables are available inside defmacro for more advanced usages:

  • &form - the actual form (as data) that is being invoked

  • &env - a map of local bindings at the point of macro expansion. The env map is from symbols to objects holding compiler information about that binding.

All of the following macros are documented on the API page. Many are also discussed on topic pages as noted:

A few special forms are actually implemented as macros, primarily to provide destructuring: fn let loop