3

A list or array of monoid type A is a monoid too. Now I would like to combine arrays of integers using cats.

scala> 1 |+| 2
res1: Int = 3

scala> Array(1, 2, 3) |+| Array(1, 2, 3)
<console>:21: error: value |+| is not a member of Array[Int]
   Array(1, 2, 3) |+| Array(1, 2, 3)

I would like to get Array(2, 4, 6) as a result of Array(1, 2, 3) |+| Array(1, 2, 3) instead. How can I do that ?

Michael
  • 41,026
  • 70
  • 193
  • 341
  • 1
    That's not really what `|+|` is for. It's meant to be an append operation. What happens if the arrays aren't the same size? – Michael Zajac Mar 07 '17 at 13:16
  • 1
    _A list or array of monoid type A is a monoid too_ this is in theory but you need to actually provide a `Monoid[Array]` in code e.g. via an `implicit`. – sebszyller Mar 07 '17 at 13:18
  • @MichaelZajac Thanks. I was probably wrong. Array of monoid is probably not a monoid. – Michael Mar 07 '17 at 14:56
  • 1
    Note, there is an operation called `merge` in Scalaz that does exactly what you want: https://github.com/scalaz/scalaz/blob/fabab8f699d56279d6f2cc28d02cc2b768e314d7/core/src/main/scala/scalaz/Align.scala#L33 I think, they just didn't port it to `cats` yet. – ZhekaKozlov Mar 13 '17 at 18:04
  • @ZhekaKozlov Oh, thanks. Good to know. I will check if it exists in `cats`. – Michael Mar 13 '17 at 18:10
  • 1
    @Michael I already checked and didn't find :( Also, I didn't find a `Monoid` instance for `Array`. – ZhekaKozlov Mar 13 '17 at 18:12
  • Thanks again. Maybe they did not port it for a reason ... – Michael Mar 13 '17 at 18:28
  • @Michael Indeed, I think it was a conscious decision by the maintainers to exclude it, as it is not a widely used collection type in Scala. – Michael Zajac Mar 14 '17 at 19:07

1 Answers1

2

combine on a Seq or an Array usually means appending them to create a new Collection.

However, you can do what you're trying to do by defining your own Monoid, with zip instead of append. Here's something I came up with on the fly:

implicit val zipArrayMonoid = new Monoid[Array[Int]] {
  override def combine(x: Array[Int], y: Array[Int]) = {
    x.zip(y).map {
      case (a, b) => a + b
    }
  }

  override def empty = Array.empty
}

This will cause arrays of different sizes to have their additional values ignored (as that's what the zip implementation does, you can check the docs here)

Here's a scalaFiddle with the result: https://scalafiddle.io/sf/YzdUl4L/0

Luka Jacobowitz
  • 22,795
  • 5
  • 39
  • 57