Finding the average of ages
In this section, we will explore a different use case for the option functor. We would like to, given a number of pirates, calculate the average of their ages. This is simple enough to do:
(defn avg [& xs]
(float (/ (apply + xs) (count xs))))
(let [a (some-> (pirate-by-name "Jack Sparrow") age)
b (some-> (pirate-by-name "Blackbeard") age)
c (some-> (pirate-by-name "Hector Barbossa") age)]
(avg a b c)) ;; 56.666668 Note that we are using some-> here, to protect us from nil values. Now, what happens if there is a pirate for which we have no information? Consider the following code snippet:
(let [a (some-> (pirate-by-name "Jack Sparrow") age)
b (some-> (pirate-by-name "Davy Jones") age)
c (some-> (pirate-by-name "Hector Barbossa") age)]
(avg a b c)) ;; NullPointerException clojure.lang.Numbers.ops (Numbers.java:961) It seems that we're back at square one! It's worse now, because using some-> doesn't help if we need to use all of the values at once, as opposed to threading them through a chain of function calls.
Of course, not all is lost. All we need to do is check whether all of the values are present, before calculating the average:
(let [a (some-> (pirate-by-name "Jack Sparrow") age)
b (some-> (pirate-by-name "Davy Jones") age)
c (some-> (pirate-by-name "Hector Barbossa") age)]
(when (and a b c)
(avg a b c))) ;; nil While this works perfectly fine, our implementation suddenly had to become aware that any (or all) of the values a, b, and c could be nil. The next abstraction that we will look at, applicative functors, will fix this.