Scala type constraint precedence in implicit resolution

165 views Asked by At

I have these 2 implicits

trait A[T] {
  val name: String
}

trait B

object A {
  implicit def product[T <: Product] = new A[T] {
    override val name: String = "product"
  }

  implicit def childOfB[T <: Product with B] = new A[T] {
    override val name: String = "child of B"
  }
}

and if I try to find an implicit instance of A[C] where C is

case class C() extends B

childOfB will be selected.

I know it is logical but why does this happen? I cannot find it documented anywhere.

3

There are 3 answers

1
ghik On BEST ANSWER

Scala Language Specification says:

If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution.

Overloading resolution has a notion of one symbol being more specific than other. Precise, general definition of specificity is quite complex (as you can see in the specification linked above) but in your case it boils down to the fact that childOfB covers strictly a subset of types covered by product and therefore is more specific.

0
Dmytro Mitin On

Specification says

If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static overloading resolution.

0
joel On

To extend @ghik's answer, from Programmming in Scala,

To be more precise, one implicit conversion is more specific than another if one of the following applies:

  • The argument of the former is a subtype of the latter's
  • Both conversions are methods, and the enclosing class of the former extends the enclosing class of the latter

My guess is that "argument" in that quote refers also to type parameters, as is sugggested by

object A {
  implicit def A2Z[T <: A] = new Z[T] {println("A")}
  implicit def B2Z[T <: B] = new Z[T] {println("B")}
}

trait A
trait B extends A

trait Z[T]

def aMethod[T <: A](implicit o: Z[T]) = ()

implicit val a: A = new A {}

aMethod // prints B even though we started with an A