As with all Lisps, Clojure’s inherent homoiconicity gives you access to the full extent of the language to write code-generation routines called «macros». Macros provide a powerful way to tailor the language to your needs.
Be careful though. It’s considered bad form to write a macro when a function will do. Use a macro only when you need control over when or if the arguments to a form will be evaluated.
You’ll want to be familiar with Clojure. Make sure you understand everything in Clojure in Y Minutes.
1;; Define a macro using defmacro. Your macro should output a list that can
2;; be evaluated as clojure code.
3;;
4;; This macro is the same as if you wrote (reverse "Hello World")
5(defmacro my-first-macro []
6 (list reverse "Hello World"))
7
8;; Inspect the result of a macro using macroexpand or macroexpand-1.
9;;
10;; Note that the call must be quoted.
11(macroexpand '(my-first-macro))
12;; -> (#<core$reverse clojure.core$reverse@xxxxxxxx> "Hello World")
13
14;; You can eval the result of macroexpand directly:
15(eval (macroexpand '(my-first-macro)))
16; -> (\d \l \o \r \W \space \o \l \l \e \H)
17
18;; But you should use this more succinct, function-like syntax:
19(my-first-macro) ; -> (\d \l \o \r \W \space \o \l \l \e \H)
20
21;; You can make things easier on yourself by using the more succinct quote syntax
22;; to create lists in your macros:
23(defmacro my-first-quoted-macro []
24 '(reverse "Hello World"))
25
26(macroexpand '(my-first-quoted-macro))
27;; -> (reverse "Hello World")
28;; Notice that reverse is no longer function object, but a symbol.
29
30;; Macros can take arguments.
31(defmacro inc2 [arg]
32 (list + 2 arg))
33
34(inc2 2) ; -> 4
35
36;; But, if you try to do this with a quoted list, you'll get an error, because
37;; the argument will be quoted too. To get around this, clojure provides a
38;; way of quoting macros: `. Inside `, you can use ~ to get at the outer scope
39(defmacro inc2-quoted [arg]
40 `(+ 2 ~arg))
41
42(inc2-quoted 2)
43
44;; You can use the usual destructuring args. Expand list variables using ~@
45(defmacro unless [arg & body]
46 `(if (not ~arg)
47 (do ~@body))) ; Remember the do!
48
49(macroexpand '(unless true (reverse "Hello World")))
50;; ->
51;; (if (clojure.core/not true) (do (reverse "Hello World")))
52
53;; (unless) evaluates and returns its body if the first argument is false.
54;; Otherwise, it returns nil
55
56(unless true "Hello") ; -> nil
57(unless false "Hello") ; -> "Hello"
58
59;; Used without care, macros can do great evil by clobbering your vars
60(defmacro define-x []
61 '(do
62 (def x 2)
63 (list x)))
64
65(def x 4)
66(define-x) ; -> (2)
67(list x) ; -> (2)
68
69;; To avoid this, use gensym to get a unique identifier
70(gensym 'x) ; -> x1281 (or some such thing)
71
72(defmacro define-x-safely []
73 (let [sym (gensym 'x)]
74 `(do
75 (def ~sym 2)
76 (list ~sym))))
77
78(def x 4)
79(define-x-safely) ; -> (2)
80(list x) ; -> (4)
81
82;; You can use # within ` to produce a gensym for each symbol automatically
83(defmacro define-x-hygienically []
84 `(do
85 (def x# 2)
86 (list x#)))
87
88(def x 4)
89(define-x-hygienically) ; -> (2)
90(list x) ; -> (4)
91
92;; It's typical to use helper functions with macros. Let's create a few to
93;; help us support a (dumb) inline arithmetic syntax
94(declare inline-2-helper)
95(defn clean-arg [arg]
96 (if (seq? arg)
97 (inline-2-helper arg)
98 arg))
99
100(defn apply-arg
101 "Given args [x (+ y)], return (+ x y)"
102 [val [op arg]]
103 (list op val (clean-arg arg)))
104
105(defn inline-2-helper
106 [[arg1 & ops-and-args]]
107 (let [ops (partition 2 ops-and-args)]
108 (reduce apply-arg (clean-arg arg1) ops)))
109
110;; We can test it immediately, without creating a macro
111(inline-2-helper '(a + (b - 2) - (c * 5))) ; -> (- (+ a (- b 2)) (* c 5))
112
113; However, we'll need to make it a macro if we want it to be run at compile time
114(defmacro inline-2 [form]
115 (inline-2-helper form))
116
117(macroexpand '(inline-2 (1 + (3 / 2) - (1 / 2) + 1)))
118; -> (+ (- (+ 1 (/ 3 2)) (/ 1 2)) 1)
119
120(inline-2 (1 + (3 / 2) - (1 / 2) + 1))
121; -> 3 (actually, 3N, since the number got cast to a rational fraction with /)