想象一种不允许数据类型使用多个值构造函数的语言。而不是写
data Color = White | Black | Blue
我们会有
data White = White
data Black = Black
data Blue = Black
type Color = White :|: Black :|: Blue
在哪里
:|:
(这里不是 |
以避免与 sum 类型混淆)是一个内置类型联合运算符。模式匹配将以相同的方式工作show :: Color -> String
show White = "white"
show Black = "black"
show Blue = "blue"
如您所见,与副产品相比,它产生了扁平结构,因此您不必处理注入(inject)。而且,与 sum 类型不同,它允许随机组合类型,从而获得更大的灵 active 和粒度:
type ColorsStartingWithB = Black :|: Blue
我相信构造递归数据类型也不是问题
data Nil = Nil
data Cons a = Cons a (List a)
type List a = Cons a :|: Nil
我知道联合类型存在于 TypeScript 和其他语言中,但为什么 Haskell 委员会选择 ADT 而不是它们?
最佳答案
Haskell 的 sum 类型与您的 :|:
非常相似。 .
两者的区别在于 Haskell 求和类型 |
是一个标记的联合,而你的“总和类型”:|:
未标记。
标记意味着每个实例都是唯一的 - 你可以区分 Int | Int
来自 Int
(实际上,这适用于任何 a
):
data EitherIntInt = Left Int | Right Int
在这种情况下:
Either Int Int
包含比 Int
更多的信息因为可以有 Left
和 Right
Int
.在您的
:|:
,您无法区分这两个:type EitherIntInt = Int :|: Int
你怎么知道是左还是右
Int
?有关以下部分的扩展讨论,请参阅评论。
标记联合还有另一个优点:编译器可以验证您作为程序员是否处理了所有情况,这对于一般的未标记联合来说是依赖于实现的。您处理了
Int :|: Int
中的所有案件吗? ?这要么与 Int
同构根据定义或编译器必须决定哪个 Int
(左或右)选择,如果它们无法区分,这是不可能的。考虑另一个例子:
type (Integral a, Num b) => IntegralOrNum a b = a :|: b -- untagged
data (Integral a, Num b) => IntegralOrNum a b = Either a b -- tagged
什么是
5 :: IntegralOrNum Int Double
在未标记的联盟中?它都是 Integral
的一个实例和 Num
,所以我们不能确定,必须依赖实现细节。另一方面,标记的联合确切地知道5
应该是因为它被标记为 Left
或 Right
.至于命名:Haskell 中的不相交联合是联合类型。 ADT 只是实现这些的一种手段。
关于haskell - 为什么我们需要 Sum 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40620913/