1.3 Categories Great and Small - 范畴可大可小

1.3 Categories Great and Small - 范畴可大可小

Categories Great and Small - 范畴可大可小

Categories Great and Small

<译> 范畴,可大可小

No Objects - 无对象

The most trivial category is one with zero objects and, consequently, zero morphisms

存在这样一个零对象零态射的范畴。它在包含所有范畴的范畴中。这是个合理的概念,既然空集合是合理的,为何不会有空范畴呢?

Simple Graphs - 简单画图

可以仅通过箭头连接对象来构造出范畴。

这样想象,基于任何一个有向图,仅通过添加更多箭头即可让它变成一个范畴。首先给每个节点添加一个恒等箭头(恒等态射 identity arrow),然后为那些“可复合”箭头添加一个新箭头作为它俩的复合,注意添加新箭头之后可能出现新的“可复合”箭头。

重新审视这个过程,这便是在构造一个范畴。

这种由一个给定图构造的范畴称为“自由范畴”。

Orders - 序

考虑这样的范畴:它的态射表示的是两对象的关系,例如小于等于关系。我们考察一下这实质上是不是个范畴。问题1:有没有恒等态射?每个对象都小于等于它自己,OK!问题2:有没有复合关系?若a小于等于b,b小于等于c,则a小于等于c,OK!问题3:复合满足结合律吗?满足结合律!伴随这种关系的集合,叫做前序集preorder),前序集是范畴

可以加强这一关系,加一个附加条件,若有a小于等于b及b小于等于a可得a必定与b相等,这种叫做偏序集partial order

若集合中任意两对象之间有这个偏序关系,那么得到了一个全序集linear order/total order

把这些集合描绘为范畴,对于前序集而言,两对象之间最多只有一个态射,这种范畴也叫做瘦范畴(thin category)

范畴C中,从对象a到对象b的态射的集合称为Hom-集(hom-set),写作C(a,b) / $C(a,b)$ / $Hom_C(a,b)$。所以每个前序集的Hom-集要么是空的要么是单例的,(包括Hom-集C(a,a),在任意前序集中,必定只包含一个从a到a的恒等态射,此Hom-集必定是单例的)。前序集中可能包含环,但是偏序集中不能包含环。

理解清楚前序集、偏序集、全序集很重要,因为排序需要它们。常见的排序算法,例如快排、冒泡、归并等等,只能在全序集上正确运行;偏序集可以用拓扑排序算法来处理。

Monoid as Set - 幺半群作为集合

Monoid is an embarrassingly simple but amazingly powerful concept. It’s the concept behind basic arithmetics: Both addition and multiplication form a monoid.

幺半群(Monoid)是一个相当简单但是功能强大的概念。它是基本算数幕后的概念:只要有加法或乘法运算就可以形成幺半群。编程中的幺半群无所不在,表现为字符串、列表、可以“折叠”的数据结构、并发编程中的future、函数式响应编程中的事件等等。

a monoid is defined as a set with a binary operation.

幺半群被定义为“一个伴有二元运算的集合”。这个二元运算必须满足结合律,集合中包含着一个特殊的元素,对于这个二元运算,该元素的行为像一个返回其自身的 unit。

例如:包含0的自然数伴随加法运算,这就形成了一个幺半群。结合律是指(a+b)+c=a+(b+c);特殊的元素是0,0+a=a+0。(自然数加法满足交换律,但交换律并非幺半群定义所需。例如字符串有加法/连接运算,该运算不满足交换律,有特殊的“中立”元素空字符串,这依然是一个幺半群)

在Haskell中可以为幺半群定义一个类型类(type class),该类型包含一个中立元素mempty和二元运算mappend

1
2
3
class Monoid m where
    mempty :: m
    mappend :: m -> m -> m      -- currying form

注意,在 Haskell 中,无法解释 mempty 与 mappend 的幺半群性质,也就是说 mempty 是个什么样的中立者,mappend 符合怎样的结合律。因为这是程序猿的责任,毕竟 Haskell 不能未卜先知。

