Question

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?

Was it helpful?

Solution

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

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top