了解依赖类型

时间:2016-02-05 16:46:57

标签: scala

假设:

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的类型吗?

1 个答案:

答案 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#AParent

当您致电foobar时,f.type标识为FImpl.type,因此f.A又名f.type#AFImpl.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的依赖类型是一个聪明的小说 - 事实上,类型永远不依赖于实际的运行时值。但事实证明,只要通过识别其他类型的类型,就可以在有限的范围内跟踪值,从而给出依赖于值的类型的印象。