Scala 为什么我们需要“我们需要什么?”;代数数据类型;?

Scala 为什么我们需要“我们需要什么?”;代数数据类型;?,scala,haskell,algebraic-data-types,Scala,Haskell,Algebraic Data Types,我已经阅读了一些代数数据类型的解释: 这些文章给出了非常详细的描述和代码示例 起初我认为代数数据类型只是为了方便地定义一些类型,我们可以用模式匹配来匹配它们。但是在阅读了这些文章之后,我发现这里甚至没有提到“模式匹配”,内容看起来很有趣,但比我预期的要复杂得多 因此,我有一些问题(这些文章没有回答): 为什么我们需要它,比如说,在哈斯克尔或斯卡拉 如果我们拥有它,我们能做什么,如果我们没有它,我们不能做什么 我们应该从Haskell的wiki文章开始 很快,这里就是我的愿景: 我

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

这些文章给出了非常详细的描述和代码示例

起初我认为代数数据类型只是为了方便地定义一些类型,我们可以用模式匹配来匹配它们。但是在阅读了这些文章之后,我发现这里甚至没有提到“模式匹配”,内容看起来很有趣,但比我预期的要复杂得多

因此,我有一些问题(这些文章没有回答):

  • 为什么我们需要它,比如说,在哈斯克尔或斯卡拉
  • 如果我们拥有它,我们能做什么,如果我们没有它,我们不能做什么

我们应该从Haskell的wiki文章开始

很快,这里就是我的愿景:

  • 我们需要它们以旧的面向对象的方式(或者实际上是以旧的基于类别的方式)对业务逻辑进行建模,并使其更加类型安全,因为编译器可以检查您是否匹配了所有可能的选择。或者,换句话说,你的功能是,不是局部的。最终,它使编译器能够证明代码的正确性(这就是为什么建议使用密封特性)。因此,您拥有的不同类型越多越好-顺便说一句,泛型编程在这里帮助您,因为它会生成更多类型
  • 标准特性:我们可以将类型表示为对象的“集合”,我们可以将对象与类型匹配(使用模式匹配),甚至用匹配器解构(分析)。我们还可以使用类型classess动态地向此类类型添加行为(在编译时)。对于常规类也是可能的,但在这里,它使我们能够将代数模型(类型)与行为(函数)分开
  • 我们可以构造不同对象/类型的类型。您可以将代数类型系统视为一个集合(或者更一般地,视为笛卡尔闭范畴)
    type Boolean=True | False
    表示布尔值是
    True
    False
    的并集(余积)
    Cat(身高:身高,体重:体重)
    height
    weight
    的“元组”(更一般地说是产品)。产品(更多更少)表示OOP的“部分”关系,副产品-“是”(但方式相反)
它还为我们提供了一种在运行时以多方法风格调度行为的方法(就像在中一样):

哈斯克尔喜欢:

 data Animal = Dog | Cat //coproduct

 let move Dog d = ...
 let move Cat c = ...
而不是:

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

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

class Dog(val ...) extends Animal {
  override def move = ...
}
从理论上讲,如果你用代数的方式对世界建模,并且你的函数是完整的和纯的,你可以证明你的应用是正确的。如果它可以编译,它就可以工作:)


p.S.2.我还应该提到,Scala的类型层次结构(其中包含
Any
)不利于类型安全(但有利于与Java和IO的互操作),因为它打破了GADT定义的良好结构。不仅如此,
case类
还可能是GADT(代数)和ADT(抽象),这也减少了保证。

您提到的博客文章更多地是关于代数数据类型的数学方面,以及它们在编程中的实际使用。我认为大多数函数式程序员最初是通过在一些编程语言中使用代数数据类型来学习代数数据类型的,后来才研究它们的代数属性

事实上,这篇博文的意图从一开始就很清楚:

在本系列文章中,我将解释为什么Haskell的数据类型是 称为代数-不提及范畴理论或高级 数学

无论如何,代数类型的实用性最好通过使用它们来欣赏

例如,假设要编写一个函数,使平面上的两段相交

def intersect(s1: Segment, s2: Segment): ???
结果应该是什么?写作很有诱惑力

def intersect(s1: Segment, s2: Segment): Point
但是如果没有交叉口呢?您可以尝试通过返回
null
,或抛出
NoIntersection
异常,来修补这种情况。但是,当两条线段位于同一条直线上时,它们也可能在多个点上重叠。在这种情况下,我们应该怎么做?抛出另一个异常

代数类型方法是设计一种涵盖所有情况的类型:


case对象NoIntersection扩展交叉点
案例类单点(p:Point)延伸交点
案例类分段部分(s:分段)延伸交点
def相交(s1:线段,s2:线段):相交

在许多实际案例中,这种方法感觉很自然。在一些缺乏代数类型的其他语言中,必须求助于异常、
null
s(另请参见)、非密封类(使编译器无法检查模式匹配的详尽性)或语言提供的其他特性。可以说,OOP中的“最佳”选项是使用来编码代数类型和模式匹配的语言,而这些语言没有这样的功能。尽管如此,在scala语言中直接支持这一点还是方便得多。

您希望元组返回多个结果,并且希望sum类型做出显式选择。(当然,这只是沧海一粟——你可以写关于这方面的书)。你给出的链接可能有误导性——你可能会认为你只需要这些东西来处理象牙塔的阿肯色魔法,但这实际上只是一种以理智的方式管理你的数据的方法(特别是如果你没有可以使用的类、继承和变异引用)@Carsten我觉得你理解我的困惑。感谢您的评论,希望您能说得更多:)只是为了表示警告,缩写词“ADT”也用于表示“抽象数据类型”,这是与“代数数据类型”相反的概念。因此,应谨慎使用缩写词“ADT”,也就是说,一点也不。@pigworker谢谢,fixed如果你想知道人们为什么需要数字,不要从数论课程开始。它是w
def intersect(s1: Segment, s2: Segment): Point