簡體   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