范畴论玩具:什么是单子 (monad)?

对于上了计概课的同学们, 相信大家都有很多问号, 什么是 monad 捏?

monad 就是自函子范畴上的一个"幺半群" (?)

我们来尝试理解一下这句话在说什么, 首先这里的"幺半群"是在通常意义上的幺半群的一个推广, 也就是说知道什么是幺半群的同学可以先忘掉原有概念


一个范畴 category C 是指下述资料:

  • 一些对象, 构成集合 Ob(C);
  • 一些态射, 构成集合 Mor(C), 以及映射 s,t:Mor(C)Ob(C) 给出态射的来源目标, 从而对于 X,YOb(C), HomC(X,Y):=s1(X)t1(Y) 表示 XY 的态射构成的集合, 称为 Hom-集;
  • 对于 XOb(C) 都有恒等态射 idXHomC(X,X);
  • 对于 X,Y,ZOb(C) 都有运算 :HomC(Y,Z)×HomC(X,Y)HomC(X,Z), 其将 (f,g) 映至 fg, 满足下述性质:
    • 对于 f,g,hMor(C), 若 (fg)hf(gh) 都有定义, 则 (fg)h=f(gh).
    • 对于 fHomC(X,Y), 都有 fidX=idYf=f.

对象很好理解, 就是一些元素...吗? 大多数时候, 我们应该把对象看作具有某种代数结构的集合, 而态射就是保持这些代数结构的映射.

我们有成堆的数学上的例子, 当然这都不重要, 就看看下面两个:

  • 全体集合作为对象构成范畴 Set, 态射及其合成是自明的;
  • 对于偏序集 (P,), 我们构造范畴 P 如下: 其对象即为 P, 而对 x,yPxy 则存在唯一态射 xy, 否则不存在态射. 特别地, 对于 nZ0, 将 n 看作序数等同于全序集 {0,1,,n1}, 这样构作的范畴记为 n.

虽然但是, 我们好像是学 Haskell 的, 那就来看看这是怎么扯上关系的.

定义范畴 Hask 的对象是 Haskell 语言的所有类型 a, 态射是所有函数 f :: a -> b, 态射的合成是运算符 (.) :: (b -> c) -> (a -> b) -> a -> c.

接下来就引出描述范畴之间态射的概念, 称为函子 functor, 它们要保持一个范畴所具有的"代数结构", 即对象之间的态射及其合成.

对于范畴 C,C, 一个函子 F:CC 是指下述资料:

  • 对象间的映射 F:Ob(C)Ob(C);
  • 对于 X,YOb(C) 有态射间的映射 F:HomC(X,Y)HomC(F(X),F(Y)) 使得 F(fg)=F(f)F(g), F(idX)=idF(X).

对应到 Haskell 语言中, 因为我们现在还只有 Hask 这个范畴, 所以只能考虑 HaskHask 的函子, 这样的函子称为自函子 endofunctor, 写成代码就是大家熟悉的这个样子:

这样的 f 称为类型构造器 type constructor, 其将一个类型"包装"为另一个类型, 注意不要与类型 type 搞混了.

有了 Functor 类之后, 在实践过程中我们会遇到 Maybe (Maybe a) 这样的东西, 这启发我们考虑函子的"合成运算", 甚至还有函子之间的态射, 而无聊的 Haskell 设计者们怎么会放过又一次提升抽象层次的机会呢 (?), 即所有自函子构成一个范畴, 称为自函子范畴 endofunctor category, 记作 End(C):=Fct(C,C), 为此我们需要一些准备工作.

对于函子 F,G:CC, 定义自然变换 natural transformation θ:FG 是指一族态射 (θXHomC(FX,GX))XOb(C) 使得下图对 C 中的态射 f:XY 交换:

图表交换是指对于起点和终点相同的路径, 其上态射的合成相同, 例如上面的图表就是 θYFf=GfθX.

"自然"在数学上指的是能画出来的图表可交换, 翻译到 Haskell 语言, 实际上是说对于相同的对象构造的函数, 在类型确定的情况下是唯一的, Transformation 类的定义如下:

至于函子间合成怎么办捏? 我们要在范畴上定义二元运算, 这引出幺半范畴 monoidal category 的概念. 为了简化理论我们只讨论严格幺半范畴 strict monoidal category, 这是说在范畴 V 上有二元函子 :V×VV 和幺元 1V 使得 (XY)Z=X(YZ)1X=X1=X. 容易验证自函子 End(C) 构成严格幺半范畴.

严格幺半范畴上的幺半对象 monoid object, 简称 monoid 错误地直译为幺半群是指对象 M 附带乘法 μ:MMM 和幺元 η:1M 满足结合律和幺元性质, 即如下的交换图表:

自函子范畴上的幺半对象即为单子 monad. 完成目标, 完结撒花 (?)

先看看远处的错误翻译"幺半群", 实际上不无道理! (哈?)
Set 作为幺半范畴, 其上的幺半对象确实是通常意义上的幺半群, 因而是一个推广定义!

再重复一遍: 具体到实际意义上时 M 是函子 (类型构造器) 而 μη 都是自然变换, 而自然变换在定义上是对全体类型 a 的一族态射 (函数).

还是以无聊的 Maybe 举例子, μ 称为 join 函数, 将 Maybe (Maybe a) 映至 Maybe a, η 称为 unit 函数, 也记作 pure, return, 将 a 映至 Maybe a, 写成代码就是大家熟悉的这个样子:

这里的 (>>=) 是个啥捏? 这就涉及到 monad 在 Haskell 中的具体应用, 例如实现管道操作和 do 语法糖, 与本文讲述理论的主题没啥关系, 而且大家在计概课也有学(

实际上, join(>>=) 可以按照下述方式互相定义:

相信大家确实理解 monad 是什么了, 于是就真的完结撒花了)

posted @   mizu164  阅读(1043)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
历史上的今天:
2019-10-12 ZROI Day6比赛总结
点击右上角即可分享
微信分享提示