[英]How to make Scala's type system catch this MatchError?
I've defined an ordering for Seq[Seq[T]]
such that it's a normal lexicographic ordering except all items (sub-sequences) are reversed first (so that C,B,A
comes before A,B,C
but after A,B,A
): 我已经定义了Seq[Seq[T]]
的排序,以便它是正常的字典顺序,除了所有项(子序列)先反转(以便C,B,A
在A,B,C
之前A,B,C
在A,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)
}
}
This used to be defined on List[List[T]]
at first but I later realized I want it for all Seq[Seq[T]]
; 最初是在List[List[T]]
上定义的,但后来我意识到我希望对所有Seq[Seq[T]]
; this is why I initially left in the Nil
s in the pattern matching block, while failing to realize Nil
never matches eg an empty Array
. 这就是为什么我最初在模式匹配块中保留Nil
的原因,而未能意识到Nil
从不匹配,例如一个空Array
。
Later I tried to run this block of code: 后来我尝试运行以下代码块:
// 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)
This compiles just fine but results in the following error at runtime: 这样编译就可以了,但是在运行时会导致以下错误:
scala.MatchError: (WrappedArray(Estonia, Tallinn),List(Estonia)) (of class scala.Tuple2)
— while I understand the cause of the error perfectly well, what I fail to understand (or at least accept) is, if the Ordering
is successfully defined on all Seq[Seq[T]]
, yet a superficially valid but obviously incompatible (in terms of how doCompare
is defined) Seq[Seq[T]]
(ie Array[List[String] | Array[String]]
) attempt to use that ordering results in no static type errors or even warnings whatsoever. —尽管我非常清楚地理解了错误的原因,但我无法理解(至少是接受)的是,如果在所有Seq[Seq[T]]
上都成功定义了Ordering
,但表面上有效,但显然不兼容(在有关如何定义doCompare
条款) Seq[Seq[T]]
(即Array[List[String] | Array[String]]
))尝试使用该顺序不会导致任何静态类型错误甚至警告。
Is this caused by the fact that the pattern matching code is not statically verified to cover all possible "instances" of Seq[Seq[T]]
and that it only handles the List
case? 这是由于模式匹配代码未经过静态验证以覆盖Seq[Seq[T]]
所有可能“实例”并且仅处理List
情况的事实引起的吗? If yes, what are the currently available workarounds to achieving type safety in such cases? 如果是,那么在这种情况下实现类型安全的当前可用的解决方法是什么? Is Scalaz something to be looked at yet again for a decent solution? Scalaz是否需要再次考虑以寻求一个体面的解决方案?
PS
I'm aware I could easily do away with a solution that works for all Seq[Seq[T]]
by not using pattern matching and resorting to head
and tail
with an if-else block (or case
statements if guards, which is only superficially nicer), but I'm keen to learn how to make the most of out Scala's type capabilities (AFAIK F# and Haskell catch these errors for breakfast); PS
我知道我可以通过不使用模式匹配并使用if-else块(或case
语句,如果有后卫)不使用模式匹配和head
和tail
来轻松解决适用于所有Seq[Seq[T]]
的解决方案只是表面上更好),但我很想学习如何充分利用Scala的类型功能(AFAIK F#和Haskell可以在早餐时捕获这些错误); not to mention pattern matching is by far more elegant and readable. 更不用说模式匹配了,更优雅,更易读。
This might be closer: 这可能更接近:
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 }
^
There is a known syndrome when the type parameter is on the class instead of the method. 当类型参数位于类而不是方法上时,存在一个已知的综合症。
It has surfaced on the ML recently. 它最近在ML上浮出水面。 Here. 这里。
Comparing to: 比较:
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
Sorry to be lazy, but off to bed. 对不起,我很懒,但是上床睡觉了。
Scala compiler produces non-exhaustive match warnings only for sealed
types, which Seq
isn't. Scala编译器仅针对sealed
类型生成非穷举的匹配警告,而Seq
不会。 AFAIK, there is no way to force it to check, like there is for tail recursion. AFAIK,没有办法强制检查它,就像尾部递归一样。
(AFAIK F# and Haskell catch these errors for breakfast) (AFAIK F#和Haskell在早餐时发现了这些错误)
Haskell doesn't have an equivalent to Seq
; Haskell没有Seq
的等效项; the code you used to have with List
is the one which is equivalent to Haskell (modulo laziness), and Scala does catch the error in this case. 与List
一起使用的代码是等效于Haskell(模懒度)的代码,在这种情况下Scala 确实捕获了错误。 I don't know F# well, but looking at http://msdn.microsoft.com/en-us/library/dd547125.aspx it seems that pattern matching a general IEnumerable<T>
isn't supported. 我不太了解F#,但是从http://msdn.microsoft.com/zh-cn/library/dd547125.aspx看,似乎不支持与常规IEnumerable<T>
匹配的模式。
Use code: 使用代码:
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)
}
}
As I mention is comment Nil is not the same type as Seq(). 正如我提到的那样,注释Nil与Seq()的类型不同。 For example WrappedArray is not a List. 例如,WrappedArray不是列表。 And when you use x :: xs
- it is matched as List. 当您使用x :: xs
-它与列表匹配。 Array("Tallinn", "Estonia")
is converted to WrappedArray. Array("Tallinn", "Estonia")
转换为WrappedArray。 Always use +:
in pattern matching when you use Seq 使用Seq时,请始终在模式匹配中使用+:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.