Pattern matching for abstracted case classes 抽象化的case类的模式匹配

9 浏览
0 Comments

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)
  }
}

有什么想法吗?

0
0 Comments

问题的原因是,在函数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%自信。

0