data Apple = Gala | Fuji | PinkLady
data Orange = Navel | Blood
data Berry = Blueberry | Cranberry | Raspberry
data Fruit = Apple Apple
| Orange Orange
| Berry Berry
这里,Fruit 是 Apple、Orange 和 Berry 的副产品1。
请注意,未标记的联合不是副产品。
1:嗯,有点。 Fruit 还包含一个额外的元素 ⊥。见下文。
对已编辑问题的回复
data Shape = Either Circle Rectangle
你的意思可能是:
type Shape = Either Circle Rectangle
如果您使用data,则您定义了一个产品类型,其中包含一个名为Either 的构造函数。这是完全合法的。如果您使用type,您将Shape 定义为Either Circle Rectangle 的另一个名称,它是Circle 和Rectangle 的联产品。
Hask 与 Set
我们把Haskell中的类型和函数的范畴叫做Hask。这是它的常用名称。它确实符合类别的定义,假设您没有仔细研究这些我们称为计算机的有限事物。
让我们将 Hask 与类别 Set 进行比较。这是很自然的,因为 Hask 是一个具体的类别。比较 Hask 中的 (,) 类型构造函数和 Set 中的笛卡尔积。如果我们想要Int 和Int 的乘积,我们得到:
-
⊥ ∈ (Int, Int)(在 Hask 中),但是
-
⊥ ∉ Int ⨯ Int(在集合中)。
所以你可以看到类型构造函数(,)与笛卡尔积不一样,因为它包含一个额外的成员⊥。我们可以重复不相交并集的论点:
-
⊥ ∈ Either Int Int(在 Hask 中),但是
-
⊥ ∉ Int ⊔ Int(在集合中)。
在每种情况下,Hask 中的结构都包含一个附加元素 ⊥,而 Set 中的等效结构则没有。
Hask 与 Pointed 集
Hask 也不是指向集合的范畴。首先,Hask 包含非指向集态射的态射。
-
对于 Hask 中的每个类型T,我们可以构造一个函数T -> T,使得f x = ⊥ 对应所有x。因此,⊥ 必须是基点,如果 Hask 中的对象是指向集的话。请注意,所有此类f 都是严格函数。
-
但是,让g 成为任何惰性(这里的正确术语实际上是“非严格”)函数。根据严格的定义,g ⊥ ≠ ⊥。但是,对于 #1,这与 Hask 是指向集的范畴的前提相矛盾。
此外,产品和副产品的结构是不同的,类似于结构与 Set 的结构不同的方式。对于产品,
-
(⊥, ⊥) ∈ (Int, Int)(在 Hask 中),但是
-
(⊥, ⊥) ∉ Int ⊗ Int(在尖集中)。
这源于态射的问题:在指向集合中,所有函数都是严格的——这包括构造函数,例如(,)。副产品也有同样的问题:
-
Left ⊥ ∈ Either Int Int(在 Hask 中),但是
-
Left ⊥ ∉ Int ⊕ Int(在尖集中)。
结论
所以,Set 和 Pointed Set 都不完全等同于 Hask 范畴。正如 Haskell Wiki 上的 Hask 页面中所述,Haskell 中的“产品”和“副产品”类型根本不符合分类产品和副产品的定义。所以严格来说,Haskell 中不存在产品和副产品。
这是个坏消息。好消息。
-
考虑 Hask 中的所有严格函数和严格构造函数。结果是 Hask 的一个子类别,它也是 Pointed Set 的一个子类别。该子类别是一个笛卡尔封闭类别。
-
考虑 Hask 中的所有总函数,如果两个函数对除⊥ 之外的每个输入产生相同的输出,则将它们视为相同的态射。 (根据“total”的定义,这些输出不一定是⊥。)结果是Set 的子类别。该子类别是一个笛卡尔封闭类别。
因此,只要您遵循正确的规则集,您仍然可以使用笛卡尔封闭类别。您甚至可以从两个不同的类别中进行选择!但是,如果您遵守这些规则,那么您正在使用 Haskell 的一个子集。
还有最后一点好消息。假设程序的严格版本终止,严格函数可以修改为惰性函数而不改变整个程序的输出。因此,您可以假装⊥ 不存在并使用范畴论完成一些工作,但仍然编写利用惰性求值的程序。
懒人总结
假装 Hask 有产品和副产品不会给你带来麻烦。