scala 2.10.x中的隐式解析.这是怎么回事?

Wal*_*ang 6 scala

我使用scala 2.10.0-snapshot日期(20120522)并具有以下Scala文件:

这个定义了类型类和基本的类型类实例:

package com.netgents.typeclass.hole

case class Rabbit

trait Hole[A] {
  def findHole(x: A): String
}

object Hole {
  def apply[A: Hole] = implicitly[Hole[A]]
  implicit val rabbitHoleInHole = new Hole[Rabbit] {
    def findHole(x: Rabbit) = "Rabbit found the hole in Hole companion object"
  }
}
Run Code Online (Sandbox Code Playgroud)

这是包对象:

package com.netgents.typeclass

package object hole {

  def findHole[A: Hole](x: A) = Hole[A].findHole(x)

  implicit val rabbitHoleInHolePackage = new Hole[Rabbit] {
    def findHole(x: Rabbit) = "Rabbit found the hole in Hole package object"
  }
}
Run Code Online (Sandbox Code Playgroud)

这是测试:

package com.netgents.typeclass.hole

object Test extends App {

  implicit val rabbitHoleInOuterTest = new Hole[Rabbit] {
    def findHole(x: Rabbit) = "Rabbit found the hole in outer Test object"
  }

  {
    implicit val rabbitHoleInInnerTest = new Hole[Rabbit] {
      def findHole(x: Rabbit) = "Rabbit found the hole in inner Test object"
    }

    println(findHole(Rabbit()))
  }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,Hole是一个简单的类型类,它定义了一个Rabbit试图找到的方法.我试图找出隐含的解决方案规则.

  • 与所有四种类型类实例注释掉,scalac抱怨的歧义rabbitHoleInHolePackagerabbitHoleInHole.(为什么?)

  • 如果我发表评论rabbitHoleInHole,scalac会编译并且我会回来"兔子在Hole包对象中找到了洞".(不应该在本地范围内隐含优先权吗?)

  • 如果我再注释掉rabbitHoleInHolePackage,scalac抱怨的歧义rabbitHoleInOuterTestrabbitHoleInInnerTest.(为什么?在eed3si9n的文章中,下面列出了url,他发现内含和外部范围的含义可能有不同的优先权.)

  • 如果我然后注释掉rabbitHoleInInnerTest,scalac编译并且我回来了"兔子在外部测试对象中找到了洞".

正如您所看到的,上述行为并未遵循我在隐式解析中阅读的规则.我只描述了你在评论/取消注释实例时可以做的一小部分组合,其中大多数确实非常奇怪 - 我还没有进入导入和子类.

我已阅读并观看由suereth介绍,由索布拉尔计算器的答案,并通过eed3si9n一个非常复杂的回访,但我仍然大惑不解.

ret*_*nym 4

让我们从包对象中的隐式和禁用的类型类伴生开始:

\n\n
package rabbit {\n  trait TC\n\n  object Test  extends App {\n    implicit object testInstance1 extends TC { override def toString = "test1" }\n\n    {\n      implicit object testInstance2 extends TC { override def toString = "test2" }\n\n      println(implicitly[TC])\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

Scalac 查找范围内的任何隐式表达式,查找testInstance1testInstance2。事实上,一个人处于更严格的范围内,只有当它们具有相同的名称时才有意义——适用正常的阴影规则。我们选择了不同的名称,并且没有一个隐式名称比另一个更具体,因此可以正确报告歧义。

\n\n

让我们尝试另一个例子,这次我们将在本地范围中的隐式与包对象中的隐式进行比较。

\n\n
package rabbit {\n  object `package` {\n    implicit object packageInstance extends TC { override def toString = "package" }\n  }\n\n  trait TC\n\n  object Test  extends App {\n    {\n      implicit object testInstance2 extends TC { override def toString = "test2" }\n\n      println(implicitly[TC])\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里会发生什么?与之前一样,隐式搜索的第一阶段考虑调用站点范围内的所有隐式搜索。在这种情况下,我们有testInstance2packageInstance。这些是不明确的,但在报告该错误之前,第二阶段开始,并搜索 的隐式范围TC

\n\n

但这里的隐含范围是什么?TC甚至没有一个伴生对象?我们需要回顾一下 Scala 参考文献 7.2 中的精确定义。

\n\n
\n

类型 T 的隐式作用域由与隐式参数\xe2\x80\x99s\n 类型关联的类的所有伴随模块\n (\xc2\xa75.4) 组成。在这里,如果类 C 是 T 的某些部分的基类 (\xc2\xa75.1.2),则我们说类 C 与类型 T 相关联。

\n\n

类型的组成部分T是:

\n\n
    \n
  • 如果T是复合类型,\n 的各部分及其自身的并T1 with ... with Tn集,T1, ..., TnT
  • \n
  • ifT是参数化类型S[T1, ..., Tn], 和 部分的并集S\n T1,...,Tn
  • \n
  • 如果T是单例类型p.type,则类型的部分p
  • \n
  • ifT是一个类型投影S#U, 的部分S以及T它本身,
  • \n
  • 在所有其他情况下,仅是T其本身。
  • \n
\n
\n\n

我们正在寻找rabbit.TC. 从类型系统的角度来看,这是: 的简写rabbit.type#TC,其中rabbit.type是表示包的类型,就好像它是常规对象一样。调用规则 4,得到零件TCp.type

\n\n

那么,这一切意味着什么呢?简而言之,包对象中的隐式成员也是隐式作用域的一部分!

\n\n

在上面的示例中,这为我们在隐式搜索的第二阶段提供了明确的选择。

\n\n

其他的例子也可以用同样的方法来解释。

\n\n

总之:

\n\n
    \n
  • 隐式搜索分两个阶段进行。导入和影子的通常规则决定了候选者列表。
  • \n
  • 假设您使用的是嵌套包,封闭包对象中的隐式成员也可能在范围内。
  • \n
  • 如果有多个候选者,则使用静态重载规则来查看是否有获胜者。另外一个决胜局是,编译器更喜欢隐式的一个,而不是第一个隐式的超类中定义的另一个。
  • \n
  • 如果第一阶段失败,则以大致相同的方式查询隐式作用域。(不同之处在于,来自不同同伴的隐式成员可能具有相同的名称,而不会相互影响。)
  • \n
  • 来自封闭包的包对象中的隐式也是此隐式范围的一部分。
  • \n
\n\n

更新

\n\n

在 Scala 2.9.2 中,行为有所不同并且是错误的。

\n\n
package rabbit {\n  trait TC\n\n  object Test extends App {\n    implicit object testInstance1 extends TC { override def toString = "test1" }\n\n    {\n      implicit object testInstance2 extends TC { override def toString = "test2" }\n\n      // wrongly considered non-ambiguous in 2.9.2. The sub-class rule\n      // incorrectly considers:\n      //\n      // isProperSubClassOrObject(value <local Test>, object Test)\n      //   isProperSubClassOrObject(value <local Test>, {object Test}.linkedClassOfClass)\n      //   isProperSubClassOrObject(value <local Test>, <none>)\n      //     (value <local Test>) isSubClass <none>\n      //        <notype> baseTypeIndex <none> >= 0\n      //        0 >= 0\n      //        true\n      //     true\n      //   true\n      // true\n      //\n      // 2.10.x correctly reports the ambiguity, since the fix for\n      //\n      // https://issues.scala-lang.org/browse/SI-5354?focusedCommentId=57914#comment-57914\n      // https://github.com/scala/scala/commit/6975b4888d\n      //\n      println(implicitly[TC])\n    }\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n