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

时间:2012-05-26 04:12:20

标签: 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"
  }
}

这是包对象:

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"
  }
}

这是测试:

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()))
  }
}

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

  • 取消注释所有四个类型类实例,scalac抱怨rabbitHoleInHolePackagerabbitHoleInHole含糊不清。 (为什么?)

  • 如果我注释掉rabbitHoleInHole,scalac会编译并且我回来了“兔子在Hole包对象中找到了洞”。 (不应该在本地范围内隐含优先权吗?)

  • 如果我随后发表评论rabbitHoleInHolePackage,则scalac会抱怨rabbitHoleInOuterTestrabbitHoleInInnerTest的含糊不清。 (为什么?在eed3si9n的文章中,下面列出了url,他发现内部和外部范围的含义可能有不同的优先权。)

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

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

我已阅读并观看presentation by suerethstackoverflow answer by sobrala very elaborate revisit by eed3si9n,但我仍然感到困惑。

1 个答案:

答案 0 :(得分:4)

让我们从包对象中的implicits和禁用的类型类伴随开始:

package rabbit {
  trait TC

  object Test  extends App {
    implicit object testInstance1 extends TC { override def toString = "test1" }

    {
      implicit object testInstance2 extends TC { override def toString = "test2" }

      println(implicitly[TC])
    }
  }
}

Scalac查找范围内的任何含义,找到testInstance1testInstance2。一个人在一个更紧凑的范围内这一事实只有在它们具有相同名称时才有意义 - 正常的阴影规则适用。我们选择了不同的名称,并且没有隐含的比另一个更具体,因此可以正确报告歧义。

让我们尝试另一个例子,这次我们将在本地范围内对照包对象中的一个隐含。

package rabbit {
  object `package` {
    implicit object packageInstance extends TC { override def toString = "package" }
  }

  trait TC

  object Test  extends App {
    {
      implicit object testInstance2 extends TC { override def toString = "test2" }

      println(implicitly[TC])
    }
  }
}

这里发生了什么?与之前一样,隐式搜索的第一阶段考虑了呼叫站点范围内的所有隐含。在这种情况下,我们有testInstance2packageInstance。这些是模糊的,但在报告错误之前,第二阶段开始,并搜索TC的隐含范围。

但这里的隐含范围是什么? TC甚至没有伴侣对象?我们需要在Scala Reference的7.2中查看精确的定义。

  

类型T的隐式范围由所有伴随模块组成   (§5.4)与隐式参数相关联的类   类型。在这里,我们说类C与类型T相关联,如果它   是T的某个部分的基类(第5.1.2节)。

     

类型T的部分是:

     
      
  • 如果T是复合类型T1 with ... with Tn,   T1, ..., Tn部分的联合,以及T本身,
  •   
  • 如果T是参数化类型S[T1, ..., Tn]ST1,...,Tn部分的并集   T
  •   
  • 如果p.type是单身人士类型p,那么T类型的部分,
  •   
  • 如果S#U是类型投影ST的部分以及T本身,
  •   
  • 在所有其他情况下,仅rabbit.TC本身。
  •   

我们正在搜索rabbit.type#TC。从类型系统的角度来看,这是:rabbit.type的简写,其中TC是表示包的类型,就像它是常规对象一样。调用规则4,为我们提供了部分p.typepackage rabbit { trait TC object Test extends App { implicit object testInstance1 extends TC { override def toString = "test1" } { implicit object testInstance2 extends TC { override def toString = "test2" } // wrongly considered non-ambiguous in 2.9.2. The sub-class rule // incorrectly considers: // // isProperSubClassOrObject(value <local Test>, object Test) // isProperSubClassOrObject(value <local Test>, {object Test}.linkedClassOfClass) // isProperSubClassOrObject(value <local Test>, <none>) // (value <local Test>) isSubClass <none> // <notype> baseTypeIndex <none> >= 0 // 0 >= 0 // true // true // true // true // // 2.10.x correctly reports the ambiguity, since the fix for // // https://issues.scala-lang.org/browse/SI-5354?focusedCommentId=57914#comment-57914 // https://github.com/scala/scala/commit/6975b4888d // println(implicitly[TC]) } } }

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

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

其他例子可以用同样的方式解释。

总结:

  • 隐式搜索分两个阶段进行。通常的导入和镜像规则决定候选人名单。
  • 假设您正在使用nested packages
  • 封闭包对象中的隐式成员也可能在范围内。
  • 如果有多个候选者,则使用静态重载规则来查看是否有胜利者。作为addiotnal的一个决胜局,编译器更喜欢隐含在第一个超类中定义的另一个。
  • 如果第一阶段失败,则以相同的方式查询隐式范围。 (不同之处在于来自不同同伴的隐式成员可能具有相同的名称而不会相互遮蔽。)
  • 封装包中的包对象中的Implicits也是此隐式范围的一部分。

<强>更新

在Scala 2.9.2中,行为是不同的和错误的。

{{1}}