Monoid

Monoid extends the Semigroup type class, adding an empty method to semigroup's combine. The empty method must return a value that when combined with any other instance of that type returns the other instance, i.e.

(combine(x, empty) == combine(empty, x) == x)

For example, if we have a Monoid[String] with combine defined as string concatenation, then empty = "".

Having an empty defined allows us to combine all the elements of some potentially empty collection of T for which a Monoid[T] is defined and return a T, rather than an Option[T] as we have a sensible default to fall back to.

scala> import cats._
import cats._

scala> import cats.std.all._
import cats.std.all._

scala> Monoid[String].empty
res0: String = ""

scala> Monoid[String].combineAll(List("a", "b", "c"))
res1: String = abc

scala> Monoid[String].combineAll(List())
res2: String = ""

The advantage of using these type class provided methods, rather than the specific ones for each type, is that we can compose monoids to allow us to operate on more complex types, e.g.

scala> import cats._
import cats._

scala> import cats.std.all._
import cats.std.all._

scala> Monoid[Map[String,Int]].combineAll(List(Map("a" -> 1, "b" -> 2), Map("a" -> 3)))
res3: Map[String,Int] = Map(b -> 2, a -> 4)

scala> Monoid[Map[String,Int]].combineAll(List())
res4: Map[String,Int] = Map()

This is also true if we define our own instances. As an example, let's use Foldable's foldMap, which maps over values accumulating the results, using the available Monoid for the type mapped onto. To use this with a function that produces a tuple, we can define a Monoid for a tuple that will be valid for any tuple where the types it contains also have a Monoid available:

scala> import cats._
import cats._

scala> import cats.implicits._
import cats.implicits._

scala> val l = List(1, 2, 3, 4, 5)
l: List[Int] = List(1, 2, 3, 4, 5)

scala> l.foldMap(identity)
res5: Int = 15

scala> l.foldMap(i => i.toString)
res6: String = 12345

scala> implicit def tupleMonoid[A : Monoid, B : Monoid]: Monoid[(A, B)] =
     |    new Monoid[(A, B)] {
     |      def combine(x: (A, B), y: (A, B)): (A, B) = {
     |        val (xa, xb) = x
     |        val (ya, yb) = y
     |        (Monoid[A].combine(xa, ya), Monoid[B].combine(xb, yb))
     |      }
     |      def empty: (A, B) = (Monoid[A].empty, Monoid[B].empty)
     |    }
tupleMonoid: [A, B](implicit evidence$1: cats.Monoid[A], implicit evidence$2: cats.Monoid[B])cats.Monoid[(A, B)]

scala> l.foldMap(i => (i, i.toString)) // do both of the above in one pass, hurrah!
res7: (Int, String) = (15,12345)

N.B. Cats does not define a Monoid type class itself, it uses the Monoid trait which is defined in the algebra project on which it depends. The cats package object defines type aliases to the Monoid from algebra, so that you can import cats.Monoid.

useful links