ClojureDocs

Nav

Namespaces

if-let

clojure.core

Available since 1.0 (source)
  • (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
14 Examples
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 [shooter (who-shot-jr 5)]
         shooter
	 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

;; Note that this implementation short-curcuits on false so that the prn form 
;; is _not_ evaluated in the below form.
(if-let* [a false
          b (prn "Do we evaluate this?")]
  true
  false)
;;=> false
;; (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
(if-let [value true]
  "The expression is true!"
  "Sorry, it's not true.")
;; Destructuring maps
(def x {:it 1 :that 2})
(def y {:that 2}
(if-let [{value :it} x] value "Not found") ;;=> 1
(if-let [{value :it} nil] value "Not found") ;;=> "Not found"
(if-let [{value :it} false] value "Not found") ;;=> "Not found"
(if-let [{value :it} y] value "Not found") ;;=> nil
(if-let [{value :it} {}] value "Not found") ;;=> nil
(if-let [{value :it} 1] value "Not found") ;;=> nil
;; if-let multiple bindings version
(defmacro if-let*
  ([bindings-vec then] `(if-let* ~bindings-vec ~then nil))
  ([bindings-vec then else]
   (if (seq bindings-vec)
     `(let ~bindings-vec
        (if (and ~@(take-nth 2 bindings-vec))
          ~then
          ~else)))))


(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 

;; Note that this implementation does _not_ short-circuit on falsey forms.
(if-let* [a false
           b (prn "Do we evaluate this?")]
  true
  false)
;; "Do we evaluate this?"
;;=> false
;; Check if the value is true print it otherwise not.

(def log-output "") 

;; Assume log-output could be the result of another operation like
;; (def log-output (Gl/ShaderInfoLog shader))
;; The Following will only print, when logout is not empty.

(if-let [out (not-empty logout)]
  (println out))
;; fun of if-let 
(defn some-condition
  [data]
  true)

(let [result (some-condition "ABC")]
  (if (true? result)
    "Success"
    "Failure"))
;; => "success"

;; if-let in Action
(if-let [result (some-condition "ABC")]
  "Success"
  "Failure")
;; => "success"
See Also

bindings => binding-form test When test is true, evaluates body with binding-form bound to the va...

Added by 0x89

Evaluates test.

Added by srid

bindings => binding-form test If test is not nil, evaluates then with binding-form bound to the ...

Added by brunchboy
2 Notes
    By , created 14.1 years ago

    The difference between when-let and if-let is that when-let doesn't have an else clause and and also accepts multiple forms so you don't need to use a (do...).

    By , created 13.3 years ago

    I wonder what motivates the restriction of only one binding, e.g. many Schemes implement an and-let* form which allows multiple bindings, evaluating them in order and breaking out on the first binding that evaluates to false. Can somebody shed some light on this?