Haskell classes are not as intrusive as C++ classes. When you’re defining a new type, you don’t have to specify its class upfront. You are free to procrastinate and declare a given type to be an instance of some class much later.

Haskell中定义一个新的类型时,不需要声明它所属的类。可以之后再声明一个给定的类型是某个类的实例。

例如可以将String声明为一个幺半群,并为之提供memptymappend的实现。

1
2
3
instance Monoid String where
    mempty = ""
    mappend = (++)

Haskell中,任何中缀运算符,用括号围住,就转化成了一个接受两个参数的函数。(F#中也是如此)

注:Haskell中允许函数相等。但是从概念上讲,mappend = (++)(函数相等)与mappend s1 s2 = (++) s1 s2(相同参数产生的值相等)是不同的。前者是Hask范畴(若忽略掉表示无休止计算的底类型,是Set)中的态射相等。这样的方程更简洁,而且常被泛化到其它范畴。后者被称为外延相等extensional equality),说的是对于任意两个输入字符串,mappend(++)值相等。由于函数的参数值有时也被称为(情同:f在点x处的值),外延相等也被称为point-wise相等,未指定参数的函数相等,称为point-free相等。

幺半群作为范畴

在范畴论中,我们尝试的事情是放弃集合和它们的元素,转而讨论对象和态射。转换一下视角,从范畴的角度来看作用与集合的移动转移二元运算。

例如,有一个将自然数加5的运算,它将0映射成5,将1映射成6等,这样是自然数集上定义了一个函数,此时我们有了一个函数和一个集合。通常,对任意数字n,都有个加n的函数,“n的adder”。这些“adder”如何复合呢?加5的函数和加7的函数复合起来,可以得到加12的函数。“adder”的复合等同与加法规则。那么我们可以用函数复合来代替加法运算。当然,也有一个面向中立元素0的“adder”,这加0不改变任何东西,它是自然数集上的恒等函数。即使不以传统的加法规则作为参照,照样能给出 adder 们的复合规则。注意,adder 们的复合是符合结合律的,因为函数的复合是符合结合律的,而且我们也有个加 0 的函数作为恒等函数。

现在,忘掉我们正在处理自然数集,将它视为一个对象,它伴随这一堆态射(这些“adder”)。幺半群是一个单对象范畴。每个幺半群可以表述为有一个态射集的单对象范畴,态射集中的态射都遵守复合规则。

字符串连接是个有趣的例子,因为我们可以选择定义左连接还是右连接。这两个态射是彼此镜像的。

You might ask the question whether every categorical monoid — a one-object category — defines a unique set-with-binary-operator monoid

是否每个范畴化的幺半群都会定义一个唯一的的伴随二元运算的幺半群?事实上,我们总能从单对象范畴中抽出一个集合,这个集合是态射的集合(如前面的中的adder)。换句话说,对于只含单个对象m的范畴M,我们有一个Hom-集 M(m,m),很容易在这个集合中定义一个二元运算:两元素相乘相当于两态射的复合。从M(m,m)中拿出两个元素f和g,它们的乘积相当与f∘g。复合总是存在的,因为这些态射的源对象和目标对象是同一个。这种乘法运算也遵循范畴论中的结合律。此外恒等态射也必定存在。因此,我们总是从幺半群范畴中复原出幺半群集合。无论从那个角度看,它们是同一个东西。

对于数学家的挑剔而言,有点小 bug:态射不必形成集合。在范畴的世界里,有比集合更大的东西。一个范畴,其中任意两个对象之间的态射们形成一个集合,这样的范畴是局部小的。不过,因为承诺不要太数学,所以我会忽略这些细枝末节,在讲 Haskell 的『记录』语法时,再谈论它们。

范畴论中大量的有趣现象都来自于:hom-集里的元素可被视为遵守复合法则的态射,也可被视为集合中的点。在此,M 中的态射的复合就会变成集合 M(m,m) 中的幺半群式的乘法运算。