为什么会发生这种隐含的歧义行为?

Ges*_*sar 3 scala implicit typeclass ambiguous type-level-computation

我有一个typeclass 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]]
}
Run Code Online (Sandbox Code Playgroud)

就像我期望的那样,隐式搜索finds case1,finds 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]]]
}
Run Code Online (Sandbox Code Playgroud)

为什么在上面的示例中最后一行没有编译?

都失败ambiguous implicit values,说case1并case2满足条件。

在Scala 2.12.8和2.13.0上观察到的行为

Dmy*_*tin 5

Scala规范说:

如果有多个与隐式参数的类型匹配的合格参数,则将使用静态重载解析规则选择一个最具体的参数。

https://www.scala-lang.org/files/archive/spec/2.13/07-implicits.html#implicit-parameters

一种替代的相对重量A比的替代B是从0到2的数,定义为的总和

  • 如果A与1 一样具体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超过case10 + 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]]]
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,使用低优先级特征是没有意义的,因为如果两个隐式都匹配期望的类型,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
}
Run Code Online (Sandbox Code Playgroud)

  • 我知道,这肯定可以解释其行为,非常感谢。如果我希望使用“ case1”,该怎么办? (2认同)
  • @Gesar尝试使用“ shapeless.LowPriority”。`object Search {隐式def case1 [A](隐式ev:TypeClass1 [A]):Search [A] = null; 隐式def case2 [M [_],A](隐式ev:TypeClass2 [M,A],lowPriority:LowPriority):Search [M [A]] = null}`另请参见https://github.com/monix / implicitbox,https://github.com/milessabin/export-hook (2认同)