Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't Scala infer the types from implicit evidence

I've tried this and it fails with error: missing parameter type for expanded function ((x$29) => x$29.sum).

Can someone please explain why this happens? Is this just that Scala's type inference is not powerful enough?

object HelloStackOverflow {
    implicit class Repro[T](val iterable: Iterable[T]) extends AnyVal {
        def foo[A, B, Z](bar: Iterable[B] => Z)(implicit evidence: T <:< (A, B)) =
            bar(iterable.map(_._2))
    }

    List(("a", 1), ("b", 2)).foo(_.sum)
}

(using Scala 2.10)

like image 812
Omer van Kloeten Avatar asked Dec 03 '25 02:12

Omer van Kloeten


1 Answers

Well this is because "The argument types of an anonymous function must be fully known" (Scala Language Specification 8.5).

When a method takes an anonymous function, scala uses the fact that it knows the type of the parameters to let the caller omit the types of the anonymous function's parameters (letting you write something like x => x+1 instead of x: Int => x+1, or _.sum instead of x: Iterable[Int] => x.sum. This is one of the nice applications of inference in scala. But obviously, this requires to know the exact expected type of the anonymous function in the first place, which is not the case here: the argument to the anonymous function bar is of type Iterable[B]. B is a free type variable that cannot in any way be inferred from earlier parameter lists (there is no previous parameter list in method foo). So there is simply no way that the type of B in the anonymous function (_.sum) can be inferred, which triggers an error as knowing the exact type is mandated by the spec.

This is pretty logical. In scala, an anonymous function is just (like any function) an object. Creating an anonymous function means instantiating a generic class (that extends Function*), where the types of the function's parameter are encoded as type parameters of the Function* class (read it again, I promise this sentence makes sense). It is simply never possible to instantiate any generic class without fully specifying the type parameters. Functions are no exception.

As Impredicative showed in a comment, explicitly specifying the type of the anonymous function's parameter fixes the compile error:

List(("a", 1), ("b", 2)).foo((a : Iterable[Int]) => a.sum)

Or even:

List(("a", 1), ("b", 2)).foo((_.sum):Iterable[Int] => Int)

But in your case, it appears simple to fix the issue without having to explicitly specify the type of the anonymous function:

object HelloStackOverflow {
    implicit class Repro[A,B](val iterable: Iterable[(A,B)]) extends AnyVal {
        def foo[Z](bar: Iterable[B] => Z) =
            bar(iterable.map(_._2))
    }

    List(("a", 1), ("b", 2)).foo(_.sum) // works like a charm
}

Maybe the reason why you used a single type parameter T (instead of parameters A and B as in my example above) with an evidence that T <: (A, B) was that in your real code you have other methods in class Repro that do not require T to be a pair. In this case, just create another implicit class Repro2 with a T type parameter, and migrate these other methods there. You don't need to put all your enrichments in the same implicit class.

like image 121
Régis Jean-Gilles Avatar answered Dec 04 '25 15:12

Régis Jean-Gilles



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!