第 31 章:单子、幺半群与范畴(Monads, Monoids, and Categories)
原文:Bartosz Milewski, Category Theory for Programmers, Scala Edition, Chapter 31. 原书 PDF、
.tex源文件及相关图像采用 CC BY-SA 4.0;本译文按同一许可发布。
一本关于范畴论的书并没有什么真正合适的结尾。总还有更多东西要学。范畴论是一个庞大的主题。与此同时,很明显,相同的主题、概念和模式会一再出现。有一句说法是:所有概念都是 Kan 扩张;事实上,你确实可以用 Kan 扩张推出极限、余极限、伴随、单子、Yoneda 引理,以及更多东西。范畴这个概念本身会出现在所有抽象层级上,幺半群和单子也一样。哪一个最基本?事实证明,它们全都互相关联,一个引向另一个,形成永无止境的抽象循环。我决定,展示这些相互联系也许是结束本书的好方式。
31.1 双范畴(Bicategories)
范畴论最困难的方面之一,是视角的不断切换。以集合范畴为例。我们习惯于用元素定义集合。空集没有元素。单元素集合有一个元素。两个集合的笛卡尔积是由有序对组成的集合,如此等等。但在谈论 Set 范畴时,我要求你忘记集合的内容,转而关注它们之间的态射(箭头)。有时,你可以掀开盖子看一眼,看看 Set 中某个具体泛构造用元素语言描述时是什么样子。终对象原来是一个单元素集合,等等。但这些只是健全性检查。
函子定义为范畴之间的映射。把映射看成某个范畴中的态射是很自然的。函子原来是范畴的范畴中的态射(如果想避免大小问题,就说小范畴的范畴)。当我们把函子当作箭头时,就放弃了它作用于范畴内部(对象和态射)的信息,就像在 Set 中把函数当作箭头时,我们放弃了它作用于集合元素的信息一样。但任意两个范畴之间的函子本身也形成一个范畴。这一次,你被要求把某个范畴中的箭头看成另一个范畴中的对象。在函子范畴中,函子是对象,自然变换是态射。我们已经发现,同一个东西可以在一个范畴中是箭头,在另一个范畴中是对象。把对象朴素地看成名词、把箭头看成动词,这种看法并不成立。
与其在两种视角之间切换,不如尝试把它们合并成一种视角。这样就得到 2-范畴 的概念,其中对象称为 0-cell,态射称为 1-cell,态射之间的态射称为 2-cell。

范畴的范畴 Cat 是一个直接的例子。这里,范畴是 0-cell,函子是 1-cell,自然变换是 2-cell。2-范畴的定律告诉我们,任意两个 0-cell 之间的 1-cell 形成一个范畴(换句话说,C(a, b) 是 hom-category,而不是 hom-set)。这正好契合我们前面的断言:任意两个范畴之间的函子形成函子范畴。
特别地,从任意 0-cell 回到自身的 1-cell 也形成一个范畴,也就是 hom-category C(a, a);但这个范畴还有更多结构。C(a, a) 的成员既可以看成 C 中的箭头,也可以看成 C(a, a) 中的对象。作为箭头,它们可以彼此组合。但当我们把它们看成对象时,组合就变成了一个从一对对象到一个对象的映射。事实上,它看起来非常像积,更准确地说,是张量积。这个张量积有一个单位:恒等 1-cell。事实证明,在任意 2-范畴中,hom-category C(a, a) 自动成为一个幺半范畴,其张量积定义为 1-cell 的组合。结合律和单位律直接来自相应的范畴定律。
我们来看看这在 2-范畴的典型例子 Cat 中意味着什么。hom-category Cat(a, a) 是 a 上自函子的范畴。自函子组合在其中扮演张量积的角色。恒等函子是这个积的单位。我们之前已经看到自函子形成幺半范畴(定义单子时就用到了这一事实),但现在我们看到,这是一个更一般的现象:任意 2-范畴中的 endo-1-cell 都形成幺半范畴。稍后在推广单子时,我们会回到这一点。
你可能还记得,在一般幺半范畴中,我们并没有坚持幺半群定律必须严格成立。单位律和结合律在同构意义下成立通常就足够了。在 2-范畴中,C(a, a) 中的幺半定律来自 1-cell 的组合律。这些定律是严格的,因此我们总会得到一个严格幺半范畴。不过,也可以放松这些定律。例如,我们可以说,恒等 1-cell id_a 与另一个 1-cell f :: a -> b 的组合,同构于 f,而不是等于 f。1-cell 的同构用 2-cell 定义。换句话说,存在一个 2-cell:
rho :: f . id_a -> f
并且它有逆。

左单位律和结合律也可以做同样处理。这种放松的 2-范畴称为双范畴(bicategory;这里我会省略一些额外的相干律)。
正如预期,双范畴中的 endo-1-cell 形成一个定律非严格的一般幺半范畴。
一个有趣的双范畴例子是 span 的范畴。两个对象 a 和 b 之间的一个 span,是一个对象 x 以及一对态射:
f :: x -> a
g :: x -> b

你可能还记得,我们在定义范畴积时用过 span。这里,我们希望把 span 看成双范畴中的 1-cell。第一步是定义 span 的组合。假设我们有一个相邻的 span:
f' :: y -> b
g' :: y -> c

