尝试路径依赖类型时,我遇到了一些意外的结果:
object Funny1 {
class X {
type Y = String
val y: Y = "y"
}
val x1 = new X
val x2 = new X
def foo(x: X)(y: x.Y): Unit = ()
def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = () // 1.3
def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()
foo(x1)(x1.y)
foo(x1)(x2.y) // <-- 1.1 would expect this to fail
foo_diff(x1.y)(x2.y)
foo_diff(x2.y)(x2.y) // <-- 1.2 would expect this to fail
foo_gen(x1.y)(x2.y)
foo_gen(x2.y)(x2.y)
}
object Funny2 {
class X {
class Y {
}
}
val x1 = new X
val x2 = new X
val x1y = new x1.Y
val x2y = new x2.Y
def foo(x: X)(y: x.Y): Unit = ()
def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()
foo(x1)(x1y)
// foo(x1)(x2y) // does not compile
foo_diff(x1y)(x2y)
// foo_diff(x2y)(x2y) // does not compile
foo_gen(x1y)(x2y)
foo_gen(x2y)(x2y)
}
object Funny3 {
trait X {
type Y
def y: Y
}
val x1 = new X {
override type Y = String
override def y: String = "y"
}
val x2 = new X {
override type Y = Int
override def y: Int = 3
}
def foo(x: X)(y: x.Y): Unit = ()
def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()
foo(x1)(x1.y)
// foo(x1)(x2.y) // 3.1 fails as expected
foo_diff(x1.y)(x2.y)
// foo_diff(x2.y)(x2.y) // 3.2 fails as expected
foo_gen(x1.y)(x2.y)
foo_gen(x2.y)(x2.y)
}
object Funny3b {
trait X {
type Y
def y: Y
}
val x1 = new X {
override type Y = String
override def y: String = "y"
}
val x2 = new X {
override type Y = String
override def y: String = "y2"
}
def foo(x: X)(y: x.Y): Unit = ()
def foo_diff(y1: x1.Y)(y2: x2.Y): Unit = ()
def foo_gen(y1: X#Y)(y2: X#Y) : Unit = ()
foo(x1)(x1.y)
foo(x1)(x2.y) // 3b.1 does not fail
foo_diff(x1.y)(x2.y)
foo_diff(x2.y)(x2.y) // 3b.2 does not fail
foo_gen(x1.y)(x2.y)
foo_gen(x2.y)(x2.y)
}
特别是,我对问题的答案感兴趣:
x
作为1.3 foo_diff
中所需的第一个参数?谢谢马丁
答案 0 :(得分:1)
我认为,所有问题背后的基本误解是您认为一种构造:
trait / class ClassName {
type T = something
}
创建一个依赖类型。没有。如果您打开规范第3.5节“类型之间的关系”的3.5.1 Equivalence小节,您可能会看到:
- 如果
t
是由类型别名类型t = T
定义的,则t
等效于T
。
这就是为什么在您的第一个示例中,编译器将x1.Y
和x2.Y
和X#Y
视为String
。如果仅用String
代替所有代码,那么为什么要编译该代码就没有问题了。
类似地,您的第三个示例仅定义了一些别名。如果用这些别名替换它们的定义,这将再次清楚为什么代码会编译并失败。
您的第二个示例使用了不同的构造
trait / class ClassName {
trait / class InnerName
}
这是一种创建依赖于路径的类型的构造,因此您希望不编译的代码实际上失败了。据我了解,这是您唯一真正涉及依赖类型的示例。
此外,如果您打开介绍第一个和第二个构造的Scala之旅Abstract Type Members和Inner Classes,您可能会注意到,只有后者(而不是前者!)提到“路径-依赖类型”。