MonoidK is a universal monoid which operates on kinds.

This type class is useful when its type parameter F[_] has a structure that can be combined for any particular type, and which also has an “empty” representation. Thus, MonoidK is like a Monoid for kinds (i.e. parameterized types).

A MonoidK[F] can produce a Monoid[F[A]] for any type A.

Here’s how to distinguish Monoid and MonoidK:

  • Monoid[A] allows A values to be combined, and also means there is an “empty” A value that functions as an identity.

  • MonoidK[F] allows two F[A] values to be combined, for any A. It also means that for any A, there is an “empty” F[A] value. The combination operation and empty value just depend on the structure of F, but not on the structure of A.

Let’s compare the usage of Monoid[A] and MonoidK[F].

First some imports:

import cats.{Monoid, MonoidK}
import cats.implicits._

Just like Monoid[A], MonoidK[F] has an empty method, but it is parameterized on the type of the element contained in F:

// res0: List[String] = List()

// res1: List[String] = List()

// res2: List[Int] = List()

And instead of combine, it has combineK, which also takes one type parameter:

Monoid[List[String]].combine(List("hello", "world"), List("goodbye", "moon"))
// res3: List[String] = List(hello, world, goodbye, moon)

MonoidK[List].combineK[String](List("hello", "world"), List("goodbye", "moon"))
// res4: List[String] = List(hello, world, goodbye, moon)

MonoidK[List].combineK[Int](List(1, 2), List(3, 4))
// res5: List[Int] = List(1, 2, 3, 4)

Actually the type parameter can usually be inferred:

MonoidK[List].combineK(List("hello", "world"), List("goodbye", "moon"))
// res6: List[String] = List(hello, world, goodbye, moon)

MonoidK[List].combineK(List(1, 2), List(3, 4))
// res7: List[Int] = List(1, 2, 3, 4)

MonoidK extends SemigroupK, so take a look at the SemigroupK documentation for more examples.