Scala类型系统如何知道cons+;零是详尽的吗?
我刚刚编写了这个函数,想知道如果忽略Nil的情况会发生什么,并注意到scalac给了我一个警告:Scala类型系统如何知道cons+;零是详尽的吗?,scala,type-systems,algebraic-data-types,Scala,Type Systems,Algebraic Data Types,我刚刚编写了这个函数,想知道如果忽略Nil的情况会发生什么,并注意到scalac给了我一个警告: def printList[String](list: List[String]) { list match { case head :: tail => { println(head) printList(tail) } //case Nil => println("Done")
def printList[String](list: List[String]) {
list match {
case head :: tail => {
println(head)
printList(tail)
}
//case Nil => println("Done")
}
}
警告:匹配可能并不详尽。
它将在以下输入时失败:Nil
我很难确定到底发生了什么。我了解递归数据类型上的模式匹配的一般概念,直到您穷尽所有案例,但我不清楚这如何映射到Scala类型系统。具体来说,我正在查看Scala标准库的源代码,并想知道:
其实并不像你想象的那么复杂
List
是一个密封的抽象类
,有两个实现,Nil
和:
(是的,这是类的名称)。这里的重要部分是密封的
修饰符。这只要求实现List
的任何类必须与List
本身位于同一源文件中
sealed
的重要性在于,现在编译器可以肯定地知道List
的每个可能实现者,因此,如果您对列表进行模式匹配,编译器可以确定您的模式匹配块是否详尽无遗
最后要意识到的一点是,:
中存在一些语法上的糖分。通常,如果您有一些案例类:
case class Foo(a: String, b: Int)
你会像这样匹配它的
x match {
case Foo(a, b) => //...
}
但是,如果案例类正好有两个成员,也可以这样编写:
x match {
case a Foo b => //...
}
因此,在您的模式匹配声明中,您确实在做:
list match {
case ::(head, tail) => {
所以实际上,您所做的只是检查列表是否是
:
的实例。因此,编译器可以看到您从未检查列表是否为Nil
的实例,并向您发出警告。您可以查看list
的源代码。基本用例没有什么特别的地方,只是List
被声明为sealed
,然后只有两个类扩展它:
sealed abstract class List[+A] ...
case object Nil extends List[Nothing] { ... }
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] { ... }
Scala编译器可以很容易地确定密封特征或抽象类是否完全匹配,因为它可以确定单个文件中可能匹配的范围。说:
如果模式匹配的选择器是密封类的实例,则模式匹配的编译可能会发出警告,诊断给定的模式集不是详尽的,即在运行时可能会引发MatchError
Scala类型匹配实现的血淋淋的细节如下:对于
Nil
来说,这并不是说存在“基本情况”<代码>列表是密封的,因此编译器知道要在匹配中查找哪些类型,即:
和Nil
。当其中一个缺失时,这是一个相当好的暗示,表明这不是一个完整的匹配。哦,这太令人失望了。我以为它在做一些像OCaml一样的分解魔法。感谢您引用规范中的相关段落。