我使用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 sobral和a very elaborate revisit by eed3si9n,但我仍然感到困惑。
答案 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查找范围内的任何含义,找到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中查看精确的定义。
类型T的隐式范围由所有伴随模块组成 (§5.4)与隐式参数相关联的类 类型。在这里,我们说类C与类型T相关联,如果它 是T的某个部分的基类(第5.1.2节)。
类型
T
的部分是:
- 如果
T
是复合类型T1 with ... with Tn
,T1, ..., Tn
部分的联合,以及T
本身,- 如果
T
是参数化类型S[T1, ..., Tn]
,S
和T1,...,Tn
部分的并集T
,- 如果
p.type
是单身人士类型p
,那么T
类型的部分,- 如果
S#U
是类型投影S
,T
的部分以及T
本身,- 在所有其他情况下,仅
rabbit.TC
本身。
我们正在搜索rabbit.type#TC
。从类型系统的角度来看,这是:rabbit.type
的简写,其中TC
是表示包的类型,就像它是常规对象一样。调用规则4,为我们提供了部分p.type
和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])
}
}
}
。
那么,这意味着什么呢?简单来说,包对象中的隐式成员也是隐式作用域的一部分!
在上面的示例中,这为隐式搜索的第二阶段提供了明确的选择。
其他例子可以用同样的方式解释。
总结:
<强>更新强>
在Scala 2.9.2中,行为是不同的和错误的。
{{1}}