There’s also MonoidK.
@typeclass trait MonoidK[F[_]] extends SemigroupK[F] { self =>
/**
* Given a type A, create an "empty" F[A] value.
*/
def empty[A]: F[A]
/**
* Given a type A, create a concrete Monoid[F[A]].
*/
override def algebra[A]: Monoid[F[A]] =
new Monoid[F[A]] {
def empty: F[A] = self.empty
def combine(x: F[A], y: F[A]): F[A] = self.combineK(x, y)
}
....
}
This adds empty[A] function to the contract.
The notion of emptiness here is defined in terms of the left and right identity laws with regards to combineK.
Given that combine and combineK behave differently, Monoid[F[A]].empty and MonoidK[F].empty[A] could also be different.
import cats._, cats.syntax.all._
Monoid[Option[Int]].empty
// res0: Option[Int] = None
MonoidK[Option].empty[Int]
// res1: Option[Int] = None
In case of Option[Int] they happened to be both None.
trait MonoidKLaws[F[_]] extends SemigroupKLaws[F] {
override implicit def F: MonoidK[F]
def monoidKLeftIdentity[A](a: F[A]): IsEq[F[A]] =
F.combineK(F.empty, a) <-> a
def monoidKRightIdentity[A](a: F[A]): IsEq[F[A]] =
F.combineK(a, F.empty) <-> a
}