Search code examples
scalafunctional-programmingscalazscala-catsmonoids

How to use Monoid Scala?


trait Monoid[A] {
  def op(a1: A, a2: A): A

  def zero: A
}

def mapMergeMonoid[K, V](V: Monoid[V]): Monoid[Map[K, V]] = new Monoid[Map[K, V]] {
    override def op(a1: Map[K, V], a2: Map[K, V]): Map[K, V] =
      (a1.keySet ++ a2.keySet).foldLeft(zero) {
        (acc, k) => acc.updated(k, V.op(a1.getOrElse(k, V.zero), a2.getOrElse(k, V.zero)))
      }

    override def zero: Map[K, V] = Map[K, V]()
  }

As I understood, i can concat 2 Maps with this Monoid. But I cant understand, how to use it. What i have to put into (V: Monoid[V]) argument to use op method after and put there 2 Maps.


Solution

  • Say we want to combine two maps of type Map[Int, String]

    val a1: Map[Int, String] = Map(1 -> "Picard")
    val a2: Map[Int, String] = Map(1 -> "Worf", 2 -> "Data")
    

    Then V becomes String which means we need to provide Monoid[String] instance in order to specify how Vs will be combined

    val stringMonoid: Monoid[String] = new Monoid[String] {
      override def op(a1: String, a2: String) = a1 + a2
      override def zero = ""
    }
    

    Putting it together we have

    mapMergeMonoid(stringMonoid).op(a1, a2)
    

    which outputs

    res0: Map[Int,String] = Map(1 -> PicardWorf, 2 -> Data)
    

    Conceptually, monoid provides a way of combining values, so when defining how to combine maps of type Map[K, V] it makes sense we would need to also specify how the values V of the map combine themselves. Hence Monoid[V] is a necessary constituent element in the definition of Monoid[Map[K, V]]:

    Map[A, B] is a Monoid if B is a Monoid