9

我有一个简单的类层次结构,它表示一个类似图形的结构,其中包含使用案例类实现的几种不同类型的顶点:

sealed trait Node

sealed abstract case class Vertex extends Node
case class Arc extends Node

case class VertexType1 (val a:Int) extends Vertex
case class VertexType2 (val b:Int) extends Vertex

这允许我编写这样的匹配块:

def test (x: Node) = x match {
  case _ : Arc => "got arc"
  case _ : Vertex => "got vertex"
}

或像这样:

def test (x: Node) = x match {
  case _ : Arc => "got arc"
  case c : Vertex => c match {
    case _ : VertexType1(a) => "got type 1 vertex " + a
    case _ : VertexType2(a) => "got type 2 vertex " + a
  }
}

请注意,此实现具有以下属性:

1) 它允许编写区分弧和顶点的匹配块,但不能区分特定的顶点类型,但也允许编写区分顶点类型的匹配块。

2) 在特定于顶点类型和非特定于顶点类型的匹配块中,检查模式匹配的穷举性。

但是,不推荐从案例类继承,并且编译器建议使用提取器来支持非叶节点上的匹配(即,在上面的示例中,区分弧和顶点,但不区分顶点类型)。

问题:是否可以在不使用案例类继承的情况下实现类似的类层次结构,但在上面显示的两个用例中仍然由编译器执行模式详尽性检查?

编辑:我在 VertexType 类中添加了一个构造函数参数,以便不仅仅对类型执行匹配。

我当前没有案例类的实现如下:

sealed trait Node

sealed abstract class Vertex extends Node
class Arc extends Node

class VertexType1 (val a:Int) extends Vertex
class VertexType2 (val b:Int) extends Vertex

object VertexType1 {
  def unapply (x : VertexType1) : Some[Int] = Some(x.a)
}

object VertexType2 {
  def unapply (x : VertexType2) : Some[Int] = Some(x.b)
}

和测试代码:

def test (x: Node) = x match {
  case _ : Arc => "got arc" 
  case v : Vertex => v match {
    case VertexType1(a) => "got vertex type 1 " + a 
  }
}

我希望在第二个块中出现关于非详尽匹配的警告(VertexType2 永远不会匹配),但没有一个。

实际上,2.9.0-RC3 之前的 Scala 编译器会产生我希望看到的警告,但以 RC3 开头的版本(包括 2.9.0 和 2.9.0-1)不会,这相当令人困惑。

4

3 回答 3

2

一般来说,这是做不到的。

密封类是一种特殊情况(没有双关语),因为scalac在编译时知道有多少匹配是可能的。

但是由于提取器允许您运行任意代码并且由于该死的停止问题,编译器无法保证在每种情况下您都会检查每种情况。考虑:

def test(foo: Int) {
  foo match {
    case IsMultipleOf8(n) => printf("%d was odd\n", n)
  }
}

这并不详尽,因为它不处理不是 8 的倍数的数字,但编译器无法推断出(不在 all 上运行您的提取器Int)只有某些类型的值Int是 8 的倍数。

于 2011-06-14T13:24:51.360 回答
2

extractors give you the possibility to use it in pattern matching like case classes in scala, but they don´t have other standard implementations you get when using the case modifier. But hust these extra implementation(especially the implementation of equals) is what makes case class inheritance dangerous and so it got deprecated.

However sealed classes are an orthogonal feature and you can use them whether or not you have a case class or an extractor. By using extractors you don´t get standard implementations on the fly but thus you can have inheritance of extractors - they are simply normal classes with an unapply and/or unapplySeq method.

What You´ve done in your example is called pattern-matching on types. If you only want to do this, you don´t need case classes neither extractors. You can do it with every class you want. So you can simply remove the case modifier.

So to conclude: exhaustiveness of pattern is achieved by sealed class hirachies. Pattern matching is achieved by extractors and case classes of what the latter is just an extractor with appealing standard implementations of frequently used functions. I hope this helped.

于 2011-06-13T17:45:31.830 回答
0

来自 scala-lang.org 的引文:

如果模式匹配的选择器是密封类的实例,则模式匹配的编译会发出警告,诊断给定的一组模式并不详尽,即有可能在运行时引发 MatchError。

于 2011-06-13T16:47:13.060 回答