我有一个类型类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失败,表示case1case2都满足条件。

在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的数字,定义为

  • 如果AB一样具体,则为1;否则为0,以及
  • 如果在从定义A的类或对象派生的类或对象中定义了B,则为1,否则为0。

  • https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#overloading-resolution
  • case1在一个对象中定义,该对象派生自定义case2的类(特征),反之亦然。
  • case2case1一样具体,反之亦然。

  • 因此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
    }
    

    08-15 22:01