scala的"case class"中的"case"这个词是什么意思?

What is the meaning of the word "case" in scala's "case class"?

我知道 case class 会导致编译器增加 class 样板文件以实现某种有用的模式(“plain and immutable data-holding objects that should exclusively depend on their constructor arguments”)。

但是 "case" 这个词本身在这种情况下对我来说没有任何意义。我习惯于将它用作 C# 中 switch 语句的一部分,但这似乎与 Scala 对该词的使用无关。

当我可以将单词附加到特定含义时,我发现编程更容易。现在我的心智模型是 case => boilerplate,就像它可能是 blurg => boilerplate 一样。这是一个有效的心智模型,但模棱两可很容易让人误解或完全忘记。

那么 case 这个词与编译器实际执行的操作有什么关系?

我不是在寻找 "what was in the mind of the designers" 而是 "what's a rational way to relate this term to its meaning, given general knowledge of the language's design."

C# 具有 switch / case 语言功能,允许通过将输入与一组可能的值匹配来控制程序流。

public static GetEmail(string name)
{
  switch (name)
  {
    case "bill":
      return "bill@example.com";
    case "jane":
      return "jane@example.com";
    default:
      return null;
  }
}

这里,case大致就是"in the case that the given value is equal to this one, do X."

Scala 的 match / case 功能有点像 C# 的功能。

def getEmail(name: String): Option[String] = {
  name match {
    case "bill" => Option("bill@example.com")
    case "jane" => Option("jane@example.com")
    case _ => None
  }
}

但它更强大。它旨在针对比字符串更复杂的事物评估 "matches"。要利用这个强大的匹配功能,您可以使用 case class.

定义不可变数据保存 classes

这里有一个简单但希望有用的案例示例 class 及其与 match / case 的使用:

case class Person(name: String, hasEmail: Boolean)

object EmailHelper {
  def getEmail(person: Person): Option[String] = {
    person match {
      case Person(_, false) => None
      case Person("bill", true) => Option("bill@example.com")
      case Person("jane", true) => Option("jane@example.com")
      case _ => None
    }
  }
}

简而言之,case class 强制执行一组约束,使 class 可用于 match / case

在我看来,case这个词来自case analysis which is a reasoning technique enabled by special structures called algebraic data typescase class 中的 case 本身可能没有多大意义,但是当它构成 sealed 结构的一部分时,这就是 Scala 定义 ADT 的方式,例如

sealed trait Number
case object Zero extends Number
case class Succ(v: Number) extends Number

然后我们看到有两种形式构造Numbers,即使用ZeroSucc构造函数。因此,每当我们必须考虑 Numbers 时,我们至少知道有两个不同的 cases 需要考虑。例如,假设我们要在 Numbers 上定义加法,那么它的定义将不得不处理两种情况,也许像这样

def sum(a: Number, b: Number): Number =
  a match {
    case Zero => b
    case Succ(v) => Succ(sum(v, b))
  }

哪里

sum(Succ(Zero), Succ(Zero)) == Succ(Succ(Zero)) // 1 + 1 = 2
sum(Succ(Succ(Zero)), Succ(Zero)) == Succ(Succ(Succ(Zero))) // 2 + 1 = 3
sum(Succ(Zero), Succ(Succ(Zero))) == Succ(Succ(Succ(Zero))) // 1 + 2 = 3
sum(Zero, Succ(Succ(Zero))) == Succ(Succ(Zero)) // 0 + 2 = 2
sum(Succ(Succ(Zero)), Zero) == Succ(Succ(Zero)) // 2 + 0 = 2

请注意 Scala 如何使用 classobjecttrait 等术语来定义 ADT,这些术语似乎来自面向对象的范式,但从概念上讲是 ADT与 OO 中的 class 层次结构几乎没有共同之处。我个人觉得这令人困惑,但我们必须记住 Scala 既是函数式又是 OO 语言,这可能是这种术语溢出的原因。在其他一些"pure"语言中,ADT的case class简单地用竖线表示|,这样说

Inductive nat : Type :=
   | O : nat
   | S : nat -> nat.

我的建议是尽量不要成为 "slave to words" 而是文字应该为您服务。重要的是单词或术语背后的含义,而不是单词本身。不要围绕术语建立心智模型,而是围绕他们难以通过微弱的沟通桥梁进行的沉重概念建立心智模型。在我看来,case 试图传达的概念是 ADT。