为什么我们需要"代数数据类型"?

Fre*_*ind 12 haskell scala algebraic-data-types

我已经阅读了代数数据类型的一些解释:

这些文章提供了非常详细的描述和代码示例.

起初我认为代数数据类型只是用于轻松定义某些类型,我们可以将它们与模式匹配相匹配.但在阅读完这些文章之后,我发现那里甚至没有提到"模式匹配",内容看起来很有趣,但比我预期的要复杂得多.

所以我有一些问题(这些文章没有回答):

  • 为什么我们需要它,比如Haskell或Scala?
  • 如果我们拥有它,我们能做什么,如果我们没有它,我们不能做什么?

dk1*_*k14 10

我们应该从Haskell wiki文章Algebraic Data Types开始

在这里,不久,只是我的愿景:

  • 我们需要它们以旧的面向对象的方式(或实际上以旧的基于类别的方式)对业务逻辑进行建模,并使其更加类型安全,因为编译器可以检查您是否匹配了所有可能的选择.或者,换句话说,你的功能是全部的,而不是部分的.最终,它使编译器能够证明代码的正确性(这就是为什么建议使用密封特性).所以,你拥有的类型越多越好 - 顺便说一下,泛型编程可以帮助你,因为它可以产生更多类型.
  • 标准特征:我们可以将类型表示为对象的"集合",我们可以将对象与类型匹配(使用模式匹配),甚至可以使用匹配器对其进行解构(分析).我们还可以使用类型classess动态地将行为(在编译时)添加到此类型.对于常规类也是可能的,但是在这里它使我们能够将代数模型(类型)与行为(函数)分开
  • 我们可以将类型构造为不同对象/类型的产品/副产品.您可以将代数类型系统视为一组(或更一般地 - 作为笛卡尔封闭类别).type Boolean = True | False意味着是布尔的联合(副产物)TrueFalse.Cat(height: Height, weight: Weight)是一个"元组"(更普遍的 - 产品)HeightWeight.产品(更多)表示来自OOP,"联合产品"的"部分"关系 - "是"(但以相反的方式).

它还为我们提供了一种以多方式方式在运行时调度行为的方法(就像它在CLOS中一样):

  sealed trait Animal
  case class Cat extends Animal
  case class Dog extends Animal

 def move(c: Animal) = c match {
   case Cat(...) => ...
   case Dog(...) =>
   case a: Animal => ...//if you need default implementation
 }
Run Code Online (Sandbox Code Playgroud)

哈斯克尔样:

 data Animal = Dog | Cat //coproduct

 let move Dog d = ...
 let move Cat c = ...
Run Code Online (Sandbox Code Playgroud)

代替:

trait Animal {
  ...
  def move = ...
}

class Cat(val ...) extends Animal {
  override def move = ...
}

class Dog(val ...) extends Animal {
  override def move = ...
}
Run Code Online (Sandbox Code Playgroud)

PS理论上,如果你用代数方式对世界进行建模,并且你的功能是完全和纯粹的 - 你可以证明你的应用是正确的.如果它编译 - 它工作:).

PS2.我还应该提一下,Scala的类型层次结构Any对于类型安全来说并不是那么好(但是对于Java和IO的互操作很好),因为它打破了GADT定义的漂亮结构.更多的case class可能是GADT(代数)和ADT(抽象),这也减少了保证.