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

我使用 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抱怨rabbitHoleInHolePackage和rabbitHoleInHole的含糊不清. (为什么?)
>如果我评论出RabbitHoleInHole,scalac编译并且我回来了“兔子在Hole包对象中找到了洞”. (不应该在本地范围内隐含优先权吗?)
>如果我随后注释掉了RabbitHoleInHolePackage,scalac会抱怨rabbitHoleInOuterTest和rabbitHoleInInnerTest的含糊不清. (为什么?在eed3si9n的文章中,下面列出了url,他发现内含和外部范围的含义可能有不同的优先权.)
>如果我然后注释掉RabbitHoleInInnerTest,scalac编译并且我回来了“兔子在外部测试对象中找到了洞”.

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

我已经阅读并观看了presentation by suereth,stackoverflow answer by sobrala very elaborate revisit by eed3si9n,但我仍然感到困惑.

解决方法

让我们从包对象中的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查找范围内的任何含义,找到testInstance1和testInstance2.一个人在一个更紧凑的范围内这一事实只有在它们具有相同名称时才有意义 – 正常的阴影规则适用.我们选择了不同的名称,并且没有隐含的比另一个更具体,因此正确报告了歧义.

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

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

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

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

The implicit scope of a type T consists of all companion modules
(§5.4) of classes that are associated with the implicit parameter’s
type. Here,we say a class C is associated with a type T,if it
is a base class (§5.1.2) of some part of T.

The parts of a type T are:

  • if T is a compound type T1 with ... with Tn,
    the union of the parts of T1,...,Tn,as well as T itself,
  • if T is a parameterized type S[T1,Tn],the union of the parts of S and
    T1,
  • if T is a singleton type p.type,the parts of the type of p,
  • if T is a type projection S#U,the parts of S as well as T itself,
  • in all other cases,just T itself.

我们正在寻找兔子.TC.从类型系统的角度来看,这是一个简写:rabbit.type#TC,其中rabbit.type是表示包的类型,就像它是一个常规对象一样.调用规则4,为我们提供TC和p.type部分.

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

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

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

综上所述:

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

UPDATE

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

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

      // 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])
    }
  }
}

相关文章

共收录Twitter的14款开源软件,第1页Twitter的Emoji表情 Tw...
Java和Scala中关于==的区别Java:==比较两个变量本身的值,即...
本篇内容主要讲解“Scala怎么使用”,感兴趣的朋友不妨来看看...
这篇文章主要介绍“Scala是一种什么语言”,在日常操作中,相...
这篇文章主要介绍“Scala Trait怎么使用”,在日常操作中,相...
这篇文章主要介绍“Scala类型检查与模式匹配怎么使用”,在日...