Scala3 已经放弃了通用类型投影,因为它不健全:
编译运行时失败的代码是可能的。
(编辑问题以反映评论)
考虑以下 scala3 代码: 斯卡斯蒂
class A:
class X:
def outer : A.this.type = A.this
class B extends A
class C extends A
val b0 = new B
val b1 = b0
val b2 = new B
val c0 = new C
val c1 = c0
val c2 = new C
val b0x : A#X = new b0.X
val pathTypeMatch = b0x match
case _ : c2.X => "c2.X"
case _ : c1.X => "c1.x"
case _ : c0.X => "c0.X"
case _ : b2.X => "b2.X"
case _ : b1.X => "b1.X"
case _ : b0.X => "b0.X"
case _ => "ELSE"
pathTypeMatch // "b1.x"
val projectionTypeMatch = b0x match
case _ : C#X => "C#X"
case _ : B#X => "B#X"
case _ : A#X => "A#X"
case _ => "ELSE"
projectionTypeMatch // "C#X" !!!
val failingTypeMatch = b0x match
case cx : C#X =>
val c : C = cx.outer // Fails at runtime
代码可以编译,但在运行时失败,并显示“B 类无法转换为 C 类”。
编译器将
C#X
和 B#X
擦除视为 A#X
,因此 case cx:C#X
分支匹配。从那里开始,将 cx.outer
视为 C
是合理的(这是错误的),因此是例外。
不发出任何警告。 不涉及类型测试。
查看
projectionTypeMatch
的字节码时,所有3个分支的测试完全相同。
在类型模式中:
(类型模式 T 是 ...)对类
、C
或p.C
的引用。 此类型模式匹配给定类的任何非空实例。 请注意,类的前缀(如果存在)与 确定类实例。T#C
事实上,当与路径依赖类型进行匹配时,行为是预期的(
pathTypeMatch
是b1.x
)。特别是,编译器必须在匹配中考虑 X 外部。
人们会期望与 projection-types 的匹配是一致的,不是吗?
意图是什么?
似乎 scala3 中对具体类型的类型投影限制将允许一致的行为。是这样吗?
参见https://github.com/lampepfl/dotty/issues/16728。
已修复https://github.com/lampepfl/dotty/pull/17136
观察到的行为并不反映意图。 scala3 编译器已修复:
3.3.1-RC1-bin-20230327-a569057-NIGHTLY
的行为符合预期。
请注意,错误行为可能被视为一般类型投影不健全的味道,并且无法在scala2中修复。