Actegories
摘要
一篇解释范畴论中Actegories概念及其在编程光学(如lenses和prisms)中作用的文章,并包含Haskell实现。
暂无内容
查看缓存全文
缓存时间: 2026/07/14 13:17
# 作用范畴
来源:https://bartoszmilewski.com/2026/06/30/actegories/
前文:双范畴中的Kan扩张(https://bartoszmilewski.com/2026/06/13/kan-extensions-in-double-categories/)
在编程中,作用范畴在光学(透镜、棱镜、遍历等)中扮演着核心角色。要理解作用范畴,我们先从幺半范畴的定义开始。
## 幺半范畴
一个幺半范畴 \(\mathbf M\) 是装备了张量积的范畴。张量积是一个函子 \(\otimes \colon \mathbf M \times \mathbf M \to \mathbf M\)。我们假定这个乘积是结合的且具有单位——在同构意义下。这意味着存在一个可逆的结合子:
\[\alpha_{a, b, c} \colon (a \otimes b) \otimes c \to a \otimes (b \otimes c)\]
关于所有三个参数自然。我们还有一个单位对象 \(1\) 和两个(可逆、自然)的单位子:
\[\lambda_a \colon 1 \otimes a \to a\]
\[\rho_a \colon a \otimes 1 \to a\]
为了更好地理解,我们尝试在 Haskell 中建模幺半范畴。我们通过张量积的类型 `ten` 进行参数化,我们希望它是一个 `Bifunctor`:
```haskell
class (Bifunctor ten) => Monoidal ten where
```
定义 Hask 的子范畴的标准方法是通过施加约束来限制对象的类型。这种限制具有特殊的种类,`Constraint`:
```haskell
type Obj :: Type -> Constraint
```
这种约束的一个常见例子是类型类。例如,`Monoid` 将范畴的对象限制为幺半群。(原则上,我们也应该限制箭头的类型,这里限制为幺半群同态。)
我们可以将幺半范畴的单位指定为关联类型(通过 `ten` 参数化):
```haskell
type Unit ten :: Type
```
单位应该是范畴的对象,因此它应该满足约束。我们可以在定义中将其编码为先决条件:`obj (Unit ten)`。这会导致循环,我们可以使用语言编译指令 `UndecidableSuperClasses` 来克服:
```haskell
class (Obj (Unit ten), Bifunctor ten) => Monoidal ten where
type Unit ten :: Type
unitObj :: Obj (Unit ten)
associator :: (Obj a, Obj b, Obj c) =>
(a `ten` b) `ten` c -> a `ten` (b `ten` c)
associatorInv :: (Obj a, Obj b, Obj c) =>
a `ten` (b `ten` c) -> (a `ten` b) `ten` c
leftUnitor :: Obj a => Unit ten `ten` a -> a
leftUnitorInv :: Obj a => a -> Unit ten `ten` a
rightUnitor :: Obj a => a `ten` Unit ten -> a
rightUnitorInv :: Obj a => a -> a `ten` Unit ten
```
注意这些函数类型中的 `obj` 约束以及张量的中缀表示法。
让我们来看几个例子。最简单的是以笛卡尔积为张量的所有类型的范畴。我们使用一个空类来定义 `Hask`,并使所有对象成为其实例:
```haskell
class Hask a
instance Hask a
```
类似地,我们可以定义以 `Either` 为张量积的幺半范畴,或以 `Monoid` 为对象约束的幺半范畴。
## 作用范畴
作用范畴是支持幺半范畴作用的范畴。可以将其想象为用幺半范畴的对象“乘”或“缩放”该范畴的对象。(左)作用可以定义为从乘积范畴到 C 的函子:
\[\triangleright \colon \mathbf M \times C \to C\]
或者,经过柯里化后,作为从 \(\mathbf M\) 到自函子范畴的函子:
![\triangleright \colon \mathbf M \to [C, C]](https://s0.wp.com/latex.php?latex=%5Ctriangleright+%5Ccolon+%5Cmathbf+M+%5Cto+%5BC%2C+C%5D&bg=ffffff&fg=29303b&s=0&c=20201002)
一致性条件是连接作用 \(\triangleright\) 与张量积 \(\otimes\) 及其单位 \(1\) 的可逆自然变换:
\[\alpha_{m n a} \colon (m \otimes n) \triangleright a \to m \triangleright (n \triangleright a)\]
\[\lambda_{a} \colon 1 \triangleright a \to a\]
作用在两个参数上都是函子性的,因此为简单起见,我们的 Haskell 翻译将其固定为 `Bifunctor`。(`Profunctor` 作用也是可能的。从范畴论的角度看,它对应使用 \(\mathbf M^{op}\) 作为幺半范畴。)
```haskell
class (Bifunctor act, Monoidal ten) => Actegory act ten | act -> ten where
act :: act m a -> a
```
另一个简化的假设是作用唯一标识张量积,这里编码为函数依赖 `act -> ten`。
作用范畴的最简单例子是笛卡尔积的自作用。这里,幺半范畴作用于自身:
```haskell
instance Actegory (,) (,) where
act = ...
```
## 幺半函子
使用相同幺半范畴进行作用的范畴构成一个范畴。该范畴中的态射是(严格)幺半函子。这些函子将一个作用映射到另一个作用:
\[f (m \triangleright_1 a) \cong m \triangleright_2 f a\]
在 Haskell 中,我们可以将其建模为:
```haskell
class (Actegory act1 ten, Actegory act2 ten) => MonoidalFunctor f ten act1 act2 where
```
事实上,作用范畴构成一个双范畴,其中保持作用的自然变换作用在幺半函子之间。
这里有一个有趣的例子,展示了非平凡作用范畴之间的幺半函子:
```haskell
instance MonoidalFunctor (Map String) (,) (,) (,) where
```
Haskell 代码可见[此处](https://v15.next.forgejo.org/BartoszMilewski/Categories/src/branch/main/src/Actegory.hs)。
相似文章
Tambara装备
本文解释了范畴论中的Tambara模块,它们与幺半群函子和profunctor光学的关系,并使用Haskell代码说明这些概念,内容基于Mateusz Stroiński近期的一篇论文。
Haskell中的Profunctor装备
这篇博客文章提供了一个用Haskell实现的Profunctor装备的玩具实现,包括自然变换和组合,旨在让范畴论概念对程序员来说更易于理解。
应用范畴论课程 (2018)
由John Baez教授的应用范畴论在线课程,基于《Seven Sketches in Compositionality》一书,通过一系列讲义涵盖了有序集、资源理论和数据库。
余代数和自动机
一份介绍性的 literate Haskell 文档,探讨余代数和自动机之间的关系,展示如何利用范畴论中的 fold 和 unfold 操作来建模状态机。
使用Rust和范畴论构建机器学习框架
这篇文章宣布了一份工作草稿书籍《Category Theory for Tiny ML in Rust》以及一个公开工作坊,介绍一个使用Rust和范畴论的微型机器学习流水线,旨在通过类型化转换使机器学习结构变得明确。