Scala类型系统如何知道cons + Nil是详尽无遗的?

Bri*_*don 4 type-systems scala algebraic-data-types

我刚写了这个函数,想知道如果我省略了Nil的情况会发生什么,并注意到scalac给了我一个警告:

def printList[String](list: List[String]) {
    list match {
        case head :: tail => {
            println(head)
            printList(tail)
        }
        //case Nil => println("Done")
    }
}
Run Code Online (Sandbox Code Playgroud)

Warning: match may not be exhaustive. It would fail on the following input: Nil

我无法确切地确定这里发生了什么.我在递归数据类型上得到了模式匹配的一般概念,直到你耗尽了案例,但我不清楚它是如何映射到Scala类型系统的.具体来说,我正在查看Scala标准库的源代码,并想知道:

  1. 在代码的确切位置Scala是否认为需要一个基本案例来完成List类实例的匹配语句?人们当然可以想象一种代数数据类型"只是继续"而没有基本情况.
  2. 在代码中究竟是哪个scala.collection.immutable.Nil特别指定为List类的基本情况?

Dan*_*mon 7

它实际上并不像你想象的那么复杂. List是sealed abstract class正好用两个实现,Nil和::(是的,这是类的名称).这里重要的部分是sealed修饰语.这只是强制实现任何实现的类List 必须与List它自己在同一个源文件中.

重要的sealed是,现在编译器确实知道每个可能的实现者List,因此如果您对列表进行模式匹配,编译器可以确定您的模式匹配块是否是详尽的.

最后要意识到的是,有一些语法糖正在进行中::.通常,如果你有一些案例类:

case class Foo(a: String, b: Int)
Run Code Online (Sandbox Code Playgroud)

你会匹配它

x match {
  case Foo(a, b) => //...
}
Run Code Online (Sandbox Code Playgroud)

但是,当您的案例类只包含两个成员时,您也可以这样写:

x match {
  case a Foo b => //...
}
Run Code Online (Sandbox Code Playgroud)

所以在你的模式匹配语句中,你真的在​​做:

list match {
        case ::(head, tail) => {
Run Code Online (Sandbox Code Playgroud)

所以你真正要做的就是检查列表是否是一个实例::.因此,编译器可以看到您从不检查列表是否是实例Nil并向您发出警告.