Last updated on November 15, 2023 5:00 PM
Preface
因为我觉得我要沉淀下期中了所以自己记给自己看一下。
不保证内容的可读性,因为懒得去想对应语言的表达所以中英文夹杂了一下。
不保证内容的正确性,如果能帮忙指出错误,感激不尽。
Category
Definition
一个范畴 C \mathsf C C 包含着如下东西:
一堆对象,记作 C 0 \mathsf C_0 C 0 ;
一堆态射(morphism),记作 C 1 \mathsf C_1 C 1 。
态射的来源与目标(domain 与 codomain),此后我们使用 f : a → b f:a\to b f : a → b 或 f a → b f_{a\to b} f a → b 表示一个态射,which satisfies dom f = a \operatorname{dom} f = a d o m f = a 且 cod f = b \operatorname{cod} f = b c o d f = b 。
单位态射:对每个 a ∈ C 0 a\in \mathsf C_0 a ∈ C 0 ,都有态射 f a → a ∈ C 1 f_{a\to a}\in\mathsf C_1 f a → a ∈ C 1 ,记作 1 a 1_a 1 a 。
态射的复合:g b → c ∘ f a → b g_{b\to c}\circ f_{a\to b} g b → c ∘ f a → b 到 h a → c h_{a\to c} h a → c 。
满足如下两条公理:
Unit:∀ f a , b ∈ C 1 , f ∘ 1 a = f = 1 b ∘ f \forall f_{a,b}\in \mathsf C_1, f\circ 1_a = f = 1_b\circ f ∀ f a , b ∈ C 1 , f ∘ 1 a = f = 1 b ∘ f
Associativity:∀ f a → b , g b → c , h c → d ∈ C 1 , h ∘ ( g ∘ f ) = ( h ∘ g ) ∘ f \forall f_{a\to b},g_{b\to c},h_{c\to d} \in \mathsf C_1, h\circ (g\circ f) = (h\circ g)\circ f ∀ f a → b , g b → c , h c → d ∈ C 1 , h ∘ ( g ∘ f ) = ( h ∘ g ) ∘ f
对象可以被理解为某种代数结构,态射可以被理解为一种对应关系。对象可以抽象成点,态射可以抽象成有向边。
而且 co 这个前缀似乎挺有意思的。冷笑话:读 CT 的书的每个人都是其 co-author。
Some Examples of Category
Some Trivial Ones
零范畴。顾名思义,啥都没有。
范畴 O n e \mathsf{One} O n e 。其中 O n e 0 = ˙ { ∗ } \mathsf{One}_0 \dot= \{*\} O n e 0 = ˙ { ∗ } ,O n e 1 = ˙ 1 ∗ \mathsf{One}_1\dot={1_*} O n e 1 = ˙ 1 ∗ 。
范畴 T w o \mathsf{Two} T w o 。其中 T w o 0 = ˙ { ∗ , ⋆ } \mathsf{Two}_0 \dot= \{*,\star\} T w o 0 = ˙ { ∗ , ⋆ } ,T w o 1 = ˙ { 1 ∗ , 1 ⋆ , f ∗ → ⋆ } \mathsf{Two}_1 \dot= \{1_*,1_{\star},f_{*\to\star}\} T w o 1 = ˙ { 1 ∗ , 1 ⋆ , f ∗ → ⋆ }
给出有限范畴的定义:C \mathsf C C 为有限范畴 iff C 1 \mathsf{C}_1 C 1 为有限集合。注意到 C 0 \mathsf C_0 C 0 必然是有限集合 。
Fn
F n 0 = ˙ { a ∣ a is a set } \mathsf {Fn}_0\dot=\{a\mid a\text{ is a set}\} F n 0 = ˙ { a ∣ a is a set }
F n 1 = ˙ { f ∣ f is a function between two sets in F n 0 } \mathsf {Fn}_1\dot= \{f\mid f\text{ is a function between two sets in }\mathsf {Fn}_0\} F n 1 = ˙ { f ∣ f is a function between two sets in F n 0 } ,即我们这里可以把 F n \mathsf{Fn} F n 中的态射理解为两个集合之间的函数关系
dom f \operatorname{dom} f d o m f 和 cod f \operatorname{cod}f c o d f 定义为 f f f 的定义域与值域
1 a = ˙ { x ↦ x ∣ x ∈ a } 1_a\dot= \{x\mapsto x\mid x\in a\} 1 a = ˙ { x ↦ x ∣ x ∈ a }
态射的复合被定义为函数的复合
Unit 和 Associativity 都是显然的。
Isomorphism(同构态射):f x → y f_{x\to y} f x → y 为同构态射 ⟺ \iff ⟺ ∃ g y → x \exists g_{y\to x} ∃ g y → x s.t. g ∘ f = 1 x ∧ f ∘ g = 1 y g\circ f = 1_x\land f\circ g = 1_y g ∘ f = 1 x ∧ f ∘ g = 1 y ,g g g 可以记作 f − 1 f^{-1} f − 1 。
一个范畴中的两个对象 x , y x,y x , y 被定义为 isomorphic,记作 x ≅ y x\cong y x ≅ y ,当且仅当存在 x x x 与 y y y 之间的同构态射。
endomorphism(自同态态射):dom f = cod f = a \operatorname{dom} f = \operatorname{cod} f = a d o m f = c o d f = a 。
automorphism(自同构态射):既是 isomorphism 也是 endomorphism。
It’s noteworthy that:
isomorphism 总是成对出现的(不解释了),且 f − 1 f^{-1} f − 1 是唯一的。
“两个对象是否同构”这件事情要放在同一个范畴下讨论。
对于任意范畴中的任意对象 a a a ,1 a 1_a 1 a 都是 automorphism。因为 1 a ∘ 1 a = 1 a = 1 a ∘ 1 a 1_a\circ 1_a=1_a=1_a\circ 1_a 1 a ∘ 1 a = 1 a = 1 a ∘ 1 a 且 1 a − 1 = 1 a 1^{-1}_a=1_a 1 a − 1 = 1 a 。
Groupoid, Group and Monoid
关于这三者的等价定义(在 CT 语境内给出):
一个范畴 C \mathsf C C 是一个 groupoid ⟺ \iff ⟺ ∀ f ∈ C 1 \forall f\in \mathsf C_1 ∀ f ∈ C 1 都有 f f f 为 isomorphism。
一个范畴 C \mathsf C C 是一个 group ⟺ \iff ⟺ C \mathsf C C 为一个 groupoid 且 ∣ C 0 ∣ = 1 |\mathsf C_0| = 1 ∣ C 0 ∣ = 1 。
或者说,仅具有一个对象,且所有态射都是同构态射。
一个范畴 C \mathsf C C 是一个 monoid ⟺ ∣ C 0 ∣ = 1 \iff |\mathsf C_0|=1 ⟺ ∣ C 0 ∣ = 1 。
~~此处有历史遗留问题待解决。~~已解决,感谢 ZJG。
举个例子,对于幺半群 monoid,其传统定义如下。
一个幺半群是一个三元组 ( m , u , ⊕ ) (m,u,\oplus) ( m , u , ⊕ ) ,其中
m m m 是一个集合
u u u 是 m m m 中唯一的一个单位元
⊕ \oplus ⊕ 是一个二元运算符 m × m → m m\times m\to m m × m → m
满足
∀ x ∈ m \forall x\in m ∀ x ∈ m 有 u ⊕ x = x = x ⊕ u u\oplus x = x = x\oplus u u ⊕ x = x = x ⊕ u
结合律:x ⊕ ( y ⊕ z ) = ( x ⊕ y ) ⊕ z x\oplus (y\oplus z) = (x\oplus y)\oplus z x ⊕ ( y ⊕ z ) = ( x ⊕ y ) ⊕ z
而我们如果用 CT 的语言去表述,则一个幺半群 ( m , u , ⊕ ) (m,u,\oplus) ( m , u , ⊕ ) 是一个范畴 M \mathsf M M ,其中
M 0 = ˙ { m } \mathsf M_0 \dot= \{m\} M 0 = ˙ { m }
M 1 = m \mathsf M_1 = m M 1 = m (解释见下)
∀ f ∈ M 1 \forall f\in \mathsf M_1 ∀ f ∈ M 1 都有 dom f = cod f = m \operatorname{dom} f = \operatorname{cod} f = m d o m f = c o d f = m
1 m = ˙ u m → m 1_m\dot=u_{m\to m} 1 m = ˙ u m → m
( g m → m ∘ f m → m ) m → m = ˙ ( g ⊕ f ) m → m (g_{m\to m}\circ f_{m\to m})_{m\to m} \dot=(g\oplus f)_{m\to m} ( g m → m ∘ f m → m ) m → m = ˙ ( g ⊕ f ) m → m
满足如下两条公理
f ∘ 1 = f = 1 ∘ f f\circ 1 =f= 1\circ f f ∘ 1 = f = 1 ∘ f
h ∘ ( g ∘ f ) = ( h ∘ g ) ∘ f h\circ (g\circ f) = (h\circ g)\circ f h ∘ ( g ∘ f ) = ( h ∘ g ) ∘ f
其实,原理就是,m m m 这个集合作为范畴中唯一的对象,而将 m m m 中所有的元素都定义为范畴中的态射,这个态射不必要理解为某种函数或者是什么对应关系,可以只当作形式化记号。而对态射复合 ( g m → m ∘ f m → m ) m → m = ˙ ( g ⊕ f ) m → m (g_{m\to m}\circ f_{m\to m})_{m\to m} \dot=(g\oplus f)_{m\to m} ( g m → m ∘ f m → m ) m → m = ˙ ( g ⊕ f ) m → m 的定义使得整件事情良定义了起来。
感觉还是很美丽的啊,非常的良定义!
Functor
Definition
一个定义在两个范畴 C , D \mathsf C,\mathsf D C , D 上的 functor F : C → D F:\mathsf C\to\mathsf D F : C → D 包含以下两种操作:
需要满足两条公理:
∀ a ∈ C 0 , F 1 a = 1 F a \forall a\in \mathsf C_0, F~1_a = 1_{F~a} ∀ a ∈ C 0 , F 1 a = 1 F a
∀ f a → b , g b → c ∈ C 1 , F g ∘ F f = F ( g ∘ f ) \forall f_{a\to b},g_{b\to c}\in \mathsf C_1, F~g\circ F~f = F~(g\circ f) ∀ f a → b , g b → c ∈ C 1 , F g ∘ F f = F ( g ∘ f ) 。
并且,一个满足 C = D \mathsf C = \mathsf D C = D 的 functor F C → D F_{\mathsf C\to\mathsf D} F C → D 被称为 endofunctor(自函子)
Examples
1 C 1_C 1 C
很蠢的“单位函子”,不说了。两条公理是自明的。
P F n → F n P_{\mathsf{Fn}\to\mathsf{Fn}} P F n → F n
P a = ˙ { x ∣ x ⊆ a } P~a~\dot=~\{x\mid x\subseteq a\} P a = ˙ { x ∣ x ⊆ a }
P f a → b = ˙ { x ↦ { f i ∣ i ∈ x } ∣ x ∈ P a } P~f_{a\to b}~\dot=~\{x\mapsto \{f ~i \mid i\in x\}\mid x\in P~a\} P f a → b = ˙ { x ↦ { f i ∣ i ∈ x } ∣ x ∈ P a }
类似于幂集的感觉。
两条公理的证明不是重点而且我也不太会 略过。
What about in Haskell
1 2 3 4 5 class Functor f where fmap :: (a -> b) -> (f a -> f b)
可以发现,f
不就是 F n 0 → F n 0 \mathsf {Fn}_0\to \mathsf {Fn}_0 F n 0 → F n 0 的一个 object-mapping 吗,fmap
不就是 F n 1 → F n 1 \mathsf{Fn}_1\to \mathsf{Fn}_1 F n 1 → F n 1 的一个 morphism-mapping 吗。这两个东西组合起来,就是一个很良定义的 functor。
补充:这两个 law 实际上就是保证了“范畴的结构不会被改变”这件事情。
而且可以证明,For any parameterized type in Haskell, there is at most one function fmap that satisfies the required laws. 具体怎么证明我也不知道。
因为 Haskell 中我们都是研究“函数”,所以我们研究的范畴为 F n \mathsf{Fn} F n 。而且我们发现,F F F 是一个 endofunctor。
下面举几个例子,例如 Maybe:
1 2 3 4 5 data Maybe a = Nothing | Just ainstance Functor Maybe where fmap _ Nothing = Nothing fmap g (Just x) = Just (g x)
再比如说树
1 2 3 4 5 6 data Tree a = Leaf a | Node (Tree a ) (Tree a ) deriving Show instance Functor Tree where fmap g (Leaf x) = Leaf (g x) fmap g (Node l r) = Node (fmap g l) (fmap g r)
是不是感觉具体了一些,此时我们可以把 functor 大概理解为一个“结构”或者说“壳子”。将抽象层级再次提高了。例如说所谓的 inc
函数,利用 functor 我们可以定义为
1 2 inc :: Functor f => f Int -> f Int inc = fmap (+1 )
注意到,刚才提到的 functor 的两条 law 保证了整个的 structure 没有改变,例如我们对于 functor []
,用 fmap
操作之后,元素个数不会增加/减少,顺序不会改变,都是一一对应的。
坑着。
Applicative
后记:为啥要叫 applicative,大抵是因为他做到了更广泛的 function application 的映射罢。。。。
Introduction
In retrospect, we know functors abstracts the idea of mapping a function over each element of a structure 。
现在若我们想解决含有更多参数的函数,怎么办呢?
1 2 3 4 fmap0 :: a -> f afmap1 :: (a -> b) -> f a -> f bfmap2 :: (a -> b -> c) -> f a -> f b -> f cfmap3 :: (a -> b -> c -> d) -> f a -> f b -> f c -> f d
如果我们能利用 curried function 的想法,将如上的东西也抽象,定义一个更加 generalized 的 fmap
,岂不是很好?
这样子考虑,引入两个函数:
1 2 pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
注意。 <*>
运算符是具有左结合性的,正如我们的 function application 一样。他能帮我们做到什么事情呢?注意看下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fmap0 :: a -> f afmap0 = purefmap1 :: (a -> b) -> f a -> f bfmap1 g x = pure g <*> xfmap2 :: (a -> b -> c) -> f a -> f b -> f cfmap2 g x y = pure g <*> x <*> yfmap3 :: (a -> b -> c -> d) -> f a -> f b -> f c -> f dfmap3 g x y z = pure g <*> x <*> y <*> z
注:其中 <$>
运算符被定义为
1 2 3 4 (<$>) :: Functor f => (a -> b) -> f a -> f b ($) :: (a -> b) -> a -> b
于是我们可以给出 applicative 的定义:
1 2 3 4 5 class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b
Examples
Let’s slow down for a while to see how we can make some known types into applicative functors.
1 2 3 4 5 instance Applicative Maybe where pure = Just Nothing <*> _ = Nothing (Just g) <*> mx = fmap g mx
1 2 3 4 instance Applicative [] where pure x = [x] gs <*> xs = [g x | g <- gs, x <- xs]
这个可以理解为 pure
transforms a value into a singleton list, while <*>
takes a list of functions and a list of
arguments, and applies each function to each argument in turn, returning all the results in a list. (我直接抄的书懒得管那么多了)
1 2 > pure (*) <*> [1,2] <*> [3,4] [3,4,6,8]
看这一段代码吧,实际上,pure (*) <*> [1,2]
即为 [(1*), (2*)]
,然后再和 [3,4]
复合的话就是(前面的是外层循环,后面的是内层循环)。
利用 list 的 applicative 性,我们可以改写一些函数来避免中间变量的书写,例如
1 2 3 prod :: [Int ] -> [Int ] -> [Int ]prod xs ys = (*) <$> xs <*> ys
最后来看一下之前遇到过的 IO
类型。
1 2 3 4 instance Applicative IO where pure = return mg <*> mx = do {g <- mg; x <- mx; return (g x)}
可能有点抽象,看个例子先吧。
1 2 3 getChars :: Int -> IO String getChars 0 = return []getChars n = pure (:) <*> getChar <*> getChars (n - 1 )
Applicative 与副作用的思考
一开始我们定义 applicative 实际上是想要达到某种“将多参数的函数进行映射”的想法。
但是其实我们可以将其视作一种,programming with effects 的 approach(我不想翻译了,对不起)
什么意思呢?有没有注意到,被 applicative functor 包裹起来的这个整体,是不是或多或少带点副作用而不是单纯的 plain value 了。例如 Maybe
就是说,有可能失败;IO
则是可能涉及到 IO 操作。
所以说,applicative 在做的事情,也可以理解为 applying pure functions to effectful arguments,具体是什么 effect 取决于 functor。
标准库里面定义了如下的一个 generic function:
1 2 3 sequenceA :: Applicative f => [f a] -> f [a]sequenceA [] = pure []sequenceA (x:xs) = pure (:) <*> x <*> sequenceA xs
就是说,将一系列的 applicative 操作变成了一个,同时返回一个返回值的列表。比如说我们刚才的 getChars
就可以被定为为
1 2 getChars :: Int -> IO String getChars = sequenceA [replicate n getChar]
是不是,重复 n n n 次读入字符这个操作,然后把结果合并,很合理吧。
Applicative Laws
1 2 3 4 pure id <*> x = xpure (g x) = pure g <*> pure xx <*> pure y = pure (\g -> g y) <*> xx <*> (y <*> z) = (pure (.) <*> x <*> y) <*> z
分别注解一下。
第一行比较显然,不解释。分析类型:id :: a -> a
,x :: f a
第二行说的就是,function application 在 pure 之后仍得到保留,分析类型:pure (g x) :: f b
,g :: a -> b
(存疑)第三行比较有趣。说的是如果我们将一个 effectful function 应用到一个 pure 的参数上时,先处理哪部分是无关紧要的。请注意这里 x
的类型是 f (a -> b)
,y
的类型是 a
,g
是一个 lambda function,其类型为 a -> b
,所以在 pure 了之后,pure (\g -> g y)
的类型为 f ((a -> b) -> b)
,后面再接一个 x :: f (a -> b)
其实就相当于是把 x
作为参数。
第四行,分析类型吧。z :: f a
,y :: f (a -> b)
,x :: f (b -> c)
,(pure (.) <*> x <*> y) :: f (a -> c)
。
~~至于为什么要有这些 law,存疑。等周三上课吧。~~老师也不太会,没事了。
好他妈难啊。
Monad
Kleisli Category
背景是实现所谓的 logging,即对于一个函数,我们让他需要同时打印一段 log。
于是我们定义了下面的这个东西:
1 2 3 4 5 6 7 8 9 type Writer a = (a , String ) (>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)m1 >=> m2 = \x -> let (y, s1) = m1 x (z, s2) = m2 y in (z, s1 ++ s2)return :: a -> Writer areturn x = (x, "" )
我们是从范畴 F n \mathsf{Fn} F n 出发构建我们的 W r i t e r \mathsf{Writer} W r i t e r 范畴的。对象保持不变,但是态射从原来的 function 变成现在的 embellished function 。
embellished function 其实可以理解为,带副作用的函数。比如说这里要实现的功能就是在正常计算值的基础上,完成 logging 的过程。
这个时候我们还是将 embellished function 理解为 a -> b
的态射,虽然他实际上是一个 a -> m b
的函数。不过我们是可以良定义出态射的复合的,也就是上面的 >=>
符号。
同时,单位态射也得到了定义,上面的 return
。
再次重申,这篇文章是给我自己看的。
上面的 W r i t e r \mathsf{Writer} W r i t e r 范畴就是 Kleisli Category —— 基于 Monad 的范畴的一个例子。这里还是先直接给出形式化定义吧。
给定一个范畴 C \mathsf C C 和一个自函子 m : C → C m: \mathsf C\to \mathsf C m : C → C ,则其对应的 Kleisli 范畴 K \mathsf K K 是如下定义的:
K 0 = ˙ C 0 \mathsf K_0~\dot=~\mathsf C_0 K 0 = ˙ C 0
K 1 = ˙ { f a → b ∣ f a → b is a morphism f a → m b ′ in C 1 } \mathsf K_1 ~\dot=~ \{f_{a\to b}\mid f_{a\to b}\text{ is a morphism }f'_{a\to m~b}\text{ in }\mathsf C_1\} K 1 = ˙ { f a → b ∣ f a → b is a morphism f a → m b ′ in C 1 }
这句话可能有些不好理解,等一下会说。
dom \operatorname{dom} d o m 和 sub \operatorname{sub} s u b 的定义略。
1 a = ˙ r e t u r n a → m a 1_a ~\dot=~ \mathsf{return}_{a\to m~a} 1 a = ˙ r e t u r n a → m a ,其中 r e t u r n a → m a ∈ C 1 \mathsf{return}_{a\to m~a}\in\mathsf C_1 r e t u r n a → m a ∈ C 1
( g b → c ∘ f a → b ) a → c = ˙ f ′ ↣ g ′ (g_{b\to c}\circ f_{a\to b})_{a\to c}~\dot=~f'\rightarrowtail g' ( g b → c ∘ f a → b ) a → c = ˙ f ′ ↣ g ′ ,其中 ↣ \rightarrowtail ↣ 是一个复合操作,其对于 C 1 \mathsf C_1 C 1 中的态射 f a → m b ′ , g b → m c ′ f'_{a\to m~b},g'_{b\to m~c} f a → m b ′ , g b → m c ′ 给出了一个其复合后的态射 h a → m c ′ ∈ C 1 h'_{a\to m~c}\in\mathsf C_1 h a → m c ′ ∈ C 1 。
满足如下两条公理:
Unit:f ∘ 1 = f = 1 ∘ f f\circ 1=f=1\circ f f ∘ 1 = f = 1 ∘ f
Associativity:h ∘ ( g ∘ f ) = ( h ∘ g ) ∘ f h\circ (g\circ f) = (h\circ g)\circ f h ∘ ( g ∘ f ) = ( h ∘ g ) ∘ f 。
之前没有理解的地方是 K 1 \mathsf K_1 K 1 的定义。实际上呢,可以理解为,我们对于 C 1 \mathsf C_1 C 1 中的态射 f a → m b ′ f'_{a\to m~b} f a → m b ′ ,将其就视为是 K 1 \mathsf K_1 K 1 中 a → b a\to b a → b 的态射。他实际上做的事情是 a → m b a\to m~b a → m b ,但是在 K \mathsf K K 中就把他视为 a → b a\to b a → b 的态射。
然后定义了那个小鱼符号之后,态射的复合就也是良定义的了。
不知道这么说够不够清楚。
然后,m m m 这个 functor 就是一个 monad。
All told, a monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
In Haskell
我们其实没有定义小鱼符号。因为其也不是那么的简洁,instead,我们定义 bind。
1 (>>=) :: m a -> (a -> m b) -> m b