Search code examples
javascalascala-collectionstype-theory

Creating three dimensional Array with arbitrary type and map in Scala


When we have an Array of arbitrary type X in Scala and we try to do a double nest to each of its values using map (that is, turning [1,2,3] into [[[1]],[[2]],[[3]]]), we get a java.lang.ArrayStoreException. Code below is a minimal failing example:

import scala.reflect.ClassTag

def doubleNest[X: ClassTag](values: Array[X]): Array[Array[Array[X]]] = {
  values map { value =>
    Array(Array(value))
  }
}

doubleNest(Array(1,2,3))

What is more, error seems to arise when turning [[1],[2],[3]] into [[[1]],[[2]],[[3]]]. Code below is a minimal failing example (error happens in the second map):

import scala.reflect.ClassTag

def doubleNest[X: ClassTag](array: Array[X]): Array[Array[Array[X]]] = {
  val nested = array map { value =>
    Array(value)
  }

  nested map { arr =>
    Array(arr)
  }
}

doubleNest(Array(1,2,3))

Why does this happen?


Solution

  • It seems that Scala doesn't like making nested generic arrays directly. Even this fails:

    def foo[T: ClassTag](t: T) = Array(Array(t))
    foo(1) // java.lang.ClassCastException: java.base/[Ljava.lang.Object; cannot be cast to [[I
    

    (In this case, it's probably because ClassTag looks at the erased class, so Array.apply does the same, and it creates an Array[Array[Object]] instead of Array[Array[T]].)

    It also appears that ClassTag has the methods wrap and newArray for this purpose. So, you can use some ugliness involving implicitly[ClassTag[X]].wrap.wrap.newArray(array.length) to get the job done, or you can do this, which seems to work by never directly asking it to create a nested array:

    import scala.reflect.ClassTag
    
    def nest[C: ClassTag](arr: Array[C]) = arr.map(Array(_))
    
    def doubleNest[X: ClassTag](array: Array[X]): Array[Array[Array[X]]] =
      nest(nest(array))