0
# Control Flow and Macros
1
2
Advanced control structures, conditional execution, and macro system for metaprogramming. Clojure provides both special forms and macros for controlling program flow.
3
4
## Capabilities
5
6
### Conditional Execution
7
8
Control structures for conditional program execution.
9
10
```clojure { .api }
11
(if test then else?)
12
;; Special form: evaluates test, returns then if truthy, else if falsy
13
;; else is optional (defaults to nil)
14
;; Returns: Object
15
16
(if-not test then else?)
17
;; Macro: inverted conditional, returns then if test is falsy
18
;; Returns: Object
19
20
(when test & body)
21
;; Macro: executes body when test is truthy
22
;; Returns: result of last body expression or nil
23
24
(when-not test & body)
25
;; Macro: executes body when test is falsy
26
;; Returns: result of last body expression or nil
27
28
(when-let bindings & body)
29
;; Macro: binds and executes body if binding value is truthy
30
;; bindings: [symbol test-expr]
31
;; Returns: result of last body expression or nil
32
33
(when-first bindings & body)
34
;; Macro: binds first item of sequence and executes body
35
;; bindings: [symbol sequence-expr]
36
;; Returns: result of last body expression or nil
37
38
(cond & clauses)
39
;; Macro: multi-branch conditional
40
;; clauses: test1 expr1 test2 expr2 ... :else default-expr
41
;; Returns: result of first matching expression
42
43
(condp pred expr & clauses)
44
;; Macro: conditional with predicate applied to expr
45
;; clauses: test-val result-expr test-val result-expr ... default-expr?
46
;; Returns: result expression for first (pred test-val expr) that's truthy
47
48
(case expr & clauses)
49
;; Macro: constant-time dispatch on expr value
50
;; clauses: const1 result1 const2 result2 ... default-expr?
51
;; Returns: result for matching constant
52
```
53
54
### Loops and Iteration
55
56
Constructs for repetitive execution and iteration.
57
58
```clojure { .api }
59
(loop [bindings*] & body)
60
;; Special form: establishes recursion point with local bindings
61
;; bindings: [symbol init-expr symbol init-expr ...]
62
;; Use recur to loop back with new values
63
;; Returns: result of last body expression
64
65
(recur & exprs)
66
;; Special form: recursively calls enclosing fn or loop
67
;; Must be in tail position
68
;; exprs become new parameter/binding values
69
;; Returns: never returns (transfers control)
70
71
(while test & body)
72
;; Macro: executes body repeatedly while test is truthy
73
;; Returns: nil
74
75
(dotimes [name n] & body)
76
;; Macro: executes body n times with name bound to 0, 1, ..., n-1
77
;; Returns: nil
78
79
(doseq [seq-exprs*] & body)
80
;; Macro: executes body for each item in sequences
81
;; seq-exprs: [name coll name coll :let [binding] :when test :while test]
82
;; Returns: nil
83
84
(for [seq-exprs*] body-expr)
85
;; Macro: list comprehension returning lazy sequence
86
;; seq-exprs: same as doseq but returns sequence of body-expr results
87
;; Returns: clojure.lang.LazySeq
88
```
89
90
### Exception Handling
91
92
Constructs for handling exceptions and errors.
93
94
```clojure { .api }
95
(try expr* catch* finally?)
96
;; Special form: exception handling
97
;; catch: (catch ExceptionClass binding-symbol catch-expr)
98
;; finally: (finally finally-expr)
99
;; Returns: result of expr or catch-expr
100
101
(throw expr)
102
;; Special form: throws exception
103
;; expr should evaluate to Throwable
104
;; Returns: never returns
105
106
(assert expr)
107
(assert expr message)
108
;; Macro: throws AssertionError if expr is falsy
109
;; Returns: true if assertion passes
110
111
(with-open [bindings*] & body)
112
;; Macro: ensures resources are closed after use
113
;; bindings: [name init-expr name init-expr ...]
114
;; Resources must implement java.io.Closeable
115
;; Returns: result of last body expression
116
```
117
118
### Threading Macros
119
120
Macros for readable function composition and data transformation pipelines.
121
122
```clojure { .api }
123
(-> x & forms)
124
;; Macro: thread-first - inserts x as first argument to each form
125
;; (-> x (f a) (g b)) expands to (g (f x a) b)
126
;; Returns: result of final form
127
128
(->> x & forms)
129
;; Macro: thread-last - inserts x as last argument to each form
130
;; (->> x (f a) (g b)) expands to (g b (f a x))
131
;; Returns: result of final form
132
133
(as-> expr name & forms)
134
;; Macro: thread with explicit binding - binds result to name in each form
135
;; (as-> x $ (f $ y) (g z $)) where $ is explicitly positioned
136
;; Returns: result of final form
137
138
(some-> expr & forms)
139
;; Macro: thread-first with nil safety - stops if any form returns nil
140
;; Returns: result of final form or nil
141
142
(some->> expr & forms)
143
;; Macro: thread-last with nil safety - stops if any form returns nil
144
;; Returns: result of final form or nil
145
146
(cond-> expr & clauses)
147
;; Macro: conditional threading - only applies forms when test is truthy
148
;; clauses: test1 form1 test2 form2 ...
149
;; Returns: final transformed expr
150
151
(cond->> expr & clauses)
152
;; Macro: conditional thread-last
153
;; Returns: final transformed expr
154
```
155
156
### Macro Definition and Usage
157
158
Tools for creating and working with macros.
159
160
```clojure { .api }
161
(defmacro name doc-string? attr-map? [params*] body)
162
;; Special form: defines a macro
163
;; Macro receives unevaluated arguments and returns code to execute
164
;; Returns: clojure.lang.Var
165
166
(macroexpand form)
167
;; Function: recursively expands all macros in form
168
;; Returns: expanded form
169
170
(macroexpand-1 form)
171
;; Function: expands outermost macro in form once
172
;; Returns: expanded form or original if no macro
173
174
(quote form)
175
;; Special form: returns form without evaluation (same as 'form)
176
;; Returns: unevaluated form
177
178
(unquote form)
179
;; Special form: evaluates form within syntax-quote (same as ~form)
180
;; Only valid within syntax-quote
181
;; Returns: evaluated form
182
183
(unquote-splicing form)
184
;; Special form: splices sequence into containing form (same as ~@form)
185
;; Only valid within syntax-quote
186
;; Returns: spliced sequence
187
188
;; Syntax-quote (backtick `) and unquote
189
`(form ~expr ~@seq-expr) ; Template with unquoting
190
'(form expr seq-expr) ; Simple quote - no unquoting
191
```
192
193
### Evaluation and Compilation
194
195
Functions for runtime evaluation and code generation.
196
197
```clojure { .api }
198
(eval form)
199
;; Function: evaluates form in current namespace context
200
;; Returns: result of evaluation
201
202
(load-string s)
203
;; Function: loads and evaluates Clojure code from string
204
;; Returns: result of last expression
205
206
(read-string s)
207
;; Function: reads one object from string (doesn't evaluate)
208
;; Returns: parsed object
209
210
(compile namespace-symbol)
211
;; Function: compiles namespace to class files
212
;; Returns: namespace symbol
213
```
214
215
**Usage Examples:**
216
217
```clojure
218
;; Conditional execution
219
(if (> 5 3) "yes" "no") ; => "yes"
220
(when (even? 4) "four is even") ; => "four is even"
221
(cond
222
(< 5 3) "less"
223
(> 5 3) "greater"
224
:else "equal") ; => "greater"
225
226
;; Loops
227
(loop [i 0 acc []]
228
(if (< i 3)
229
(recur (inc i) (conj acc i))
230
acc)) ; => [0 1 2]
231
232
(dotimes [i 3] (println i)) ; Prints 0, 1, 2
233
234
(doseq [x [1 2 3]] (println (* x x))) ; Prints 1, 4, 9
235
236
(for [x [1 2 3] y [10 20]]
237
(+ x y)) ; => (11 21 12 22 13 23)
238
239
;; Threading macros
240
(-> 5
241
(+ 3)
242
(* 2)) ; => 16, equivalent to (* (+ 5 3) 2)
243
244
(->> [1 2 3 4]
245
(map inc)
246
(filter even?)) ; => (2 4), equivalent to (filter even? (map inc [1 2 3 4]))
247
248
;; Exception handling
249
(try
250
(/ 1 0)
251
(catch ArithmeticException e
252
"Cannot divide by zero")) ; => "Cannot divide by zero"
253
254
;; Macro definition
255
(defmacro unless [test & body]
256
`(when (not ~test)
257
~@body))
258
259
(unless false (println "This prints")) ; Prints "This prints"
260
261
;; Macro expansion
262
(macroexpand '(when true (println "hi")))
263
; => (if true (do (println "hi")))
264
265
;; Evaluation
266
(eval '(+ 1 2 3)) ; => 6
267
(read-string "(+ 1 2)") ; => (+ 1 2) [not evaluated]
268
(load-string "(+ 1 2)") ; => 3 [evaluated]
269
```