2011-06-13 33 views
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)在这两个顶点类型特异性和非顶点类型专用匹配块模式匹配的穷尽被检查。

但是,不推荐使用case类的继承,编译器建议使用提取器来支持非叶节点上的匹配(即,在上例中,为了区分弧和顶点,而不是在顶点类型之间) 。

的问题:是有可能实现类似的类层次结构,而无需使用情况下类的继承,但仍具有图案全面性如上所示通过在这两种情况下,使用编译器执行检查?

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

我没有case类目前实现如下:

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)没有,这相当混乱。

+1

倍数:这已被固定在斯卡拉2.10。 (在Scala 2.9.x中出现了回归) – gourlaysama 2013-01-31 16:16:51

回答

0

从scala-lang.org引证:

如果模式匹配的选择器是一个密封类的一个实例,模式匹配的汇编可以发射其诊断出一组给定的警告模式并不详尽,即有在运行时提出了MatchErrorbeing的可能性。

2

提取器为您提供了在模式匹配中使用它的可能性,例如scala中的case类,但它们在使用case修饰符时没有其他标准实现。 但华中科技大学这些额外的实现(特别是平等的实现)是什么使情况类继承危险的,所以它得到了否决。

然而密封类是正交功能,你可以使用他们,你是否拥有一个案例类或提取。通过使用提取器,您不会即时获得标准实现,因此您可以继承提取器 - 它们只是具有unapply和/或unapplySeq方法的普通类。

......你在你的例子做了什么叫做在类型模式匹配。如果你只想做到这一点,你不需要案例类,也不需要提取器。你可以用你想要的每一堂课来完成。所以你可以简单地删除大小写修饰符。

因此得出结论:图案的全面性是由密封类hirachies实现。模式匹配是通过提取器和案例类来实现的,后者只是一个具有吸引力的常用函数标准实现的提取器。 我希望这有助于。

+1

很好的答案,但是请多阅读一遍,以便了解许多错别字。谢谢! – 2011-06-13 19:19:52

+0

非常感谢你的回答,我不知道穷举是通过密封层次结构实现的,而不是通过case修饰符实现的。但是,如果我将模式匹配扩展到匹配类型之外(我已经将这个更正添加到原始问题),我仍然会得到意想不到的行为,事实证明,这取决于编译器版本:( – 2011-06-13 19:45:03

2

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

密封类是因为scalac知道在编译的时候很多比赛是如何可能的特殊情况(没有双关语意)。

但由于提取允许您运行任意代码,并因为该死的停机问题,有没有办法让编译器中,你会检查每一个案件任何情况下保证。试想一下:

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

这还不全面,因为它不处理不属于8的倍数的数字,但是编译器不能推断(不上的所有Int的运行您的提取器),只有类型的某些值Int是出于完整性8.

+1

是的,当然在一般情况下这是真的,但在我的例子中,提取器对象总是返回一些东西(因为它的返回类型是Some [Int]而不是Boolean或Option),所以保证匹配不会失败(也就是说,如果最终的unapply方法终止,我认为这是一个合理的假设)因此唯一需要检查的是覆盖层次结构中的所有类型注意,编译器能够将匹配的值(类型为Node)隐式转换为提取器接受的具体类型,所以它知道层次结构。 – 2011-06-14 14:24:57