I’m trying to combine two Option[Iterable[_]] into a new Option[Iterable[_]]. I would like to return a Some if one (or both) of the elements is a Some and a None otherwise. It seems like there should be an idiomatic way of doing this, but I can’t seem to find one. The following seems to do what I want, but isn’t quite the slick solution I was hoping for.
def merge(
i1: Option[Iterable[_]], i2: Option[Iterable[_]]
): Option[Iterable[_]] = (i1, i2) match {
case (Some(as), Some(bs)) => Some(as ++ bs)
case (a @ Some(as), None) => a
case (None, b @ Some(bs)) => b
case _ => None
}
Any tips are appreciated. Thanks!
If you’re willing to put up with a bit of abstract algebra, there’s a nice generalization here:
Iterable[_]is a monoid under concatenation, where a monoid’s just a set of things (iterable collections, in this case) and an addition-like operation (concatenation) with some simple properties and an identity element (the empty collection).Similarly, if
Ais a monoid, thenOption[A]is also a monoid under a slightly more general version of yourmerge:(Note that we need the fact that
Ais a monoid to know what to do in the first line.)The Scalaz library captures all these generalizations in its
Monoidtype class, which lets you write yourmergelike this:Which works as expected:
(Note that there are other operations that would give valid
Monoidinstances forIterableandOption, but yours are the most commonly used, and the ones that Scalaz provides by default.)