我正在阅读有关代数数据类型的信息(感谢Richard Minerich,我对这个概念有了很好的解释)。虽然我认为我了解求和类型和乘积类型等的概念,但我不太了解的是代数数据类型在指定模式匹配之外如何有用。ADT的超出模式匹配功能还能做什么?
编辑:我不是在问开发人员可以用对象无法完成的ADT做些什么。我问是否还有ADT允许的其他操作;例如,如果使用ADT,可以对涉及的类型进行其他推理吗?如果没有它们,ADT是否可以促进某种类型的分析?
我正在阅读有关代数数据类型的信息(感谢Richard Minerich,我对这个概念有了很好的解释)。虽然我认为我了解求和类型和乘积类型等的概念,但我不太了解的是代数数据类型在指定模式匹配之外如何有用。ADT的超出模式匹配功能还能做什么?
编辑:我不是在问开发人员可以用对象无法完成的ADT做些什么。我问是否还有ADT允许的其他操作;例如,如果使用ADT,可以对涉及的类型进行其他推理吗?如果没有它们,ADT是否可以促进某种类型的分析?
Answers:
代数数据类型的不同之处在于它们可以由多种类型的“事物”构造而成。例如,树可以不包含任何内容(空),叶或节点。
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,但在构建软件时确实提供了不同的张力。
我不确定解释是否如此出色。
代数数据类型用于创建数据结构,例如列表和树。
例如,解析树很容易用代数数据结构表示。
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')]))