Monoids
You might find life becomes a lot easier when you realize that you can stand on the shoulders of giants and take advantage of common abstractions and the libraries built to use them. To this end, this question is basically about dealing with
monoids (see related questions below for more about this) and the library in question is called scalaz.
Using scalaz FP, this is just:
def add(a: Option[Int], b: Option[Int]) = ~(a |+| b)
What is more this works on any monoid M:
def add[M: Monoid](a: Option[M], b: Option[M]) = ~(a |+| b)
Even more usefully, it works on any number of them placed inside a Foldable
container:
def add[M: Monoid, F: Foldable](as: F[Option[M]]) = ~as.asMA.sum
Note that some rather useful monoids, aside from the obvious Int
, String
, Boolean
are:
Map[A, B: Monoid]
A => (B: Monoid)
Option[A: Monoid]
In fact, it's barely worth the bother of extracting your own method:
scala> some(some(some(1))) #:: some(some(some(2))) #:: Stream.empty
res0: scala.collection.immutable.Stream[Option[Option[Option[Int]]]] = Stream(Some(Some(Some(1))), ?)
scala> ~res0.asMA.sum
res1: Option[Option[Int]] = Some(Some(3))
Some related questions
Q. What is a monoid?
A monoid is a type M
for which there exists an associative binary operation (M, M) => M
and an identity I
under this operation, such that mplus(m, I) == m == mplus(I, m)
for all m
of type M
Q. What is |+|
?
This is just scalaz shorthand (or ASCII madness, ymmv) for the mplus
binary operation
Q. What is ~
?
It is a unary operator meaning "or identity" which is retrofitted (using scala's implicit conversions) by the scalaz library onto Option[M]
if M
is a monoid. Obviously a non-empty option returns its contents; an empty option is replaced by the monoid's identity.
Q. What is asMA.sum
?
A Foldable
is basically a datastructure which can be folded over (like foldLeft
, for example). Recall that foldLeft
takes a seed value and an operation to compose successive computations. In the case of summing a monoid, the seed value is the identity I
and the operation is mplus
. You can hence call asMA.sum
on a Foldable[M : Monoid]
. You might need to use asMA
because of the name clash with the standard library's sum
method.
Some References
- Slides and Video of a talk I gave which gives practical examples of using monoids in the wild