Clojure has a programmatic macro system that lets user code extend the compiler. A macro is a function that receives forms (data) and returns a form that is evaluated in its place. Many core constructs are not primitives but ordinary macros: when expands to if and do, and -> rearranges its arguments.
(macroexpand '(when (pos? a) (println "positive") (/ b a)))
;; => (if (pos? a) (do (println "positive") (/ b a)))
(macroexpand '(-> {} (assoc :a 1) (assoc :b 2)))
;; => (assoc (assoc {} :a 1) :b 2)
Defining and expanding macros
| Name | Role |
|---|---|
(defmacro name doc-string? attr-map? [params*] body) | defines a macro: a function whose var has :macro true. Its parameters are the unevaluated forms and are destructured like function parameters; a multi-arity form is allowed |
&form | available inside defmacro: the actual form being expanded, as data |
&env | available inside defmacro: a map from the symbols of the local bindings at the point of expansion to objects that hold compiler information about them |
definline | defines a function that is also inlined by the compiler |
macroexpand-1, macroexpand | expand a form once; expand until it is no longer a macro call (only the outer form) |
clojure.walk/macroexpand-all | expands every macro in a form |
The quote family
| Written | Reads as | What it does |
|---|---|---|
'form | (quote form) | the form itself, unevaluated |
`form | syntax-quote | a template, see below |
~form | (clojure.core/unquote form) | inside a syntax-quote: evaluate form and put the value here |
~@form | (clojure.core/unquote-splicing form) | inside a syntax-quote: evaluate form to a sequence and put its elements here |
name# | generated symbol | inside a syntax-quote: an unqualified symbol that ends in # becomes one generated symbol with the same name and a unique id added (the reference writes x_123), the same everywhere in that syntax-quote |
Syntax-quote
- For every form other than a symbol, list, vector, set or map,
`xis the same as'x. - A symbol is resolved in the current context to a fully qualified symbol: a var becomes
namespace/nameand a class becomes its full name. A symbol that is not found is qualified with the current namespace. Special forms such asifanddostay as they are. This is what makes a macro robust:`(map f xs)meansclojure.core/mapwherever it is used. - A symbol that is unqualified and ends with
#is an auto-gensym:x#becomes a symbol such asx_123, and everyx#within the same syntax-quote form is the same symbol. - For a list, vector, set or map it makes a template of that data structure. Inside it, forms behave as if recursively syntax-quoted, except those marked
~(replaced by their value) or~@(replaced by the sequence of values).
(def x 5)
(def lst '(a b c))
`(fred x ~x lst ~@lst 7 8 :nine)
;; => (user/fred user/x 5 user/lst a b c 7 8 :nine)
Writing a macro
(defmacro unless [test & body]
`(if ~test nil (do ~@body)))
(defmacro with-timing [label & body]
`(let [start# (System/nanoTime)
result# (do ~@body)]
(println ~label (- (System/nanoTime) start#) "ns")
result#))
~test inserts the form; ~@body splices the body; start# and result# cannot clash with names used by the caller.- Clojure macros are not hygienic by themselves. Syntax-quote's symbol qualification and auto-gensyms protect against most capture. To capture a name on purpose, quote the symbol inside the template:
~'it. A bare unqualified name in a syntax-quote is qualified with a namespace, so it is not the caller's local of that name. - A macro runs at expansion time, in the compiler. It receives forms, not values, and should return a form; avoid doing at expansion time what could be done at run time.
- A function cannot be passed where a macro is expected:
(map when …)is an error becausewhenis a macro.
The macros of clojure.core, by purpose
| Purpose | Names |
|---|---|
| creating macros | defmacro definline (and the functions macroexpand-1 macroexpand) |
| branching | and or when when-not when-let when-first when-some if-let if-not if-some cond condp case |
| looping | for doseq dotimes while |
| working with vars | ns declare defn defn- defmacro defonce defmulti defmethod defprotocol defrecord deftype defstruct definterface with-local-vars |
| arranging code | .. doto -> ->> as-> cond-> cond->> some-> some->> |
| dynamic scopes | binding locking time with-in-str with-out-str with-open with-precision with-redefs with-bindings |
| lazy things | lazy-seq lazy-cat delay future pvalues |
| Java interop | .. amap areduce gen-class gen-interface proxy proxy-super reify memfn |
| documenting | assert comment |
| transactions | dosync io! sync |
| special forms written as macros (for destructuring) | fn let loop (and letfn and case) |
Not described
Known, not yet covered
- Reference: the Clojure guide to writing macros and the macro-writing sections of the Learn Clojure pages were not read;
&envcontents,:inlinemetadata and macros that expand at ClojureScript compile time (:require-macros) are not detailed. - Viewer: a macro the program defines is coloured as a function call unless its name begins
def(and is notdefault,define,deferand similar); the arguments of user macros are read as ordinary code.
Unknown
- Reference: the exact spelling of generated names was not checked against a running Clojure: the reference writes
x_123, and the macros that the runtime itself generates use other forms. Also from knowledge of the language and not from a downloaded page: that macros are not hygienic by themselves, the use of~'nameto capture a name on purpose, and that a macro cannot be passed tomap.
See also
- Lexical structure and the reader – the reader macros
- Common Lisp: standard macros and Racket: macros – other macro systems