From the documentation: The fnil function takes a function f, and returns a function that calls f, replacing a nil first argument to f with a supplied value.
A simple example is working with + and nil.
user=> (+ nil 1)As you can see, the + function throws an exception if an argument is nil; however, we were easily able to create our own new+ function that handles the first argument being nil.
java.lang.NullPointerException (NO_SOURCE_FILE:0)
user=> (def new+ (fnil + 0))
#'user/new+
user=> (new+ nil 1)
1
In isolation, it might be hard to see how this is valuable. However, once combined with high order functions it's easy to see the benefit.
Several months ago I wrote about composing functions and used the update-in function as my final example of what I thought was the best implementation. What I didn't address in the blog entry was how to handle the first update.
As you can see from the following code, the first update will fail if a default value isn't populated.
user=> (def current-score {})At the time of the writing Clojure 1.2 was not production ready, and I used a definition of update-score that was much more verbose, but did handle nil.
#'user/current-score
user=> (defn update-score [current {:keys [country player score]}]
(update-in current [country player] + score))
#'user/update-score
user=> (update-score current-score {:player "Paul Casey" :country :England :score -1})
java.lang.NullPointerException (NO_SOURCE_FILE:0)
user=> (defn update-score [current {:keys [country player score]}]While the above code works perfectly well, it's obviously not nearly as nice to read as the example that doesn't need to concern itself with nil.
(update-in current [country player] #(+ (or %1 0) score)))
#'user/update-score
user=> (update-score current-score {:player "Paul Casey" :country :England :score -1})
{:England {"Paul Casey" -1}}
However, Clojure 1.2 is now production ready and the fnil function is available. As a result, you can now write the following version of the update-score function that is obviously preferable to the version that uses the or function.
user=> (defn update-score [current {:keys [country player score]}]I'll admit that fnil wasn't my favorite function when I first found it; however, it's become indispensable. Looking through my code I find
(update-in current [country player] (fnil + 0) score))
#'user/update-score
user=> (update-score current-score {:player "Paul Casey" :country :England :score -1})
{:England {"Paul Casey" -1}}
(fnil + 0)
and (fnil - 0)
a few times, and I definitely prefer those to the versions that use the or function.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.