I'm trying to write a generic mean function that operates on an Iterable that contains numeric types. It would operate, say, on arrays, as so:
val rand = new scala.util.Random()
val a = Array.fill(1000) { rand.nextInt(101) }
val b = Array.fill(1000) { rand.nextDouble }
println(mean(a))
println(mean(b))
etc., hopefully being able to work on other iterables, such as lists.
I have tried various incantations for the mean method, to no avail:
def mean[T <% Numeric[T]](xs: Iterable[T]) = xs.sum.toDouble / xs.size
def mean[A](xs: Iterable[Numeric[A]]):Double = xs.sum.toDouble / xs.size
def mean[T](xs: Iterable[T])(implicit num: Numeric[T]):Double = xs.sum / xs.size
def mean(xs: Iterable[Double]) = xs.sum / xs.size
What is the proper way to do this in Scala?
This works:
def mean[T : Numeric](xs: Iterable[T]): T = implicitly[Numeric[T]] match {
case num: Fractional[_] => import num._; xs.sum / fromInt(xs.size)
case num: Integral[_] => import num._; xs.sum / fromInt(xs.size)
case _ => sys.error("Undivisable numeric!")
}
So, let's make some explanations. First, Numeric
must be used in type class pattern. That is, you don't say a type T
is, or can be converted into, Numeric
. Instead, Numeric
provides methods over a type T
. One such example is num.fromInt
.
Next, Numeric
does not provide a common division operator. Instead, one must choose between Fractional
and Integral
. Here, I'm matching on Numeric[T]
to distinguish between both.
Note that I don't use T
on the match, because Scala cannot check for type parameters on matches, as they are erased. Instead, I use _
, and Scala infers the correct type if possible (as it is here).
After that, I'm importing num._
, where num
is either Fractional
or Integral
. This brings some implicit conversions into context that let me do stuff like calling the method /
directly. If I did not do that import, I'd be forced to write this:
num.div(xs.sum, num.fromInt(xs.size))
Note that I do not have to pass the implicit parameter to xs.sum
, since it is already implicitly available in the scope.
I guess that's it. Did I miss anything?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With