Scala implicit typeclass precedence in companion o

2019-07-17 00:53发布

问题:

trait Eq[-A] {
  def eq(a: A, b: A): Boolean
}
object Eq {
  implicit object IntEq extends Eq[Int] {
    def eq(a: Int, b: Int) = a == b
  }
}

trait Supertrait[+A]
object Supertrait {
  implicit def Eq[A: Eq]: Eq[Supertrait[A]] = ???
}

trait Subtrait[+A] extends Supertrait[A]
object Subtrait {
  implicit def Eq[A: Eq]: Eq[Subtrait[A]] = ???
}

def f[A](x: Subtrait[A])(implicit ev: Eq[Subtrait[A]]) = ???

f(new Subtrait[Int] {})

When compiling this code, the following error occurs:

Error:(32, 4) ambiguous implicit values:
 both method Eq in object Supertrait of type [A](implicit evidence$1: Eq[A])Eq[Supertrait[A]]
 and method Eq in object Subtrait of type [A](implicit evidence$2: Eq[A])Eq[Subtrait[A]]
match expected type Eq[Subtrait[Int]]
  f(new Subtrait[Int] {})
   ^

Why doesn't the implicit def in the Subtrait companion object has higher precedence than the one in Supertrait?

I'd like that implicit defs in the companion object of subtraits have higher precedence than those in supertraits.

UPDATE

The LowPriorityImplicits trick does not work either. See Enforcing precedence in implicit instances in Scala.

回答1:

It looks like your code violates the Non-Ambiguity Rule for implicits.

From Programming in Scala, 1st edition:

Non-Ambiguity Rule: An implicit conversion is only inserted if there is no other possible conversion to insert. If the compiler has two options to fix x + y, say using either convert1(x) + y or convert2(x) + y, then it will report an error and refuse to choose between them. It would be possible to define some kind of "best match" rule that prefers some conversions over others. However, such choices lead to really obscure code. Imagine the compiler chooses convert2, but you are new to the file and are only aware of convert1—you could spend a lot of time thinking a different conversion had been applied!

The complete text can be found here.