如何使Scala的类型系统捕获此MatchError?

时间:2014-09-09 10:21:12

标签: scala pattern-matching typeclass scalaz static-typing

我已经定义了Seq[Seq[T]]的排序,使得它是正常的词典排序,除了所有项目(子序列)首先被反转(以便C,B,AA,B,C之前出现A,B,A 1}}但在implicit def ReverseListOrdering[T: Ordering] = new Ordering[Seq[T]] { override def compare(xs1: Seq[T], xs2: Seq[T]) = doCompare(xs1.reverse, xs2.reverse) private def doCompare(xs1: Seq[T], xs2: Seq[T]): Int = (xs1, xs2) match { case (Nil, Nil) => 0 case (x :: _, Nil) => 1 case (Nil, x :: _) => -1 case (x :: xs, y :: ys) => val a = implicitly[Ordering[T]].compare(x, y) if (a != 0) a else doCompare(xs, ys) } } 之后):

List[List[T]]

以前曾在Seq[Seq[T]]上定义,但我后来意识到我想要所有Nil;这就是为什么我最初离开模式匹配块中的Nil,而未能意识到Array从未匹配,例如空// the Seq[String] declarations are needed; otherwise sample` will be Array[Object] for some reason val sample = List( List("Estonia"): Seq[String], Array("Tallinn", "Estonia"): Seq[String], List("Tallinn", "Harju", "Estonia"): Seq[String]) println(sample.sorted)

后来我尝试运行这段代码:

scala.MatchError: (WrappedArray(Estonia, Tallinn),List(Estonia)) (of class scala.Tuple2)

这样编译得很好,但在运行时会导致以下错误:

Ordering

- 虽然我完全理解错误的原因,但我无法理解(或至少接受)的是,如果Seq[Seq[T]]在所有doCompare上成功定义,但表面上有效但显然不兼容(就Seq[Seq[T]]的定义而言)Array[List[String] | Array[String]](即Seq[Seq[T]])尝试使用该排序不会产生静态类型错误甚至警告。

这是否是因为模式匹配代码未经过静态验证以涵盖所有可能的"实例" List并且只处理P.S.案件?如果是,那么在这种情况下实现类型安全的当前可用解决方法是什么? Scalaz还有什么需要重新审视才能得到合适的解决方案吗?

Seq[Seq[T]]我知道通过不使用模式匹配并诉诸headtail,我可以轻松取消适用于所有case的解决方案使用if-else块(或{{1}}语句,如果警卫,这只是表面上更好),但我热衷于学习如何充分利用Scala的类型功能(AFAIK F#和Haskell早餐捕获这些错误);更不用说模式匹配更加优雅和可读。

3 个答案:

答案 0 :(得分:2)

这可能更接近:

scala> def cmp[A, B[_] <: Seq[_]](xs: B[A], ys: B[A]) = (xs, ys) match { case (Nil, Nil) => true }
<console>:7: error: pattern type is incompatible with expected type;
 found   : scala.collection.immutable.Nil.type
 required: B[?A1] where type ?A1 (this is a GADT skolem)
       def cmp[A, B[_] <: Seq[_]](xs: B[A], ys: B[A]) = (xs, ys) match { case (Nil, Nil) => true }
                                                                           ^

当类型参数在类而不是方法上时,存在已知的综合症。

最近已经出现在ML上。 Here.

比较:

scala> (null: Seq[_]) match { case _: Nil.type => true }
scala.MatchError: null
  ... 33 elided

scala> (null: List[_]) match { case _: Nil.type => true }
<console>:8: warning: match may not be exhaustive.
It would fail on the following input: List(_)
              (null: List[_]) match { case _: Nil.type => true }
                   ^
scala.MatchError: null
  ... 33 elided

scala> (null: List[_]) match { case Nil => true }
<console>:8: warning: match may not be exhaustive.
It would fail on the following input: List(_)
              (null: List[_]) match { case Nil => true }
                   ^
scala.MatchError: null
  ... 33 elided

抱歉懒惰,但是下床睡觉。

答案 1 :(得分:1)

Scala编译器仅为sealed类型生成非详尽匹配警告,Seq不是Seq。 AFAIK,没有办法强迫它检查,就像尾递归一样。

  

(AFAIK F#和Haskell在早餐时发现这些错误)

Haskell没有等同于List;您曾经使用IEnumerable<T>的代码是等同于Haskell(模数懒惰)的代码,而Scala 在这种情况下会捕获错误。我不太了解F#,但是看http://msdn.microsoft.com/en-us/library/dd547125.aspx,似乎不支持匹配普通{{1}}的模式。

答案 2 :(得分:0)

使用代码:

    implicit def ReverseListOrdering[T: Ordering] = new Ordering[Seq[T]] {
            override def compare(xs1: Seq[T], xs2: Seq[T]) =
            doCompare(xs1.reverse, xs2.reverse)

    private def doCompare(xs1: Seq[T], xs2: Seq[T]): Int = (xs1, xs2) match {
            case (Seq(), Seq()) => 0
            case (x +: _, Seq()) => 1
            case (Seq(), x +: _) => -1
            case (x +: xs, y +: ys) =>
            val a = implicitly[Ordering[T]].compare(x, y)
            if (a != 0) a else doCompare(xs, ys)
            }
   }

正如我所提到的,评论Nil与Seq()的类型不同。例如,WrappedArray不是List。当您使用x :: xs时 - 它与List匹配。 Array("Tallinn", "Estonia")转换为WrappedArray。使用Seq

时,始终在模式匹配中使用+: