So far, we have seen some examples where monoids are used to improve efficiency and write generic functions. They, however, are even more powerful. The reason is that they follow another useful rule:
What does this mean exactly and how can we take advantage of this? Let's look at the following function:
def compose[T, Y](a: Monoid[T], b: Monoid[Y]): Monoid[(T, Y)] = new Monoid[(T, Y)] { val zero: (T, Y) = (a.zero, b.zero) override def op(l: (T, Y), r: (T, Y)): (T, Y) = (a.op(l._1, r._1), b.op(l._2, r._2)) }
In the preceding code, we showed a function that applies the composition exactly as we mentioned in our definition. ...