Advanced functional programmers will have noticed that I used A => _ as context instead of A => B. This is on purpose: the context in this scenario is a type with exactly one hole, i.e. a type constructor with one type argument.

With a little bit of imagination, this suggests that there may be other type constructors where monoids can be lifted into. It turns out that yes, this works for arbitrary applicative functors:

def applicativeMonoid[F[_] : Applicative, A: Monoid]: Monoid[F[A]] = new Monoid[F[A]] {
  def empty = Applicative[F].pure(Monoid[A].empty)
  def combine(x: F[A], y: F[A]) =
    (x, y).mapN(_ |+| _)
}

Notice two things about this definition: Firstly, it is very hard to give any kind of reasonable names to the type and value parameters because the concepts are becoming too abstract to have any relationship with any business domain. This is why functional programmers love single-letter names, since you can’t interpret them in any way; hence you can’t interpret them wrongly.

Secondly, we’re veering dangerously close into symbolic operator territory.

Let’s confirm that this is in fact the definition that Cats uses by default when summoning an implicit monoid for Int => Int:

@ val func = (applicativeMonoid: Monoid[Int => Int]).combineAll(funcs)

@ func(1)
res8: Int = 4

@ func(2)
res9: Int = 7

As it turns out, the pointwise function monoid is a special case of the applicative monoid for the reader (F[x] = A => x) applicative.

It’s not entirely trivial to see that the monoid that comes out of this construction is in fact valid. The high-level reasoning uses the fact that the mapN operation is associative and respects the identity (i.e. the pure operation).

Fun fact: Cats has a dedicated type class for the binary mapN operator required for the combine operation:

trait Semigroupal[F[_]] {
  def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]
}

A monoid without the empty element is also called a semigroup. Do you hear the sound of the plot thickening? What happens when we add an “empty” element to a “semigroupal”?

def empty[A]: F[A]

This operation is often also called return (in Haskell) or pure.

Ever heard of this infamous quote, popularized by James Iry:

A monad is just a monoid in the category of endofunctors, what’s the problem?

Various explanations of this witticism can be found on Stack Overflow, but let me say this: A monad (which is also an applicative functor) is a thing that has an identity element and an associative bind (flatMap) operation. For the purpose of this post, I’ll leave it at that, since it is just an interesting tangent.