1. 简介

在本教程中,我们将深入探讨 代数数据类型(Algebraic Data Types, ADT)的概念及其在 Scala 中如何用于定义数据模型

2. 什么是代数数据类型?

ADT 在 Scala 中非常常见。简单来说,任何使用“乘积类型(Product Type)”或“和类型(Sum Type)”模式定义的数据结构都可以称为代数数据类型

接下来我们要搞清楚的关键点是:什么是 Product Type 和 Sum Type

3. 乘积类型(Product Type)模式

当我们需要建模一个包含其他数据的数据结构时,就可以使用 乘积类型(Product Type) 模式。

举个例子,我们来定义一个简化版的国际象棋棋子:

case class ChessPiece(color: Color, name: Name)
val rook = ChessPiece(White, Rook)

在这个例子中,ChessPiece 是由 ColorName 组成的。后面我们会进一步展开 ColorName 的定义。

从面向对象的角度看,这相当于一种 “has-a” 关系(即组合关系)。

ChessPiece 包含了多个字段,因此它是一个典型的 Product Type。✅ 在 Scala 中,通常用 case class 来表示 Product Type。

4. 和类型(Sum Type)模式

如果我们需要定义一个可以取多种不同值的类型,就可以使用 和类型(Sum Type) 模式。

继续以国际象棋为例,我们可以这样定义 ColorName

sealed trait Color
final case object White extends Color
final case object Black extends Color

sealed trait Name
final case object Pawn extends Name
final case object Rook extends Name
final case object Knight extends Name
final case object Bishop extends Name
final case object Queen extends Name
final case object King extends Name

用面向对象的术语来说,这种关系是 “is-a”

比如上面的例子:“Color 可以是 White 或者 Black”。✅ 使用 sealed trait 是定义 Sum Type 的标准方式。

5. 更多组合模式

我们已经看到了 has-a 和 is-a 的基本用法,并结合了 and / or 操作符:

  • ChessPiece has a Color and a Name
  • Color is a White or a Black

但其实还有两种组合我们还没提到:

  • has-a + or
  • is-a + and

5.1 has-a + or 示例

下面是一个 Semaphore(信号灯)的例子,其中 Color 是一个 Sum Type:

trait Semaphore {
    val color: Color
}

sealed trait Color
final case object Green extends Color
final case object Amber extends Color
final case object Red extends Color

这段代码的意思是:“Semaphore 有一个 Color,而 Color 可以是 Green、Amber 或 Red”。

5.2 is-a + and 示例

再来看一个“is-a + and”的例子:

trait Feline
trait Animal
trait Cat extends Animal with Feline

这段代码表达的是:“Cat 既是一个 Animal,也是一个 Feline”。

⚠️ 这种多重继承在 Scala 中通过 trait 实现,属于 Mixin 模式的一种应用。

6. 模式匹配(Pattern Matching)

一旦使用了 ADT,模式匹配(Pattern Matching)就成了自然而然的选择,它鼓励我们采用函数式编程风格。

举个例子,我们可以写一个方法判断某个棋子是否是最关键的那个(国王 King):

def isTheMostImportantPiece(c: ChessPiece): Boolean = c match {
    case ChessPiece(_, King) => true
    case _ => false
}

✅ 模式匹配在这里简直是天作之合,完美契合 ADT 的设计思想。

7. 小结

在这篇文章中,我们学习了如何将对象之间的关系转化为 Scala 代码中的 ADT 表达形式。✅ 掌握这些模式并能在实际项目中识别它们,是写出地道函数式代码的关键一步。

一如既往,所有示例代码都可以在 GitHub 上找到。


原始标题:Algebraic Data Types in Scala