In continuation to previous post.

There is no documentation for MonoidK (Monoid for higher Kinds) on the cats site, hence let me try explaining it. Though Monoid first.

### Monoid

`Monoid[T]` technically is a Semigroup with an `empty`/`Identity` function. i.e. There are two main properties:

- Associativity (From Semigroup)
- Identity

Roughly:

trait Monoid[T]{ /** combine is defined as: `(a combine b) combine c = a combine (b combine c)` */ def combine(x:T, y:T): T /** empty is defined as: `A o empty = empty o A = A` */ def empty : T }

An example:

implicit def intMonoid = new Monoid[Int]{ override def empty: Int = 0 override def combine(x: Int, y: Int): Int = x + y } Semigroup[Int].combine(1,2) // returns 3 implicit def listMonoid[T](implicit ev: Monoid[T]): Monoid[List[T]] = new Monoid[List[T]]{ override def empty: List[T] = List() override def combine(x: List[T], y: List[T]): List[T] = x ::: y } Semigroup[List[Int]].combine(List(1,2), List(3,4)) //gives List(1,2,3,4) implicit def optionMonoid[T](implicit ev: Monoid[T]) : Monoid[Option[T]] = new Monoid[Option[T]]{ override def empty: Option[T] = None override def combine(x: Option[T], y: Option[T]): Option[T] = x match { case Some(a) => y match { case Some(b) => Some(ev.combine(a, b)) case None => None } case None => y } } Semigroup[Option[Int]].combine(Option(1), Some(2)) //gives Option(3) //This is quite awesome in the sense, that it even works for its inner type automatically. It is same as: Semigroup.apply[Option[Int]](optionMonoid[Int](intMonoid)).combine(Option[Int](1), Some[Int](2)); //Many standard Monoids are available under below imports import cats._ import cats.std.all._ Monoid[String].combineAll(List()) Monoid[Map[String,Int]].combineAll(List(Map("a" -> 1, "b" -> 2), Map("a" -> 3))) //Returns Map(b -> 2, a -> 4)

## MonoidK

MonoidK is a Monoid for a kind (K stands for Kind). This is how it is roughly defined:

trait MonoidK[F[_]]{ def combine[A](x:F[A], y:F[A]): F[A] def empty[A] : F[A] }

We basically define the typeconstructor while declaring the `MonoidK` object and later define the type argument while calling `combine`. Its like you are working on the container and work based on its proper type provided later. One important thing to notice is that, you dont really stand a chance to work in nested. Example:

implicit def Ksemi[T]: SemigroupK[Option] = new SemigroupK[Option]{ override def combine[A](x: Option[A], y: Option[A]): Option[A] = x orElse y } SemigroupK[Option].combine(Option(1), Option(2)) // returns Option(1) implicit def listK : MonoidK[List] = new MonoidK[List] { override def empty[A]: List[A] = List() override def combine[A](x: List[A], y: List[A]): List[A] = x ::: y } MonoidK[List].combine(List(1,2,3),List(4))

When we define `SemigroupK[Option]`, we actually provide the type constructor. Doing so, leaves us no chance to provide implicit Monoid for its type parameter. So the behavior of `combine` is purely based on the container rather than the type argument.