与ADT和类型成员匹配的模式

时间:2017-01-26 03:27:37

标签: scala inheritance types

这是this question的后续行动。

我的ADT模型如下:

sealed trait Foo {
  type A
  def method: A
}

case class Bar(p: T, q: S) extends Foo {
  type A = Array[String]
  def method: A = // implementation
}

case class Baz(p: T, q: S) extends Foo {
  type A = Map[Array[String], Array[String]]
  def method: A = // implementation
}

我还有另一个类,它将Foo的子类型作为其构造函数参数之一,并根据它是Bar还是Baz来执行特定的操作。

在链接问题的接受答案中,用户@marios向我建议实现这样的想法:

class Qux[X <: Foo](foo: X) {
  val m: X#A = foo.method

  def process = m match {
    case a: Bar#A => // do stuff with Bar
    case b: Baz#A => // do other stuff with Baz
  }

这很好用,但是由于类型擦除导致了关于未检查类型的警告(因为我在Map上匹配Baz)。但在我的特殊情况下,这些可以被安全地忽略。但我想知道我是否可以避免这种情况,并试图写下这样的东西:

class Qux[X <: Foo](foo: X) {
  def process(param: U) = {
    val m: X#A = foo.method
    foo match {
      case Bar(_, _) => BarProcessor(m).doStuff(param) // where BarProcessor expects m to be an Array[String]
      case Baz(_, _) => BazProcessor(m.keys).doStuff(param, m.values) // do related stuff using m, but where m is instead now a Map[Array[String], Array[String]]
    }
  }
}

然而,在这种情况下,我得到像

这样的错误
[error] Qux.scala:4: type mismatch;
[error]  found   : X#A
[error]  required: Array[String]
[error]  case Bar(_, _) => BarProcessor(m).doStuff(rows)
[error]                                 ^

我的问题是,为什么?看起来这两个代码片段实际上并没有什么不同,那么为什么访问类型成员在第一种情况下工作,而不是第二种?

1 个答案:

答案 0 :(得分:2)

编译器甚至不根据分支优化foo的类型(即在分支内部,X的类型仍为Bar,而不是Baz或{ {1}}),更少m。你可以写的是

  def process(param: U) = {
    foo match {
      case foo: Bar => 
        val m = foo.method // m is Bar#A, i.e. Array[String]
        BarProcessor(m).doStuff(param)
      case foo: Baz => 
        val m = foo.method // m is Baz#A, i.e. Map[Array[String], Array[String]]
        BazProcessor(m.keys).doStuff(param, m.values)
    }
  }

请注意,模式中的foo实际上是一个新变量,它会遮蔽外部foo,因此这相当于

    foo match {
      case bar: Bar => 
        val m = bar.method // m is Bar#A, i.e. Array[String]
        // note that if you use foo here, it still has type Foo
        BarProcessor(m).doStuff(param)
      ...