From cd5471d1d22eea195cdd75c87deaa928488c4ea2 Mon Sep 17 00:00:00 2001 From: Philip Wills Date: Thu, 3 Sep 2015 10:05:57 +0100 Subject: [PATCH] Add an example using foldMap on to a tuple --- docs/src/main/tut/monoid.md | 55 ++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/docs/src/main/tut/monoid.md b/docs/src/main/tut/monoid.md index 9a133ecefe..8ff8f37f7c 100644 --- a/docs/src/main/tut/monoid.md +++ b/docs/src/main/tut/monoid.md @@ -7,17 +7,20 @@ source: "https://github.com/non/algebra/blob/master/core/src/main/scala/algebra/ --- # Monoid -`Monoid` extends the [`Semigroup`](semigroup.html) typeclass, 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. +`Monoid` extends the [`Semigroup`](semigroup.html) 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 = ""`. +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. +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. ```tut import cats._ @@ -28,8 +31,9 @@ Monoid[String].combineAll(List("a", "b", "c")) Monoid[String].combineAll(List()) ``` -The advantage of using these typeclass 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. +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. ```tut import cats._ @@ -38,9 +42,40 @@ import cats.std.all._ Monoid[Map[String,Int]].combineAll(List(Map("a" -> 1, "b" -> 2), Map("a" -> 3))) Monoid[Map[String,Int]].combineAll(List()) ``` + +This is also true if we define our own instances. As an example, let's use +[`Foldable`](foldable.html)'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: + +```tut +import cats._ +import cats.implicits._ + +val l = List(1, 2, 3, 4, 5) + +l.foldMap(identity) +l.foldMap(i => i.toString) + +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) + } + +l.foldMap(i => (i, i.toString)) // do both of the above in one pass, hurrah! +``` + +------------------------------------------------------------------------------- N.B. -Cats does not define a `Monoid` typeclass itself, it uses the [`Monoid` +Cats does not define a `Monoid` type class itself, it uses the [`Monoid` trait](https://github.com/non/algebra/blob/master/core/src/main/scala/algebra/Monoid.scala) which is defined in the [algebra project](https://github.com/non/algebra) on which it depends. The [`cats` package object](https://github.com/non/cats/blob/master/core/src/main/scala/cats/package.scala)