Array of Arrays as Iterable of Iterables

2.9k views Asked by At

Let's consider the following definition to add up all elements in a nested Iterable structure of Ints:

def add(xss : Iterable[Iterable[Int]]) : Int = xss.map(_.sum).sum

However, evaluating following expression yields a type error:

   scala> add(Array(Array(1,2,3)))
   <console>:9: error: type mismatch;
   found   : Array[Array[Int]]
   required: Iterable[Iterable[Int]]
                add(Array(Array(1,2,3)))
                         ^

The function works as expected with other Iterables (like Lists). How can I avoid that error? What's the rationale for it? Guess that is something to do with Arrays being native from Java, but don't know the specifics details in this case.

Thanks

2

There are 2 answers

3
Marth On BEST ANSWER

It doesn't work because Scala would need to use 2 implicit conversion in a row to go from Array[Array[Int]] to Iterable[Iterable[Int]], which it (purposefully) doesn't do.

You could specify the outer Array's type :

scala> add(Array[Iterable[Int]](Array(1,2,3)))
res4: Int = 6

or transform its elements to Iterable[Int] (thus bypassing an implicit conversion) :

scala> add(Array(Array(1,2,3)).map(_.toIterable))
res5: Int = 6

The problem comes from the fact that Scala's Array[T] is just a representation for Java's T[]. What makes Array[T] behave like an usual Scala collection is an implicit conversion in Predef.

From Array's documentation :

Two implicit conversions exist in scala.Predef that are frequently applied to arrays: a conversion to mutable.ArrayOps and a conversion to mutable.WrappedArray (a subtype of scala.collection.Seq). Both types make available many of the standard operations found in the Scala collections API. The conversion to ArrayOps is temporary, as all operations defined on ArrayOps return an Array, while the conversion to WrappedArray is permanent as all operations return a WrappedArray.

The conversion to ArrayOps takes priority over the conversion to WrappedArray.

1
Gavin Schulz On

Your intuition is correct. See the Array type signature:

final class Array[T] extends java.io.Serializable with java.lang.Cloneable

contrast that with Seq type signature:

trait Seq[+A] extends Iterable[A] with collection.Seq[A] ...

As you can see Array does not relate to the Iterable[A] trait.

You can fix this by calling toIterable on the instance:

scala> add(Array(Array(1,2,3).toIterable).toIterable)
res1: Int = 6