假设:
scala> sealed trait Parent
defined trait Parent
scala> case object Boy extends Parent
defined object Boy
scala> case object Girl extends Parent
defined object Girl
scala> trait F {
| type A
| def x: A
| }
defined trait F
scala> case object FImpl extends F {
| override type A = Parent
| def x: Parent = Boy
| }
defined object FImpl
然后我定义了一个方法:
scala> def foobar(f: F)(matcher: f.A => Boolean): Boolean =
| matcher(f.x)
foobar: (f: F)(matcher: f.A => Boolean)Boolean
scala> foobar(FImpl)(_ match { case Boy => true; case Girl => false})
res3: Boolean = true
我对这是如何工作感到困惑。编译器必须在编译时知道f.A
的类型吗?
答案 0 :(得分:0)
您的问题基本上是:当f.A
是运行时值时,编译器如何看到成员f
?答案是它没有。从语法上讲,f.A
看起来正在访问f
的成员,但实际上它只依赖于f
的类型。
当你写:
object FImpl extends F {
override type A = Parent
def x: Parent = Boy
}
FImpl
定义了一种新的单例类型,称为FImpl.type
。因此,FImpl.type#A
为Parent
。
当您致电foobar
时,f.type
标识为FImpl.type
,因此f.A
又名f.type#A
为FImpl.type#A
又名Parent
考虑以下两个例子:
def needsParent(p: Parent) = ???
def foobar(f: F)(matcher: f.A => Boolean) = ???
foobar(FImpl)(needsParent) // Works
foobar(FImpl: F)(needsParent) // Does not work
虽然运行时值相同,但类型不同,因此编译器接受一个并拒绝另一个。
换句话说,Scala的依赖类型是一个聪明的小说 - 事实上,类型永远不依赖于实际的运行时值。但事实证明,只要通过识别其他类型的类型,就可以在有限的范围内跟踪值,从而给出依赖于值的类型的印象。