summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/clj/clojure/core.clj23
-rw-r--r--test/clojure/test_clojure/other_functions.clj11
2 files changed, 34 insertions, 0 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 017b0b70..22357b69 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -5669,6 +5669,29 @@
(cons x (keepi (inc idx) (rest s)))))))))]
(keepi 0 coll))))
+(defn fnil
+ "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."
+ ([f x]
+ (fn
+ ([a] (f (if (nil? a) x a)))
+ ([a b] (f (if (nil? a) x a) b))
+ ([a b c] (f (if (nil? a) x a) b c))
+ ([a b c & ds] (apply f (if (nil? a) x a) b c ds))))
+ ([f x y]
+ (fn
+ ([a b] (f (if (nil? a) x a) (if (nil? b) y b)))
+ ([a b c] (f (if (nil? a) x a) (if (nil? b) y b) c))
+ ([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) c ds))))
+ ([f x y z]
+ (fn
+ ([a b] (f (if (nil? a) x a) (if (nil? b) y b)))
+ ([a b c] (f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c)))
+ ([a b c & ds] (apply f (if (nil? a) x a) (if (nil? b) y b) (if (nil? c) z c) ds)))))
+
(defn- ^{:dynamic true} assert-valid-fdecl
"A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn."
[fdecl]
diff --git a/test/clojure/test_clojure/other_functions.clj b/test/clojure/test_clojure/other_functions.clj
index 575ac9fb..afb67631 100644
--- a/test/clojure/test_clojure/other_functions.clj
+++ b/test/clojure/test_clojure/other_functions.clj
@@ -50,6 +50,17 @@
"bar" 'bar
"quux" "quux"))
+(deftest test-fnil
+ (are [x y] (= x y)
+ ((fnil + 0) nil 42) 42
+ ((fnil conj []) nil 42) [42]
+ (reduce #(update-in %1 [%2] (fnil inc 0)) {}
+ ["fun" "counting" "words" "fun"])
+ {"words" 1, "counting" 1, "fun" 2}
+ (reduce #(update-in %1 [(first %2)] (fnil conj []) (second %2)) {}
+ [[:a 1] [:a 2] [:b 3]])
+ {:b [3], :a [1 2]}))
+
; time assert comment doc
; partial