In scala, how do I define addition over two Option arguments? Just to be specific, let\'s say they\'re wrappers for Int
types (I\'m actually working with maps of do
def addOpt(ao: Option[Int], bo: Option[Int]) =
for {
a <- ao
b <- bo
} yield a + b
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))
Q. What is a monoid?
A monoid is a type
M
for which there exists an associative binary operation(M, M) => M
and an identityI
under this operation, such thatmplus(m, I) == m == mplus(I, m)
for allm
of typeM
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]
ifM
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 (likefoldLeft
, for example). Recall thatfoldLeft
takes a seed value and an operation to compose successive computations. In the case of summing a monoid, the seed value is the identityI
and the operation ismplus
. You can hence callasMA.sum
on aFoldable[M : Monoid]
. You might need to useasMA
because of the name clash with the standard library'ssum
method.
def addOpts(xs: Option[Int]*) = xs.flatten.sum
This will work for any number of inputs.
If they both default to 0 you don't need pattern matching:
def addOpt(a:Option[Int], b:Option[Int]) = {
a.getOrElse(0) + b.getOrElse(0)
}
(Repeating comment above in an answer as requested)
You don't extract the content of the option the proper way. When you match with case Some(x)
, x
is the value inside the option(type Int
) and you don't call get
on that. Just do
case Some(x) => x
Anyway, if you want content or default, a.getOrElse(0)
is more convenient