Inductive Monoids
In the first article in this series, I talked about how monoids support multiple aggregations via product composition.
Well, they also compose in other ways.
Inductive composition of Semigroup
Some data structures have a Semigroup instance but are unable to support a Monoid. The canonical example of this is NonEmptyList. There is no natural empty value for NonEmptyList, by, well, by definition.
However, since NonEmptyList has a Semigroup instance, it is possible to create a Monoid[Option[NonEmptyList]] via the general inductive instance.
class OptionMonoid[A](implicit A: Semigroup[A]) extends Monoid[Option[A]] {
def empty: Option[A] = None
def combine(x: Option[A], y: Option[A]): Option[A] =
x match {
case None => y
case Some(a) =>
y match {
case None => x
case Some(b) => Some(A.combine(a, b))
}
}
}The inductively manufactured Monoid[Option[A]] uses None to represent empty, and relies on the Semigroup combine inside Some values in order to do implement the composite combine.
Think of it like this
A List can be thought of as an optional NonEmptyList. Therefore it seems intuitively reasonable that there should exist a Monoid instance for Option[NonEmptyList].