程序设计中的范畴论 (第一部分)
范畴论是极其抽象的概念, 为了解释这个概念, 下文中会频繁举例, 甚至嵌套地举例, 因此要分清每段所说的"例如", 是前述哪个"例如"中的"例如".
0 引言
0.1 抽象
一般来说, 我们的一些观念, 特别是数学观念会是基于构造的.
比如, 我们有时候会讨论数字 1 1 1 是什么, 我们往往会想去构造一个东西, 使其行为表现恰好与1相同. 比如定义 0 = { ∅ } 0=\{\varnothing\} 0={
∅}, 1 = { { ∅ } , ∅ } 1=\{\{\varnothing\}, \varnothing\} 1={
{
∅},∅} 等. 这是一种基于构造的思想.
但实际上我们在考虑数字的时候会去想他的具体构造, 或者说底层实现吗, 答案是不会的. 我们实际上的对数字的思考只会停留在 1 1 1 就是 1 1 1, 其具体构造根本不重要.
事实上数字 1 1 1 之所以是数字 1 1 1, 并不是因为什么所谓的数学上定义 1 = { { ∅ } , ∅ } 1=\{\{\varnothing\}, \varnothing\} 1={
{
∅},∅}, 而是因为数字 1 1 1 与现实生活中很多常见的一些结构的"形状"相同. 例如在计数的时候, 我们会把一只羊视作 1 1 1, 两只羊视作 2 2 2 等等, 这样羊在羊群中的合并与数字在数学上的加法具有完全相同的"形状". 再例如我们会把一个苹果视作 1 1 1, 两个苹果视作 2 2 2 等等, 这样苹果的合并与数字在数学上的加法也具有完全相同的"形状". 这样所谓的"形状"的相同, 在数学上称为同构.
换而言之, 1 1 1 之所以为 1 1 1, 就是因为 1 1 1 忽略了他的底层实现细节, 只保留了他的功能, 我们无须关心这个 1 1 1 到底是一只羊, 还是一个苹果, 抑或是一个集合论的构造 { { ∅ } , ∅ } \{\{\varnothing\}, \varnothing\} {
{
∅},∅}. 我们只需关心 1 1 1 他具有与另一个数相加的功能.
数学不是一个具体的对象, 他是一个模板, 任何与其同构(即所谓"形状"相同)的东西, 都可以往里面套, 甚至包括数学他自己. 并且在数学领域内取得的一切成果都可以反馈到与之同构的一切事物上.
在程序设计中, 类似这样忽略底层实现细节的现象则更多. 比如我们有时候会考虑一个读取文件的函数String fread(File)
, 他接受一个File
对象, 并返回一个String
, 他的功能是从File
对象描述的文件里读取内容, 并返回一个String
表示的内容. 作为用户, 我们只会考虑如何使用这个函数, 去和其他业务函数组合, 期间完全忽略这个函数的底层实现细节. 若是作为平台开发者, 我们才会考虑这个函数的具体实现.
人类单次思考的概念范围是有限的, 因此我们需要对事物进行抽象, 以忽略掉其底层实现细节, 以将我们有限的精力, 投入到更有意义的高层概念中. 这就是抽象的开端.
0.2 悖论
有时候我们会讨论所有的集合, 并会去尝试构造一个包括所有集合的集合. 注意, 这种操作会导致悖论.
- 罗素悖论
设有 S = { x ∣ x ∉ S } S=\{x|x\notin S\} S={ x∣x∈/S}
我们既可以推导出 S ⊆ S S\subseteq S S⊆S, 又可以推导出 S ⊈ S S\nsubseteq S S⊈S.
为避免悖论, 引入一个对整个推理体系稍有一点限制的小宇宙, 以使得在小宇宙内的讨论能避免悖论. 在可能引发悖论的地方, 默认使用小宇宙来加以限制.
相关资料可参考Grothendieck宇宙.
1 范畴 (Category)
与关心构造的集合论不同, 范畴论只关心对象的功能, 其对象具体是如何构造的, 不在范畴的讨论范围, 即所谓的抽象. 只要一个事物符合范畴的定义, 那都可以往范畴里套, 甚至包括从范畴出发引申出来的一些结构.
符合下述要求的事物, 我们可以将其视作为一个范畴 C \mathcal{C} C:
- 有一群对象 Ob ( C ) \text{Ob}(\mathcal{C}) Ob(C).
- 对象之间有一些箭头 Mor ( C ) \text{Mor}(\mathcal{C}) Mor(C), 箭头的起点和终点是确定的. 对象 A A A 到 B B B 的所有箭头记作 Hom ( A , B ) \text{Hom}(A, B) Hom(A,B).
- 如果有一个箭头 f f f 是 A → B A→B A→B 的, 箭头 g g g 是 B → C B→C B→C 的, 那么总存在一个 A → C A→C A→C 的箭头, 他是 f f f 和 g g g 的复合. 该复合的箭头可记作 g ∘ f g\circ f g∘f.
- 任意一个对象都有一个从自己出发并到达自己的恒等箭头. 对于某一对象 A A A 的恒等箭头, 记作 id A ∈ Hom ( A , A ) \text{id}_A\in\text{Hom}(A, A) idA∈Hom(A,A). 满足对于任何从 A A A 出发的箭头 f f f, 和到达 A A A 的箭头 g g g, 总有 f ∘ id A = f f\circ\text{id}_A=f f∘idA=f, id A ∘ g = g \text{id}_A\circ g=g idA∘g=g.
1.1 交换图
以上定义涉及的一些定义与有向图比较相似, 所以有时会用称为交换图的有向图来表示范畴. 注意, 有向图中, 没画出来的箭头一般意味着不存在这个箭头, 而交换图中, 一般只画出部分当前讨论中有意义的箭头, 没画出来箭头不代表不存在. 下面是一个交换图描述范畴的例子. 其中恒等箭头 id A \text{id}_A idA, id B \text{id}_B idB 等, 以及其他 Hom ( A , B ) \text{Hom}(A, B) Hom(A,B), Hom ( A , C ) \text{Hom}(A, C) Hom(A,C) 等箭头均被忽略.
如果我们称一个有向图(例如上图和下图)是交换图,
是指途中所有箭头的复合殊途同归, 即等式 h = g ∘ f h=g\circ f h=g∘f 和 y ∘ x = v ∘ u y\circ x=v\circ u y∘x=v∘u 恒成立.
1.2 示例
正整数与偏序关系
正整数和大于等于关系可以构成一个范畴, 因为他满足如下范畴要求
- 对象 = Z + = \Z^+ =Z+
- 箭头 = { a ≥ b ∣ a , b ∈ Z + } =\{a \ge b|a, b \in \Z^+\} ={ a≥b∣a,b∈Z+}
- 箭头的复合: 如果 a ≥ b ∧ b ≥ c a\ge b \land b \ge c a≥b∧b≥c, 那么有 a ≥ c a\ge c a≥c
- 恒等箭头: id a = a ≥ a \text{id}_a=a\ge a ida=a≥a
其交换图大致如下
类似地, 正整数和小于等于关系同样可以构成一个范畴, 交换图大致如下
整数和大于等于关系, 实数与大于等于关系等等, 都可以构成一个范畴.
矩阵范畴 (向量空间范畴的特例)
矩阵, 或者简单点, 有限维矩阵可以构成一个范畴, 因为他满足如下范畴的要求
- 对象 = { 1 行 矩 阵 , 2 行 矩 阵 , 3 行 矩 阵 , ⋯ } = { n 行 矩 阵 ∣ n ∈ Z + } =\{1行矩阵, 2行矩阵,3行矩阵,\cdots\}=\{n行矩阵|n\in \Z^+\} ={ 1行矩阵,2行矩阵,3行矩阵,⋯}