繁体   English   中英

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

[英]How to make Scala's type system catch this MatchError?

我已经定义了Seq[Seq[T]]的排序,以便它是正常的字典顺序,除了所有项(子序列)先反转(以便C,B,AA,B,C之前A,B,CA,B,A之后) A,B,A ):

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)

—尽管我非常清楚地理解了错误的原因,但我无法理解(至少是接受)的是,如果在所有Seq[Seq[T]]上都成功定义了Ordering ,但表面上有效,但显然不兼容(在有关如何定义doCompare条款) Seq[Seq[T]] (即Array[List[String] | Array[String]] ))尝试使用该顺序不会导致任何静态类型错误甚至警告。

这是由于模式匹配代码未经过静态验证以覆盖Seq[Seq[T]]所有可能“实例”并且仅处理List情况的事实引起的吗? 如果是,那么在这种情况下实现类型安全的当前可用的解决方法是什么? Scalaz是否需要再次考虑以寻求一个体面的解决方案?

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

这可能更接近:

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上浮出水面。 这里。

比较:

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

对不起,我很懒,但是上床睡觉了。

Scala编译器仅针对sealed类型生成非穷举的匹配警告,而Seq不会。 AFAIK,没有办法强制检查它,就像尾部递归一样。

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

Haskell没有Seq的等效项; List一起使用的代码是等效于Haskell(模懒度)的代码,在这种情况下Scala 确实捕获了错误。 我不太了解F#,但是从http://msdn.microsoft.com/zh-cn/library/dd547125.aspx看,似乎不支持与常规IEnumerable<T>匹配的模式。

使用代码:

    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不是列表。 当您使用x :: xs -它与列表匹配。 Array("Tallinn", "Estonia")转换为WrappedArray。 使用Seq时,请始终在模式匹配中使用+:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM