How to set type parameter bound in scala to make generic function for numerics?

非 Y 不嫁゛ 提交于 2021-02-19 07:12:58

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!