【发布时间】:2023-04-04 17:50:01
【问题描述】:
这两个概念都允许创建新的数据类型。 我能看到的唯一区别是,在函数式语言中,可以对代数数据类型执行模式匹配。但是 OO 语言没有类似的简洁特性。这是一个准确的说法吗?
【问题讨论】:
标签: functional-programming algebraic-data-types
这两个概念都允许创建新的数据类型。 我能看到的唯一区别是,在函数式语言中,可以对代数数据类型执行模式匹配。但是 OO 语言没有类似的简洁特性。这是一个准确的说法吗?
【问题讨论】:
标签: functional-programming algebraic-data-types
我可以看到代数数据类型和 OO 样式类之间的三个主要区别,不包括(im)可变性,因为它会有所不同。
我故意从该列表中遗漏的一件事是子类型化。虽然绝大多数 OO 语言允许您对(非最终、非密封、当前可访问)类进行子类化,而绝大多数一般 ML 系列函数式语言不允许,但显然可以在假设的情况下完全禁止继承OO(或至少类似 OO)语言,同样可以在代数数据类型中产生子类型和超类型;有关后者的有限示例,请参阅this page on O'Haskell,已由Timber 继承
【讨论】:
类不仅仅是一个类型定义——在大多数 OO 语言中,类实际上是提供各种松散相关功能的厨房水槽功能。
特别是,类充当一种模块,为您提供数据抽象和命名空间。代数数据类型没有内置此功能,模块化通常作为单独的正交特征(通常是模块)提供。
【讨论】:
从某种意义上说,人们可以这样看。每种语言只有这么多的机制来创建用户定义的类型。在函数式(ML,Haskell 风格)语言中,唯一的一个就是创建 ADT。 (Haskell 的 newtype 可以看作是 ADT 的退化案例)。在 OO 语言中,它是类。在程序语言中,它是struct 或record。
不言而喻,用户定义数据类型的语义因语言而异,从范式#1 中的语言到范式#2 中的语言更是如此。 @Pharien's Flame 已经概述了典型的差异。
【讨论】:
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 对象区分开来,这主要是:
【讨论】:
代数数据类型的概念是否类似于 OO 语言中的类定义?
在函数式语言中,可以对代数数据类型执行模式匹配。但是 OO 语言没有类似的简洁特性。这是一个准确的说法吗?
这是其中的一部分。
正如 Andreas 所说,类是从 Simula 派生的静态类型的面向对象语言(如 C++、Java 和 C#)中的厨房水槽功能。类是万事通,但在这方面却一无所知:它们解决了很多问题。
将 vanilla ML 和 Haskell 与在 C++、Java 和 C# 中看到的 OO 进行比较,您会发现:
因此 ADT 并不是真正“类似于”类,因为它们只解决一个特定问题:深一层的类层次结构。从这个意义上说,我们可以看到两个近似的观察结果:
您还可以查看 GoF 设计模式。它们已经使用 C++ 中的类来表达。功能等价物并不总是 ADT,而是诸如 lambda 函数而不是命令模式以及像 map 和 fold 这样的高阶函数而不是访问者模式等等。
【讨论】: