我有一个类型类Search
,如果我们有Search[A]
或TypeClass1[A]
实例,则它有一个实例TypeClass2[A]
。优先考虑1
实例。
编译如下:
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]]
}
就像我期望的那样,隐式搜索找到
case1
,找到ev1
,然后停止搜索。但是,如果我们将
TypeClass2
更改为具有更多结构,则隐式搜索将停止工作: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]]]
}
为什么在上面的示例中最后一行没有编译?
它使用
ambiguous implicit values
失败,表示case1
和case2
都满足条件。在scala 2.12.8和2.13.0上观察到的行为
最佳答案
Scala规范说:
如果有多个与隐式参数的类型匹配的合格参数,则将使用静态重载解析规则选择一个最具体的参数。
https://www.scala-lang.org/files/archive/spec/2.13/07-implicits.html#implicit-parameters
替代A
相对于替代B
的相对权重是一个从0到2的数字,定义为
A
与B
一样具体,则为1;否则为0,以及A
的类或对象派生的类或对象中定义了B
,则为1,否则为0。 https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#overloading-resolution
case1
在一个对象中定义,该对象派生自定义case2
的类(特征),反之亦然。case2
与case1
一样具体,反之亦然。因此
case1
相对case2
的相对权重为1 + 0 = 1,而case2
相对case1
的相对权重为0 + 1 = 1。所以这是模棱两可的。Error: ambiguous implicit values:
both method case2 in trait LPSearch of type [M[_], A](implicit ev: App.TypeClass2[M,A])App.Search[M[A]]
and method case1 in object Search of type [A](implicit ev: App.TypeClass1[A])App.Search[A]
match expected type App.Search[List[Int]]
implicitly[Search[List[Int]]]
在第二种情况下,使用低优先级特征是没有意义的,因为如果两个隐式都匹配期望的类型,则在同一对象中定义它们时,首选case2
。所以尝试object Search {
implicit def case1[A](implicit ev: TypeClass1[A]): Search[A] = null
implicit def case2[M[_], A](implicit ev: TypeClass2[M, A]): Search[M[A]] = null
}