Applicatives and Monoids
Lukewarm on the heels of my series of monoidal meditation, it is time to examine the connection between Applicative
and Monoid
typeclasses.
Similarities
Looking at the typeclasses for Applicative
trait Applicative[F[_]] extends Functor[F] {
def pure[A](a: A): F[A]
def ap[A, B](fa: F[A])(ff: F[A => B]): F[B]
}
and Monoid
there are similarities. Each has a kind of ‘unit’ function (pure
and empty
), and each has a combination function (ap
and combine
). It should come as no surprise, since the category theory for Applicative
is lax monoidal functor, since it represents monoidal combination inside effects.
Interchangeability
It turns out that we can
- create a
Monoid
instance for anyApplicative
- promote any
Monoid
to anApplicative
How?
Monoid instance for any Applicative
This is easy. To create a Monoid[F[A]]
where F
has an Applicative
instance, just wrap the empty
value with pure
, and use the Applicative
combination.
With this
implicit def monoidForApplicative[F[_], A](
implicit F: Applicative[F],
M: Monoid[A]
): Monoid[F[A]] =
new Monoid[F[A]] {
override def empty: F[A] =
F.pure(M.empty)
override def combine(x: F[A], y: F[A]): F[A] =
F.map2(x, y)(M.combine)
}
we can now write
(Previously you would have had to write Monoid[Option[NonEmptyList[String]]]
.)
Promote Monoid to Applicative
This one is a bit harder, and requires some use of the fabulous Const
data type and its phantom type B
.
From Cats,
final case class Const[A, B](getConst: A)
sealed private[data] trait ConstApplicative[C]
extends ConstApply[C] with Applicative[Const[C, *]] {
implicit def C0: Monoid[C]
def pure[A](x: A): Const[C, A] =
Const.empty
def ap[A, B](f: Const[C, A => B])(fa: Const[C, A]): Const[C, B] =
f.retag[B].combine(fa.retag[B]) // retag means cast
}
This Applicative
instance is lawful, but does look a bit useless. Its pure
function ignores its parameter x
, and uses Const(C.empty) via the implicit Monoid[C]. And ap
ignores the lifted function A => B
altogether!
But, unlikely as it seems, this Applicative
instance is useful, as shown in the next article in the series.