【问题标题】:How does the Scala type system know that cons + Nil is exhaustive?Scala 类型系统如何知道 cons + Nil 是详尽无遗的?
【发布时间】:2015-05-05 02:47:52
【问题描述】:

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

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

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 类的基本情况?

【问题讨论】:

  • Scala 类型匹配实现的血腥细节在这里:lampwww.epfl.ch/~emir/written/…
  • 对于Nil,这与“基本情况”无关。 List 是密封的,因此编译器知道要在匹配中查找哪些类型,即::Nil。当其中一个缺失时,这是一个相当好的暗示,它不是一个详尽的匹配。

标签: scala type-systems algebraic-data-types


【解决方案1】:

其实并没有你想象的那么复杂。 List 是一个 sealed abstract class,恰好有两个实现,Nil::(是的,这是类的名称)。这里重要的部分是sealed 修饰符。这只是强制实现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) => {

所以实际上你所做的只是检查列表是否是:: 的一个实例。因此,编译器可以看到您永远不会检查 list 是否是 Nil 的实例并警告您。

【讨论】:

    【解决方案2】:

    您可以查看List here 的源代码。基本情况没有什么特别的,只是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 编译器可以很容易地确定密封特征或抽象类是否完全匹配,因为它可以确定单个文件中可能匹配的范围。 Section 8.4 of the Scala specification 说:

    如果模式匹配的选择器是密封类的实例,则模式匹配的编译会发出警告,诊断给定的模式集并不详尽,即MatchError 可能存在在运行时引发。

    【讨论】:

    • 哦,真令人失望。我以为它在做某种分解魔法,比如 OCaml。感谢您引用规范中的相关段落。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多