Scala parameterization of my array assumes a String?
-
16-06-2021 - |
Question
I've read up a little on Manifests and the erasure-avoidance techniques required to allow Scala to do things like "new Array[Array[T]]", but I'm a little stumped with this one....
I've got a method that sort of tabulates up a bunch of rows in an array like a spreadsheet. For example imagine a 2D array like this:
11, 5, 4
8, 3, 7
2, 1, 4
I wrote a method that sums up the columns of that array and spits out a 1D array like [21, 9, 15]
I want to genericize it beyond just Ints (like Doubles or Floats maybe), and when I add a parameter and manifest I get a compile error.
Here's the code
def sumGrid[T](grid: Array[Array[T]])(implicit m: ClassManifest[T]): Array[T] = {
val sum = new Array[T](grid(0).size)
for(i <- 0 until grid.size) {
for(j <- 0 until grid(0).size) {
sum(j) = sum(j) + grid(i)(j)
}
}
sum
}
Here's the compile error:
[ERROR] ...scala/euler/GridOperations.scala:126: error: type mismatch;
[INFO] found : T
[INFO] required: String
[INFO] sum(j) = sum(j) + grid(i)(j)
[INFO] ^
[ERROR] one error found
What's going on here? Why is String "required"?
La solution
Because you use the +
operator which is always defined for strings. Any type can be converted to String (toString
is always defined) and thus it will work for any type T
.
But you could add some constraints on T
to ensure it corresponds to the arithmetic operation. For instance, you can use implicits to get a Numeric
objects defining the addition for type T
:
def sumGrid[T](grid: Array[Array[T]])
(implicit m: ClassManifest[T], num: Numeric[T]): Array[T] = {
val sum = new Array[T](grid(0).size)
for(i <- 0 until grid.size) {
for(j <- 0 until grid(0).size) {
sum(j) = num.plus( sum(j), grid(i)(j) )
}
}
sum
}
Autres conseils
You handled the Array
part, but T
doesn't have any constrains that say it has a +
method. Because of that, Scala thinks you are adding strings -- it will auto-convert any type to a String
when you add it to another String
. So it auto-converted sum(j)
to String
, and now expects grid(i)(j)
to be another String
, at which point it gives up and fails.
You can handle the thing about T
being numeric in a similar manner to the ClassManifest
stuff. Here:
// I'm using the "context bound" notation, to make the declaration shorter and
// more obvious on what I'm saying about T
def sumGrid[T : ClassManifest : Numeric](grid: Array[Array[T]]): Array[T] = {
// the following line, plus the Numeric bound, makes T usable as a number
import scala.math.Numeric.Implicits._
val sum = new Array[T](grid(0).size)
for(i <- 0 until grid.size) {
for(j <- 0 until grid(0).size) {
sum(j) = sum(j) + grid(i)(j)
}
}
sum
}