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

时间:2012-05-09 06:12:05

标签: functional-programming algebraic-data-types

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

5 个答案:

答案 0 :(得分:6)

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.
从这些运算符

可以构造所有常规数据类型。 代数数据类型也支持参数多态性 - 这意味着它们可以用作任何基础类型的constainer,具有静态的安全保证。此外,为ADT提供了统一的语法,用于引入和消除数据类型(通过构造函数和模式匹配)。 E.g。

-- 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对象区分开来,这主要是:

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

答案 1 :(得分:4)

我可以看到代数数据类型和OO风格类之间的三个主要区别,不计算(im)可变性,因为它有所不同。

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

我故意遗漏的一件事是分组。虽然绝大多数OO语言允许你子类化(非最终的,非密封的,当前可访问的)类,而绝大多数通常是ML系列的函数式语言都没有,但显然可以在假设中完全禁止继承。 OO(或至少类似OO)语言,同样可以在代数数据类型中产生子类型和超类型;对于后者的有限示例,请参阅this page on O'Haskell已成功的Timber

答案 2 :(得分:2)

一个类不仅仅是一个类型定义 - 大多数OO语言中的类都是厨房接收器功能,它们提供各种松散相关的功能。

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

答案 3 :(得分:2)

在某种意义上,人们可以这样看待它。每种语言都只有很多机制来创建用户定义的类型。在功能(ML,Haskell风格)语言中,唯一的一种是创建ADT。 (Haskell的新类型可以看作是ADT的退化案例)。在OO语言中,它是类。在过程语言中,它是structrecord

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

答案 4 :(得分:0)

  

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

     

在功能语言中,可以对代数数据类型执行模式匹配。但是面向对象语言没有可比的简洁功能。这是正确的说法吗?

那是它的一部分。

正如Andreas所说,类是从Simula派生的静态类型的面向对象的语言(如C ++,Java和C#)中的一个厨房接收器功能。在这方面,班级是千篇一律,但无人掌握:它们严重地解决了许多问题。

在C ++,Java和C#中比较香草ML和Haskell与OO,您会发现:

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

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

  1. ADT需要通过继承来构成。
  2. 通过类可以轻松地扩展类型,但是很难扩展成员函数集,而ADT可以轻松地在类型上扩展函数,但是很难扩展类型。

您还可以查看GoF设计模式。它们已使用C ++中的类表示。功能等效项并不总是ADT,而是lambda函数(而不是命令模式)和高阶函数(例如mapfold(而不是访问者模式)之类的东西。