ClojureDocs

Nav

Namespaces

fnil

clojure.core

Available since 1.2 (source)
  • (fnil f x)
  • (fnil f x y)
  • (fnil f x y z)
Takes a function f, and returns a function that calls f, replacing
a nil first argument to f with the supplied value x. Higher arity
versions can replace arguments in the second and third
positions (y, z). Note that the function f can take any number of
arguments, not just the one(s) being nil-patched.
3 Examples
;; a function that expects a non-nil value
(defn say-hello [name] (str "Hello " name))
;;=> #'user/say-hello

;; fnil lets you create another function with a default
;; arg in case it is passed a nil
(def say-hello-with-defaults (fnil say-hello "World"))
;;=> #'user/say-hello-with-defaults

;; the happy path works as you would expect
(say-hello-with-defaults "Sir")
;;=> "Hello Sir"

;; but in the case that the function is passed a nil it will use the 
;; default supplied to fnil
(say-hello-with-defaults nil)
;;=> "Hello World"

;; this works with different arities too
(defn say-hello [first other] (str "Hello " first " and " other))
;;=> #'user/say-hello

;; lets create it with defaults
(def say-hello-with-defaults (fnil say-hello "World" "People"))
;;=> #'user/say-hello-with-defaults

;; call the function with all nil args - notice it uses the defaults
;; supplied to fnil
(say-hello-with-defaults nil nil)
;;=> "Hello World and People"

;; any of the args can be nil - the function will supply 
;; the default supplied with fnil
(say-hello-with-defaults "Sir" nil)
;;=> "Hello Sir and People"

;; and again - notice that "World" is the default here
(say-hello-with-defaults nil "Ma'am")
;;=> "Hello World and Ma'am"

;; or pass all args 
(say-hello-with-defaults "Sir" "Ma'am")
;;=> "Hello Sir and Ma'am"
;; Treat nil as 0 for the purposes of incrementing
((fnil inc 0) nil)
;;=> 1
;; While the following would not work:
(inc nil)
;;=> NullPointerException   clojure.lang.Numbers.ops (Numbers.java:961)

;; fnil is very useful for specifying default values when updating maps
;; For a map containing counters of keys:
(update-in {:a 1} [:a] inc)
;;=> {:a 2}

;; Oops, our map does not have a key :b and update-in passes nil to inc
(update-in {:a 1} [:b] inc)
;;=> NullPointerException   clojure.lang.Numbers.ops (Numbers.java:961)

;; But if we use fnil it works:
(update-in {:a 1} [:b] (fnil inc 0))
;;=> {:b 1, :a 1}

;; Another example is when map values are collections and we don't want
;; default behavior of conj with nil that produces a list
(conj nil 1)
;;=> (1)
;; I.e.
(update-in {} [:a] conj 1)
;;=> {:a (1)}

;; But say we want map values to be vectors instead:
(update-in {} [:a] (fnil conj []) 1)
;;=> {:a [1]}
;;; Note that `fnil` still requires the appropriate number of arguments for an
;;; arity (i.e. no "implicit" default)

(def tempids {-1 1
              -2 2
              -3 3})

(def id (fnil get nil -1))

;; You may expect `fnil` to implicitly add -1 as the last argument, but that's
;; not true.
(id tempids)
;;=> ArityException: Wrong number of args (1) passed to: clojure.core/fnil/fn--...

;; But it will here, since the argument is there (as `nil`).
(id tempids nil)
;;=> 1

;; Of course, you can supply it explicitly.
(id tempids -1)
;;=> 1
See Also

Takes a function f and fewer than the normal arguments to f, and returns a fn that takes a variabl...

Added by MicahElliott

Takes a set of functions and returns a fn that is the composition of those fns. The returned fn t...

Added by MicahElliott

Takes a set of functions and returns a fn that is the juxtaposition of those fns. The returned fn...

Added by MicahElliott

Evaluates exprs one at a time, from left to right. If a form returns a logical true value, or retu...

Added by MicahElliott

Takes a set of predicates and returns a function f that returns the first logical true value retur...

Added by MicahElliott
0 Notes
No notes for fnil