让我们用抽象类定义一个特征
object Outer {
trait X {
type T
val empty: T
}
Run Code Online (Sandbox Code Playgroud)
现在我们可以创建它的一个实例:
val x = new X {
type T = Int
val empty = 42
}
Run Code Online (Sandbox Code Playgroud)
Scala现在承认,这x.empty是Int:
def xEmptyIsInt = x.empty: Int
Run Code Online (Sandbox Code Playgroud)
现在,让我们定义另一个类
case class Y(x: X) extends X {
type T = x.T
val empty = x.empty
}
Run Code Online (Sandbox Code Playgroud)
并制作一个实例
val y = Y(x)
Run Code Online (Sandbox Code Playgroud)
但现在Scala,无法推断出y.empty类型Int.下列
def yEmptyIsInt = y.empty: Int
Run Code Online (Sandbox Code Playgroud)
现在生成错误消息:
error: type mismatch;
found : y.x.T
required: Int
y.empty : Int
Run Code Online (Sandbox Code Playgroud)
为什么会这样?这是一个scala bug吗?
我们可以通过向case类引入参数来缓解此问题:
case class Z[U](x: X { type T = U }) extends X {
type T = U
val empty = x.empty
}
Run Code Online (Sandbox Code Playgroud)
然后再次运作
val z = Z(x)
def zEmptyIsInt: Int = z.empty
Run Code Online (Sandbox Code Playgroud)
但我们总是要在呼叫现场提到X内的所有类型.理想情况下,这应该是一个实现细节,导致以下方法:
case class A[U <: X](z: U) extends X {
type T = z.T
val empty = z.empty
}
Run Code Online (Sandbox Code Playgroud)
这也减轻了这个问题
val a = A(x)
def aEmptyIsInt: Int = a.empty
}
Run Code Online (Sandbox Code Playgroud)
总而言之,我的问题如下:为什么简单的情况不起作用?这是一个有效的解决方法吗?当我们遵循两种解决方法之一时,可能会出现哪些其他问题?有更好的方法吗?
小智 1
您已经重复使用了不同的东西,因此从这里开始,我将调用由“实例 x”x实例化的对象以及在类 Y 中使用的“参数 x”。val xx: X
“实例x”是 的匿名子类trait X,其具体成员覆盖了抽象trait X成员。作为 的子类,X您可以将其传递给构造函数,case class Y并且它会很高兴地被接受,因为作为子类,它是一个X.
在我看来,您希望在运行时case class Y检查传递的实例是否已覆盖 的成员,并根据传入的内容生成其成员具有不同类型的实例。XXY
这显然不是 Scala 的工作方式,并且几乎会违背其(静态)类型系统的目的。例如,如果没有运行时反射,您将无法使用 Y.empty 执行任何有用的操作,因为它可以具有任何类型,此时您最好只使用动态类型系统。如果您想要参数性、自由定理、不需要反射等好处,那么您必须坚持静态确定的类型(模式匹配有一个小例外)。这就是 Scala 在这里所做的。
实际发生的情况是,您已经告诉Y构造函数参数x是 an X,因此编译器静态地确定x.empty, 的类型是X.empty,这是抽象类型T。Scala 的推理并没有失败;而是失败了。你的类型实际上不匹配。
另外,这实际上与路径依赖类型没有任何关系。这是一个很好的演练,但简而言之,路径相关类型绑定到其父实例,而不是在运行时动态确定。
| 归档时间: |
|
| 查看次数: |
208 次 |
| 最近记录: |