问题
I want to make a sum function that works with all Numeric types.
This works:
object session {
def mapReduce[A](f: A => A, combine: (A, A) => A, zero: A, inc: A)
(a: A,b: A)
(implicit num:Numeric[A]): A = {
def loop(acc: A, a: A) =
if (num.gt(a, b)) acc
else combine(f(a), mapReduce(f, combine, zero, inc)(num.plus(a, inc), b))
loop(zero, a)
}
def sum(f: Int => Int)
(a: Int, b: Int) : Int = {
mapReduce(f, (x: Int, y: Int) => x + y, 0, 1)(a, b)}
sum(x => x)(3, 4) //> res0: Int = 7
def product(f: Int => Int)
(a: Int, b: Int): Int = {
mapReduce(f, (x: Int, y: Int) => x * y, 1, 1)(a, b)}
product(x => x)(3, 4) //> res1: Int = 12
def fact(n: Int) = product(x => x)(1, n)
fact(5) //> res3: Int = 120
}
but when I try to make sum generic like this:
def sum[A](f: A => A)
(a: A, b: A)
(implicit num:Numeric[A]): A = {
mapReduce(f, (x: A, y: A) => num.plus(x, y), 0, 1)(a, b)}
sum(x => x)(3.0, 4.0) // should be 12.0
I get this error on f
type mismatch; found : A => A required: Any => Any
when i pass it to mapReduce. So what do i need to do to make sum accept any Numeric value?
回答1:
If I'm not mistaken, the other answer is almost correct except for the fact that the type has to be made explicit when calling mapReduce
instead of when calling sum
.
So in your generic definition of sum
, you might want to do this:
def sum[A](f: A => A)(a: A, b: A)(implicit num: Numeric[A]): A =
mapReduce[A](f, num.plus, num.zero, num.one)(a, b)
That's however not enough for the typer to infer the concrete type of A
, e.g. Double
when calling sum(x => x)(3.0, 4.0)
. The reason for this is that the typer is going from first to last parameter list, left to right.
Your first parameter list declares f: A => A
and only the second parameter list defines (a: A, b: A)
. Once the typer reaches the end of the first parameter list, it doesn't know what A
exactly is, and hence it fixes the type of A
to some "abstract" A
. The only thing that it knows about A
at this point is that it is a subtype of Any
.
When the typer then gets to the second parameter list, it could theoretically infer A
as Double
or whatever your concrete type is, but as said before the type has already been fixed. This is just a flaw of Scala's typer which can easily be circumvented once you get the hang of it.
More precisely, all you have to do is help the typer by switching the first and second parameter lists like so:
def sum[A](a: A, b: A)(f: A => A)(implicit num: Numeric[A]): A =
mapReduce[A](f, num.plus, num.zero, num.one)(a, b)
Then it's possible to call sum
without being explicit about the type, e.g.
Console println sum(3.0, 4.0)(x => x) // 7.0
回答2:
Give the type of sum
explicitly. I have already provided an answer to a similar question in
A simple foldRight type issue in Scala.
Questions are totally different but the reason is exactly the same.
来源:https://stackoverflow.com/questions/21302982/how-to-set-type-parameter-bound-in-scala-to-make-generic-function-for-numerics