Control Flow
if and when
if takes a condition, a then-branch, and an optional else-branch:
(if (> x 0) "positive" "non-positive")
; Without else — returns nil when condition is false
(if (> x 0) (println "positive"))
when is if without an else, but accepts multiple body expressions:
(when (> x 0)
(println "x is positive")
(println "x =" x))
cond
cond tests multiple conditions in order, returning the first truthy match:
(defn grade [score int] -> string
(cond
(>= score 90) "A"
(>= score 80) "B"
(>= score 70) "C"
(>= score 60) "D"
:else "F"))
:else is a common idiom for the default case — it's just a keyword, which is always truthy.
switch and case
switch dispatches on equality, like Go's switch:
(defn http-status [code int] -> string
(switch code
200 "OK"
201 "Created"
404 "Not Found"
500 "Internal Server Error"
:default "Unknown"))
case is the Clojure-style variant where the default is a trailing unpaired expression:
(defn day-type [day string] -> string
(case day
"Saturday" "weekend"
"Sunday" "weekend"
"weekday")) ; trailing default
Both compile to Go's switch statement.
Conditional Binding
if-let evaluates an expression, binds the result, and branches based on truthiness:
(defn find-user [id string] -> string
(if-let [user (lookup-user id)]
(str "Found: " (:name user))
"Not found"))
The else branch runs when the binding is nil or false.
when-let is the same but with no else — just runs the body when the binding is truthy:
(when-let [session (get-session req)]
(log! "session user:" (:user-id session)))
do
do sequences multiple expressions, returning the last value:
(if error
(do
(log/error "something went wrong" "err" error)
(sys/exit 1))
(println "ok"))
Loops
doseq iterates a collection for side effects:
(doseq [x [1 2 3 4 5]]
(println x))
dotimes runs a body n times with an index:
(dotimes [i 5]
(println "iteration" i))
For loops that produce values or need recursion, see Functions — loop/recur.