Haskell中的Profunctor装备
摘要
这篇博客文章提供了一个用Haskell实现的Profunctor装备的玩具实现,包括自然变换和组合,旨在让范畴论概念对程序员来说更易于理解。
暂无内容
查看缓存全文
缓存时间: 2026/05/18 06:53
# Haskell 中的 Profunctor 设备
来源:https://bartoszmilewski.com/2026/05/16/profunctor-equipment-in-haskell/
此前:Profunctor 设备 (https://bartoszmilewski.com/2026/04/24/profunctor-equipment/)
为了让程序员更容易理解,我决定在 Haskell 中提供一些设备的玩具实现。这种编码的优势在于编译器可以验证它,而我仍然更信任编译器而不是 AI。
更充分的实现需要完整的依赖类型语言,但如果我们将自己限制在单个范畴内,仅使用内函子和内 Profunctor,我们至少可以获得一些直觉。如果你想看到更详尽的版本,请参阅 Sjoerd Visscher 的 `proarrows` (https://github.com/sjoerdvisscher/proarrow/) 库。
我将使用的唯一 0-胞腔是类型与函数的 Haskell 范畴。对于垂直 1-胞腔,我将使用标准库中的 `Functor` 实现;对于水平 1-胞腔,我将使用 `Profunctor`。
在 \(\mathbb{P}rof\) 中的 2-胞腔:
[](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-18-at-16.48.46.png?ssl=1)
实现为一个自然变换:
```
type Cell f g h j = forall a c . h a c -> j (f a) (g c)
```
这里的 `forall` 充当全称量词。
此类胞腔的水平组合由下式给出:
```
hcomp :: (Functor f, Functor f', Functor g, Functor g'
, Profunctor h, Profunctor j, Profunctor k) =>
Cell f g h j -> Cell f' g' j k
-> Cell (Compose f' f) (Compose g' g) h k
hcomp fg_hj fg_jk hac = dimap getCompose Compose $ fg_jk (fg_hj hac)
```
我使用了库中定义的函子组合:
```
newtype Compose f g a = Compose { getCompose :: f (g a) }
```
胞腔的垂直组合使用了更复杂的 Profunctor 组合:
```
vcomp :: (Functor f, Functor g, Functor h
, Profunctor p, Profunctor q, Profunctor r, Profunctor s) =>
Cell f g p r -> Cell g h q s
-> Cell f h (Procompose q p) (Procompose s r)
vcomp fg_pr gh_qs (Procompose qxc pax)
= Procompose (gh_qs qxc) (fg_pr pax)
```
Profunctor 组合使用 coend 定义。在 Haskell 中,我们将 coend:
\[ \int^x P \langle x, c\rangle \times Q \langle d, x \rangle \]
实现为存在类型:
```
data Procompose p q d c where
Procompose :: p x c -> q d x -> Procompose p q d c
```
这里,`x` 是不在参数列表中的类型,因此它使用全称量词的存在对应形式解释。
这是水平单位胞腔:
```
type Hunit p = Cell Identity Identity p p
hUnit :: Profunctor p => Hunit p
hUnit = dimap runIdentity Identity
```
以及其垂直对应物:
```
type Vunit f a b = Cell f f (->) (->)
vUnit :: Functor f => Vunit f a b
vUnit = fmap
```
我使用了库中的 `Identity` 函子实现,以及类型构造器 `(->)` 作为同态 Profunctor——Profunctor 组合的单位。单位律满足同构。
陪伴(companion)和共轭(conjoint)是库类型 `Costar` 和 `Star` 的同义词:
```
newtype Star f d c = Star { runStar :: d -> f c }
newtype Costar f d c = Costar { runCostar :: f d -> c }
```
```
type Companion f d c = Costar f d c
type Conjoint f d c = Star f d c
```
陪伴的单位和余单位胞腔:
[](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-19-at-19.15.45.png?ssl=1)
分别由下式给出:
```
type CompUnit f = Cell Identity f (->) (Costar f)
compUnit :: Functor f => CompUnit f
compUnit h = Costar (fmap (h . runIdentity))
```
以及:
```
type CompCoUnit f = Cell f Identity (Costar f) (->)
compCoUnit :: Functor f => CompCoUnit f
compCoUnit (Costar h) = Identity . h
```
类似地,对于共轭:
[](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-19-at-21.59.56.png?ssl=1)
```
type ConjUnit f = Cell f Identity (->) (Star f)
conjUnit :: Functor f => ConjUnit f
conjUnit h = Star (fmap (Identity . h))
```
以及:
```
type ConjCoUnit f = Cell Identity f (Star f) (->)
conjCoUnit :: Functor f => ConjCoUnit f
conjCoUnit (Star h) = h . runIdentity
```
更高级的构造需要定义 Hask 内部的范畴并使用依赖类型。
Haskell 代码可在此处 (https://v15.next.forgejo.org/BartoszMilewski/Categories/src/branch/main/Equipment.hs) 获取。
相似文章
余代数和自动机
一份介绍性的 literate Haskell 文档,探讨余代数和自动机之间的关系,展示如何利用范畴论中的 fold 和 unfold 操作来建模状态机。
Data types à la carte (2008)
本文提出了一种从独立组件组合数据类型和函数的技术,并将该方法扩展到结合自由单子,从而实现了对Haskell的IO单子的模块化结构。
Effectful 递归方案
《Effekt》编程语言博客文章演示了如何利用效应和处理器实现递归方案(特别是 catamorphisms),以此取代传统的基于函子的方法,从而避免了对无限递归类型的依赖。
使用Rust和范畴论构建机器学习框架
这篇文章宣布了一份工作草稿书籍《Category Theory for Tiny ML in Rust》以及一个公开工作坊,介绍一个使用Rust和范畴论的微型机器学习流水线,旨在通过类型化转换使机器学习结构变得明确。
代数效应:给普通人的解释
这是一篇教育性博客文章,通过类比 try/catch 和 async/await 来解释编程中的代数效应概念,并讨论了它们与 React 及未来编程范式的潜在关联。