(if-let bindings then) (if-let bindings then else & oldform)
bindings => binding-form test If test is true, evaluates then with binding-form bound to the value of test, if not, yields else
user=> (defn sum-even-numbers [nums]
(if-let [nums (seq (filter even? nums))]
(reduce + nums)
"No even numbers found."))
#'user/sum-even-numbers
user=> (sum-even-numbers [1 3 5 7 9])
"No even numbers found."
user=> (sum-even-numbers [1 3 5 7 9 10 12])
22
(if-let [x false y true]
"then"
"else")
;; java.lang.IllegalArgumentException: if-let requires exactly 2 forms in binding vector (NO_SOURCE_FILE:1)
;; see if-let* below
(defn if-let-demo [arg]
(if-let [x arg]
"then"
"else"))
(if-let-demo 1) ; anything except nil/false
;;=> "then"
(if-let-demo nil)
;;=> "else"
(if-let-demo false)
;;=> "else"
;; This macro is nice when you need to calculate something big. And you need
;; to use the result but only when it's true:
(if-let [life (meaning-of-life 12)]
life
(if-let [origin (origin-of-life 1)]
origin
(if-let [shot (who-shot-jr 5)]
block-sol
\t 42)))
;; As you can see in the above example it will return the answer
;; to the question only if the answer is not nil. If the answer
;; is nil it will move to the next question. Until finally it
;; gives up and returns 42.
;; See examples for "if" explaining Clojure's idea of logical true
;; and logical false.
;;; with destructuring binding
;; successful case
(if-let [[w n] (re-find #"a(\\d+)x" "aaa123xxx")]
[w n]
:not-found) ;=> ["a123x" "123"]
;; unsuccessful case
(if-let [[w n] (re-find #"a(\\d+)x" "bbb123yyy")]
[w n]
:not-found) ;=> :not-found
;; same as above
(if-let [[w n] nil]
[w n]
:not-found) ;=> :not-found
;; on Map
(if-let [{:keys [a b]} nil]
[a b]
:not-found) ;=> :not-found
;; Note that the binding only extends to the then form, not to the else:
user=> (if-let [x nil] "then" x)
CompilerException java.lang.RuntimeException: Unable to resolve symbol:
x in this context, compiling: ...
;; Works well with collections
=> (def x {:whatever 1})
=> (if-let [value (:whatever x)] value "Not found")
1
=> (if-let [value (:no-match x)] value "Not found")
"Not found"
;; if-let multiple bindings version
;; Edited: Else branch did not work with expressions.
(defmacro if-let*
([bindings then]
`(if-let* ~bindings ~then nil))
([bindings then else]
(if (seq bindings)
`(if-let [~(first bindings) ~(second bindings)]
(if-let* ~(drop 2 bindings) ~then ~else)
~else)
then)))
(if-let* [a 1
b (+ a 1) ]
b)
;;=> 2
(if-let* [a 1
b (+ a 1)
c false] ;;false or nil - does not matter
b
a)
;;=> 1
;; (if-let [definition condition] then else):
;; if the value of condition is truthy, then that value is assigned to the definition,
;; and "then" is evaluated.
;; Otherwise the value is NOT assigned to the definition, and "else" is evaluated.
;; Although you can use this structure with booleans,
;; there's not much point unless you only want to
;; use the resulting boolean if it's true - as evidenced in the first example below.
;; if-let is mostly useful when checking for nil.
;; In this first example if Clare is old, it outputs "Clare is old".
;; (the let part of the statement is rather pointless,
;; as the definition old-clare-age is never used).
(def clare-age 47)
(if-let [old-clare-age (> clare-age 100)]
"Clare is old"
"Clare is not old")
;;=> Clare is not old
;; In the next two examples, it only outputs Clare's age if it is valid (ie not nil)
(def clare-age nil)
(if-let [valid-clare-age clare-age]
(str "Clare has a valid age: " valid-clare-age)
"Clare's age is invalid")
;;=> Clare's age is invalid
(def clare-age 47)
(if-let [valid-clare-age clare-age]
(str "Clare has a valid age: " valid-clare-age)
"Clare's age is invalid")
;;=> Clare has a valid age: 47