I have a typeclass Search
, which has an instance Search[A]
if we have a TypeClass1[A]
or a TypeClass2[A]
instance. With preference given to the 1
instance.
The following compiles:
trait TypeClass1[A]
trait TypeClass2[A]
trait Search[A]
object Search extends LPSearch {
implicit def case1[A](implicit ev: TypeClass1[A]): Search[A] = null
}
trait LPSearch {
implicit def case2[A](implicit ev: TypeClass2[A]): Search[A] = null
}
object Test {
implicit val ev1: TypeClass1[Int] = null
implicit val ev2: TypeClass2[Int] = null
implicitly[Search[Int]]
}
This is as I would expect, the implicit search finds case1
, finds ev1
, and stops searching.
However, if we change TypeClass2
to have more structure, the implicit search stops working:
trait TypeClass1[A]
trait TypeClass2[M[_], A]
trait Search[A]
object Search extends LPSearch {
// This is the same as before
implicit def case1[A](implicit ev: TypeClass1[A]): Search[A] = null
}
trait LPSearch {
implicit def case2[M[_], A](implicit ev: TypeClass2[M, A]): Search[M[A]] = null
}
object Test {
implicit val ev1: TypeClass1[List[Int]] = null
implicit val ev2: TypeClass2[List, Int] = null
// Does not compile:
implicitly[Search[List[Int]]]
}
Why does this last line not compile in the above example?
It fails with ambiguous implicit values
, saying both case1
and case2
satisfy the condition.
Behaviour observed on scala 2.12.8 and 2.13.0
Scala specification says:
https://www.scala-lang.org/files/archive/spec/2.13/07-implicits.html#implicit-parameters
https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#overloading-resolution
case1
is defined in an object which is derived from the class (trait) definingcase2
but not vice versa.case2
is as specific ascase1
but not vice versa.So relative weight of
case1
overcase2
is 1+0=1 and relative weight ofcase2
overcase1
is 0+1=1. So it's ambiguity.In the second case there is no sense to use low-priority trait since if both implicits match expected type,
case2
is preferred when they are defined in the same object. So try