Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inferring type of generic implicit parameter from return type

Say I have a simple class like this

abstract class Foo {
  implicit val impInt: Int = 42
  def f[A]()(implicit a: A): A
  val f2: Int = f()
}

When declaring val f2, compiler is able to infer that the type of implicit parameter of function f is Int because that type is the same as the result type, and result type needs to match the type of value f2, which is Int.

However, throwing an Ordering[A] into the mix:

def f[A]()(implicit a: A, m: Ordering[A]): A
val f2: Int = f()

results in this compile error:

Ambiguous implicit values: both value StringCanBuildFrom in object Predef of type => scala.collection.generic.CanBuildFrom[String,Char,String] and method $conforms in object Predef of type [A]=> <:<[A,A] match expected type A

If I add the type information when invoking f(), it compiles:

val f2: Int = f[Int]()

First I encountered the case with implicit ordering and I thought it has to do with Scala inferring left-to-right; I thought it's not able to match the return type first and then infer the (implicit) parameter type of f. But then I tried the case without implicit ordering and saw that it works - it inferred that f must be parameterized by Int because the return type has to be an Int (because f2 is an Int).

Note that if we remove implicit a: A and leave only the Ordering implicit parameter, the error remains, but becomes

Diverging implicit expansion for type Ordering[A] starting with method Tuple9 in object Ordering.

Again, adding type parameter so that it becomes val f2: Int = f[Int]() helps.

What's going on? Why can the compiler infer that parameter A must be an Int, but not that parameter Ordering[A] must be an Ordering[Int]?

like image 650
slouc Avatar asked Apr 03 '16 17:04

slouc


1 Answers

There must be something wrong with the way ordering instances are generated, as the code below works. I'd report a bug.

case object types {
  implicit def buh[X]: List[X] = List()
}
abstract class Foo {

  import types._

  def f[A]()(implicit l: List[A]): A
  val f2: Int = f()
}
like image 169
Eduardo Pareja Tobes Avatar answered Sep 27 '22 20:09

Eduardo Pareja Tobes