Search code examples
scalafunctional-programmingscala-catscategory-theorymonoids

Cats' Monoid instance throws exception when merging Maps of Arrays


I defined a Monoid instance for Map[Int, Array[Int]] and tried to use it to merge a list of such maps:

import cats.Monoid
import cats.implicits._

implicit val m: Monoid[Map[Int, Array[Int]]] = Monoid[Map[Int, Array[Int]]]

List(
  Map(
    (0 -> Array(8, 9))
  ),
  Map(
    (0 -> Array(10))
  ),
  Map(
    (1 -> Array(30))
  ),
).foldMap(identity)

I would expect the output to be this:

Map(
  (0 -> Array(8, 9, 10),
  (1 -> Array(30),
)

However, the code throws the following excpetion:

[error] java.lang.NullPointerException
[error]         at cats.instances.ListInstances$$anon$1.foldMap(list.scala:74)
[error]         at cats.instances.ListInstances$$anon$1.foldMap(list.scala:16)
[error]         at cats.Foldable$Ops.foldMap(Foldable.scala:31)
[error]         at cats.Foldable$Ops.foldMap$(Foldable.scala:31)
[error]         at cats.Foldable$ToFoldableOps$$anon$5.foldMap(Foldable.scala:31)

I tried using .reduce(_ |+| _) or m.combineAll instead of .foldMap(identity), with the same result. What am I doing wrong?


Solution

  • To make your code work, you'd only need to provide proper Monoid for an array:

    //we also need ClassTag because we don't know type yet and arrays aren't generic
    implicit def arrayMonoid[T: ClassTag]: Monoid[Array[T]] = new Monoid[Array[T]] {
        override def empty: Array[T] = Array[T]()
    
        override def combine(x: Array[T], y: Array[T]): Array[T] = x ++ y
    }
    

    Cats library doesn't provide Monoid for arrays by default.