【问题标题】:Is the concept of Algebraic Data Type akin to Class definitions in OO languages?代数数据类型的概念是否类似于 OO 语言中的类定义?
【发布时间】:2023-04-04 17:50:01
【问题描述】:

这两个概念都允许创建新的数据类型。 我能看到的唯一区别是,在函数式语言中,可以对代数数据类型执行模式匹配。但是 OO 语言没有类似的简洁特性。这是一个准确的说法吗?

【问题讨论】:

    标签: functional-programming algebraic-data-types


    【解决方案1】:

    我可以看到代数数据类型和 OO 样式类之间的三个主要区别,不包括(im)可变性,因为它会有所不同。

    • 代数数据类型允许求和和乘积,而 OO 风格的类只允许乘积。
    • OO 风格的类允许您将复杂数据项与其可接受的操作捆绑在一起,而代数数据类型则不能。
    • 代数数据类型不区分传递给构造函数的数据和存储在结果值中的数据,而 OO 样式的类可以(或可以)。

    我故意从该列表中遗漏的一件事是子类型化。虽然绝大多数 OO 语言允许您对(非最终、非密封、当前可访问)类进行子类化,而绝大多数一般 ML 系列函数式语言不允许,但显然可以在假设的情况下完全禁止继承OO(或至少类似 OO)语言,同样可以在代数数据类型中产生子类型和超类型;有关后者的有限示例,请参阅this page on O'Haskell,已由Timber 继承

    【讨论】:

    • 所以ADT不需要关闭? Haskell 的 ADT 不允许子类型化(通过 blog.tmorris.net/algebraic-data-types-again
    • @BonAmi 不,不需要关闭 ADT。正如您所指出的,Haskell 是,但 Scala 肯定不是(如果您可以将案例类称为 ADT)。
    【解决方案2】:

    类不仅仅是一个类型定义——在大多数 OO 语言中,类实际上是提供各种松散相关功能的厨房水槽功能。

    特别是,类充当一种模块,为您提供数据抽象和命名空间。代数数据类型没有内置此功能,模块化通常作为单独的正交特征(通常是模块)提供。

    【讨论】:

      【解决方案3】:

      从某种意义上说,人们可以这样看。每种语言只有这么多的机制来创建用户定义的类型。在函数式(ML,Haskell 风格)语言中,唯一的一个就是创建 ADT。 (Haskell 的 newtype 可以看作是 ADT 的退化案例)。在 OO 语言中,它是类。在程序语言中,它是structrecord

      不言而喻,用户定义数据类型的语义因语言而异,从范式#1 中的语言到范式#2 中的语言更是如此。 @Pharien's Flame 已经概述了典型的差异。

      【讨论】:

        【解决方案4】:

        Algebraic data types 之所以如此命名,是因为它们构成了一个“初始代数”,

        + represents sum types (disjoint unions, e.g. Either).
        • represents product types (e.g. structs or tuples)
        X for the singleton type (e.g. data X a = X a)
        1 for the unit type ()
        and μ for the least fixed point (e.g. recursive types), usually implicit.
        

        从这些运算符可以构造所有常规数据类型。 代数数据类型还支持参数多态性——这意味着它们可以用作任何底层类型的容器,并具有静态的安全保证。此外,ADT 提供了用于引入和消除数据类型的统一语法(通过构造函数和模式匹配)。例如

        -- this defines a tree
        data Tree a = Empty | Node a (Tree a) (Tree a)
        
        -- this constructs a tree
        let x = Node 1 (Node 2 Empty) Empty
        
        -- this deconstructs a tree
        f (Node a l r) = a + (f l) + (f r)
        

        代数数据类型的丰富性和统一性,以及它们不可变的事实,将它们与 OO 对象区分开来,这主要是:

        • 仅表示产品类型(因此没有递归或求和类型)
        • 不支持模式匹配
        • 是可变的
        • 不支持参数多态性

        【讨论】:

        • 嗯。子类化不是有点像添加类型加法吗? (所以一个类基本上是一个开和?)我也看不到参数多态性是如何与代数数据类型相关联的。当然,你可以将它们参数化为其他类型——但你不能对类也这样做吗?类型递归通常也可以,对吧?
        • 是的,通过足够的扩展,您可以实现:“泛型、子类化和虚拟分派的组合支持在面向对象的编程语言中定义和限制使用广义代数数据类型” - @987654322 @
        【解决方案5】:

        代数数据类型的概念是否类似于 OO 语言中的类定义?

        在函数式语言中,可以对代数数据类型执行模式匹配。但是 OO 语言没有类似的简洁特性。这是一个准确的说法吗?

        这是其中的一部分。

        正如 Andreas 所说,类是从 Simula 派生的静态类型的面向对象语言(如 C++、Java 和 C#)中的厨房水槽功能。类是万事通,但在这方面却一无所知:它们解决了很多问题。

        将 vanilla ML 和 Haskell 与在 C++、Java 和 C# 中看到的 OO 进行比较,您会发现:

        1. 类可以包含其他类,而代数数据类型可以相互引用,但不能包含彼此的定义。
        2. 类层次结构可以任意深,而代数数据类型只有一级深:类型包含其类型构造函数,仅此而已。
        3. 新类可以从旧类派生,因此类是可扩展类型,而代数数据类型通常(但不总是)封闭。

        因此 ADT 并不是真正“类似于”类,因为它们只解决一个特定问题:深一层的类层次结构。从这个意义上说,我们可以看到两个近似的观察结果:

        1. ADT 需要组合而不是继承。
        2. 类可以轻松扩展类型,但难以扩展成员函数集,而 ADT 可以轻松扩展类型上的函数,但难以扩展类型。

        您还可以查看 GoF 设计模式。它们已经使用 C++ 中的类来表达。功能等价物并不总是 ADT,而是诸如 lambda 函数而不是命令模式以及像 mapfold 这样的高阶函数而不是访问者模式等等。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-02-23
          • 1970-01-01
          • 1970-01-01
          • 2012-07-26
          • 1970-01-01
          • 1970-01-01
          • 2011-02-18
          相关资源
          最近更新 更多