代数数据类型的用途是什么?


16

我正在阅读有关代数数据类型的信息(感谢Richard Minerich,我对这个概念有了很好的解释)。虽然我认为我了解求和类型和乘积类型等的概念,但我不太了解的是代数数据类型在指定模式匹配之外如何有用。ADT的超出模式匹配功能还能做什么?


编辑:我不是在问开发人员可以用对象无法完成的ADT做些什么。我问是否还有ADT允许的其他操作;例如,如果使用ADT,可以对涉及的类型进行其他推理吗?如果没有它们,ADT是否可以促进某种类型的分析?


2
除了调用方法外,您还可以使用对象做什么?

1
ADT实际上是指“抽象数据类型”,而不是代数数据类型。
Rein Henrichs

4
@Rein:它可以根据上下文进行引用。
sepp2k 2011年

4
@Rein:确实的(老实说,这让我感到非常惊讶):但是ADT的维基百科文章同时列出了Abstract Data Type和Algebraic Data Type作为可能的含义。ADT非常常用作Haskell邮件列表和IRC通道等代数数据类型的缩写。
sepp2k 2011年

1
@Rein,我知道-只是厌倦了一遍又一遍地输入“代数数据类型”,而且我认为人们可以理解给定上下文的含义。
Onorio Catenacci

Answers:


10

代数数据类型的不同之处在于它们可以由多种类型的“事物”构造而成。例如,树可以不包含任何内容(空),叶或节点。

data Tree = Empty
          | Leaf Int
          | Node Tree Tree

由于节点由两棵树组成,因此代数数据类型可以是递归的。

模式匹配允许以保持类型安全的方式来解构代数数据类型。考虑以下depth的实现及其等效的伪代码:

depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)

相比:

switch on (data.constructor)
  case Empty:
    return 0
  case Leaf:
    return 1
  case Node:
    let l = data.field1
    let r = data.field2
    return 1 + max (depth l) (depth r)

这样做的缺点是,程序员必须记住在Leaf之前将Case设置为空,以便在空树上不访问field1。同样,必须在Node事例之前声明Leaf事例,以便在Leaf上不访问field2。因此,类型安全性不是由语言维护的,而是给程序员增加了额外的认知负担。顺便说一句,我直接从Wikipedia页面上获取这些示例。

当然,鸭式语言可以做这样的事情:

class Empty
  def depth
    0
  end
end

class Leaf
  def depth
    1
  end
end

class Node
  attr_accessor :field1, :field2

  def depth
    1 + [field1.depth, field2.depth].max
  end
end

因此,代数数据类型不一定严格优于其OOP,但在构建软件时确实提供了不同的张力。


9

我不确定解释是否如此出色。

代数数据类型用于创建数据结构,例如列表和树。

例如,解析树很容易用代数数据结构表示。

data BinOperator = Add
                 | Subtr
                 | Div
                 | Mult
                 | Mod
                 | Eq
                 | NotEq
                 | GreaterThan
                 | LogicAnd
                 | LogicOr
                 | BitAnd
                 | BitOr
                 | ...

data UnOperator = Negate
                | Not
                | Increment
                | Decrement
                | Complement
                | Ref
                | DeRef


data Expression = Empty
                | IntConst Int
                | FloatConst Float
                | StringConst String
                | Ident String
                | BinOp BinOperator Expression Expression
                | UnOp UnOperator Expression Bool //prefix or not
                | If Expression Expression Expression
                | While Expression Expression Bool //while vs. do while
                | Block List<Expression>
                | Call Expression List<Expression>
                | ...

代表C语言实际上并不需要花费太多。

但实际上,您可以对代数数据类型进行所有操作。Lisp证明,您可以使用结对完成所有操作,而ADT只是为这种方法提供了更细粒度和类型安全的方法。

当然,如果您问“对于ADT,您不能对对象做什么?”,答案是“没有”。仅在某些时候(大多数情况下),您会发现ADT的解决方案冗长得多,而基于对象的解决方案则更具灵活性。因此,将其放入以ADT表示的解析树中:

If(Call(Ident('likes_ADTs'),[Ident('you')]),
   Call(Ident('use_ADTs'),[Ident('you')]),
   Call(Ident('use_no_ADTs'),[Ident('you')]))
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.