Pattern matching for abstracted case classes 抽象化的case类的模式匹配
Pattern matching for abstracted case classes 抽象化的case类的模式匹配
我正在尝试使用依赖方法类型和编译器的夜间版本(2.10.0.r26005-b20111114020239)在一个模块中抽象案例类。我从Miles Sabin的例子中找到了一些灵感。
我真的不明白下面的(自包含的)代码中有什么问题。输出结果取决于foo
中模式的顺序。
//据我所知,编译器不公开伴生对象的unapply方法 // for a companion object trait Isomorphic[A, B] { def apply(x: A): B def unapply(x: B): Option[A] } //抽象模块 trait Module { // 3个带有一些约束的类型 type X type Y <: X type Z <: X // 以及它们的"伴生"对象 def X: Isomorphic[Int, X] def Y: Isomorphic[X, Y] def Z: Isomorphic[Y, Z] } //一个依赖于案例类的实现 object ConcreteModule extends Module { sealed trait X { val i: Int = 42 } object X extends Isomorphic[Int, X] { def apply(_s: Int): X = new X { } def unapply(x: X): Option[Int] = Some(x.i) } case class Y(x: X) extends X // 我猜编译器可以为我完成这个 object Y extends Isomorphic[X, Y] case class Z(y: Y) extends X object Z extends Isomorphic[Y, Z] } object Main { def foo(t: Module)(x: t.X): Unit = { import t._ // 输出结果取决于前三行的顺序 // 我不确定这里发生了什么... x match { //未经检查,因为被擦除 case Y(_y) => println("y "+_y) //未经检查,因为被擦除 case Z(_z) => println("z "+_z) //这个没问题 case X(_x) => println("x "+_x) case xyz => println("xyz "+xyz) } } def bar(t: Module): Unit = { import t._ val x: X = X(42) val y: Y = Y(x) val z: Z = Z(y) foo(t)(x) foo(t)(y) foo(t)(z) } def main(args: Array[String]) = { //使用具体模块调用bar bar(ConcreteModule) } }
有什么想法吗?
问题的原因是,在函数foo中,由于泛型擦除的影响,变量Y和Z都被擦除为它们的上界X。而更令人惊讶的是,无论是匹配Y还是匹配Z,都会阻止对X的匹配。具体来说,当匹配Y或者匹配Z的代码被注释掉时,输出结果是x 42。但是当其中一个匹配被恢复时,输出结果变为xyz AbstractMatch$ConcreteModule$X$$anon$1、y AbstractMatch$ConcreteModule$X$$anon$1和xyz Z(Y(AbstractMatch$ConcreteModule$X$$anon$1))。这种结果并不合理,因为无法找到导致xyz被选择而不是X的充分理由。因此,可以认为在模式匹配器中出现了一个bug。建议在Scala JIRA中搜索类似的问题,并且如果找不到类似问题,可以使用上述代码提取出一个最小化的重现示例,然后提交一个新的问题。
说实话,在上述第二个示例中,我本来预计在所有三个实例中都选择Y的情况,因为Y被擦除为X,并且Y的匹配在X的匹配之前。但是由于我们处于未检查的领域,对于自己的直觉并不100%自信。