组合会是第三个 span,带有某个顶点 z。对它来说,最自然的选择是沿 f' 对 g 做拉回。记住,拉回是对象 z,以及两个态射:
h :: z -> x
h' :: z -> y
使得:
g . h = f' . h'
并且在所有这样的对象中是泛的。

现在,先把注意力放在集合范畴上的 span。在这种情况下,拉回只是笛卡尔积 x x y 中满足下面条件的有序对 (p, q) 的集合:
g p = f' q
两个端点相同的 span 之间的态射,定义为它们顶点之间的一个态射 h,并且相应的三角形交换。

总结一下,在双范畴 Span 中,0-cell 是集合,1-cell 是 span,2-cell 是 span 态射。恒等 1-cell 是退化 span,其中三个对象全都相同,两个态射都是恒等态射。
我们之前还见过另一个双范畴例子:profunctor 的双范畴 Prof,其中 0-cell 是范畴,1-cell 是 profunctor,2-cell 是自然变换。profunctor 的组合由余端给出。
31.2 单子(Monads)
到现在为止,你应该已经非常熟悉单子的定义:单子是自函子范畴中的幺半群。现在,让我们带着新的理解重看这个定义:自函子范畴只是双范畴 Cat 中 endo-1-cell 的一个小小 hom-category。我们知道它是一个幺半范畴:张量积来自自函子的组合。幺半群定义为幺半范畴中的一个对象,也就是这里的一个自函子 T,再配上两个态射。自函子之间的态射是自然变换。
一个态射把幺半单位,也就是恒等自函子,映射到 T:
eta :: I -> T
第二个态射把 T tensor T 的张量积映射到 T。张量积由自函子组合给出,所以得到:
mu :: T . T -> T

我们认出它们正是定义单子的两个操作(在 Haskell 中称为 return 和 join),并且知道幺半群律会变成单子律。
现在,让我们从这个定义中去掉所有关于自函子的说法。我们从一个双范畴 C 开始,并在其中选取一个 0-cell a。如前所见,hom-category C(a, a) 是一个幺半范畴。因此,可以通过选取一个 1-cell T 和两个 2-cell 来定义 C(a, a) 中的幺半群:
eta :: I -> T
mu :: T . T -> T
并要求它们满足幺半群律。我们把这称为一个单子。

这是一个更加一般的单子定义,只使用 0-cell、1-cell 和 2-cell。当它应用到双范畴 Cat 时,就退化为通常意义上的单子。但让我们看看在其他双范畴中会发生什么。
我们在 Span 中构造一个单子。先选取一个 0-cell,也就是一个集合;出于马上会变得清楚的原因,我会把它称为 Ob。接着选取一个 endo-1-cell:一个从 Ob 回到 Ob 的 span。它的顶点处有一个集合,我会把它称为 Arr,并配备两个函数:
dom :: Arr -> Ob
cod :: Arr -> Ob

我们把集合 Arr 的元素称为“箭头”。如果我还告诉你把 Ob 的元素称为“对象”,你也许已经猜到这会走向哪里。两个函数 dom 和 cod 为一个“箭头”指定定义域和余定义域。
为了把这个 span 变成单子,需要两个 2-cell:eta 和 mu。在这个例子中,幺半单位是从 Ob 到 Ob 的平凡 span,其顶点是 Ob,两个函数都是恒等函数。2-cell eta 是顶点 Ob 和 Arr 之间的函数。换句话说,eta 为每个“对象”指派一个“箭头”。Span 中的 2-cell 必须满足交换条件;在这里就是:
dom . eta = id
cod . eta = id

按分量写就是:
dom (eta ob) = ob = cod (eta ob)
其中 ob 是 Ob 中的一个“对象”。换句话说,eta 为每个“对象”指派一个定义域和余定义域都是该“对象”的“箭头”。我们把这个特殊“箭头”称为“恒等箭头”。
第二个 2-cell mu 作用于 span Arr 与自身的组合。组合定义为拉回,因此它的元素是来自 Arr 的元素对,也就是“箭头”对 (a1, a2)。拉回条件是:
cod a1 = dom a2
我们说 a1 和 a2 是“可组合的”,因为其中一个的定义域是另一个的余定义域。

2-cell mu 是一个函数,它把一对可组合箭头 (a1, a2) 映射为来自 Arr 的单个箭头 a3。换句话说,mu 定义了箭头的组合。
很容易检查,单子律对应于箭头的恒等律和结合律。我们刚刚定义了一个范畴(请注意,是一个小范畴,其中对象和箭头都形成集合)。
所以,总而言之,范畴只是 span 双范畴中的单子。
这个结果令人惊讶之处在于,它把范畴和单子、幺半群等其他代数结构放到了同一层面上。成为范畴并没有什么特殊之处。它只是两个集合和四个函数。事实上,我们甚至不需要为对象准备一个单独的集合,因为对象可以用恒等箭头识别(它们一一对应)。所以它其实只是一个集合和几个函数。考虑到范畴论在整个数学中扮演的核心角色,这是一个非常令人谦卑的认识。
31.3 挑战(Challenges)
- 推导在双范畴中以 endo-1-cell 的组合定义的张量积所满足的单位律和结合律。
- 检查
Span中单子的单子律对应于所得范畴中的恒等律和结合律。 - 证明
Prof中的单子是对象上恒等(identity-on-objects)的函子。 Span中单子的单子代数是什么?
31.4 参考文献(Bibliography)
- Paweł Sobociński 的博客:A Monoid is a Category, a Category is a Monad, a Monad is a Monoid.