scala的类型检查器无法识别抽象路径依赖类场景中的类型

时间:2016-05-26 12:49:25

标签: scala types

让我们用抽象类

定义一个特征
object Outer {

  trait X {
    type T
    val empty: T
  }

现在我们可以创建一个实例:

  val x = new X {
    type T = Int
    val empty = 42
  }

Scala现在认识到,x.emptyInt

  def xEmptyIsInt = x.empty: Int

现在,让我们定义另一个类

  case class Y(x: X) extends X {
    type T = x.T
    val empty = x.empty
  }

并制作一个实例

  val y = Y(x)

但现在Scala,无法推断y.empty的类型为Int。以下

  def yEmptyIsInt = y.empty: Int

现在会生成错误消息:

error: type mismatch;
found   : y.x.T
required: Int
       y.empty : Int

为什么会这样?这是一个scala bug吗?

我们可以通过向案例类引入参数来缓解此问题:

  case class Z[U](x: X { type T = U }) extends X {
    type T = U
    val empty = x.empty
  }

然后再次运作

  val z = Z(x)
  def zEmptyIsInt: Int = z.empty

但是我们总是要在呼叫现场提到X内的所有类型。理想情况下,这应该是一个实现细节,导致以下方法:

  case class A[U <: X](z: U) extends X {
    type T = z.T
    val empty = z.empty
  }

这也减轻了问题

  val a = A(x)
  def aEmptyIsInt: Int = a.empty

}

总而言之,我的问题如下:为什么简单的案例不起作用?这是一个有效的解决方法吗?当我们遵循两种解决方法之一时,可能会出现哪些其他问题?有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

您已经重复使用x来处理不同的事情,所以从这里开始,我将调用由val x“实例x”实例化的对象和Y类中使用的x: X “参数x”。

“Instance x”是trait X的{​​{3}},具体成员会覆盖trait X的抽象成员。作为X的子类,您可以将其传递给case class Y的构造函数,并且它将被愉快地接受,因为作为子类, X。< / p>

在我看来,您希望case class Y然后在运行时检查 以查看它传递的X实例是否已覆盖X成员,并生成Y的实例,其成员具有不同的类型,具体取决于传入的内容。

这显然不是Scala的工作方式,并且几乎会破坏其(静态)类型系统的目的。例如,如果没有运行时反射,你就无法对Y.empty做任何有用的事情,因为它可以有任何类型,那时你最好只使用动态类型系统。如果你想要参数,自由定理,不需要反射等的好处,那么你必须坚持静态确定的类型(模式匹配的一个小例外)。这就是Scala在这里所做的。

实际发生的是你告诉Y的构造函数,参数xX,因此编译器静态地确定x.empty,具有类型X.empty,它是抽象类型T。斯卡拉的推断并没有失败;你的类型实际上是不匹配的。

另外,这与路径依赖类型没有任何关系。这是一个很好的anonymous subclass,但简而言之,路径依赖类型绑定到它们的父实例,而不是在运行时动态确定。