丹巴拉设备
Tambara Equipment

原始链接: https://bartoszmilewski.com/2026/07/11/tambara-equipment/

本文探讨了范畴论与函数式编程之间的深层联系,重点聚焦于 **Tambara 模**(Tambara modules)及其在光学(optics)中的作用。基于先前对预函子光学(profunctor optics)和幺半群作用的研究,作者通过 Mateusz Stroiński 的理论,将 Tambara 模构建为双范畴(double category)中的水平箭头,即“预箭头装备”(proarrow equipment)。 核心见解在于:Tambara 模与幺半群函子的关系,正如预函子与函子的关系。通过在双范畴框架下定义这些模,该架构自然地扩展到了富范畴(enriched categories)。作者使用 Haskell 说明了这些概念,并展示了: * **光学**可以推导为作用于可表预函子上的自由 Tambara 模。 * **米田归约**(Yoneda reduction)可将复杂的光学定义简化为易于管理的 Haskell 类型。 * **幺半群作用**可通过伴随函子内化,从而优雅地表示遍历光学(traversal optics)。 最终,本文将 Tambara 模定位为抽象范畴论与实际代码之间的有力桥梁,为理解和实现 Haskell 中复杂的数据访问模式提供了统一的结构。

Hacker News 新帖 | 往期 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 Tambara Equipment (bartoszmilewski.com) 10 点,由 ibobev 发布于 4 小时前 | 隐藏 | 往期 | 收藏 | 讨论 帮助 欢迎申请 YC 2026 年秋季班!申请截止日期为 7 月 27 日。 准则 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
相关文章

原文

I was originally attracted to category theory when trying to understand Haskell optics. I was puzzled by the van Laarhoven’s functor representations and Kmett’s use of Tambara modules. By playing Tetris with the Yoneda lemma I was able to make some progress, attacking more and more esoteric topics. With a group of researcher and students at the Oxford Adjoint School in Applied Category Theory we cracked the problem of traversal optics and published a paper summarizing the advances in profunctor optics.

Optics sit at an intersection of monoidal actions and Tambara modules. There is a duality between optics and Tambara representations. It is related to what mathematicians call Tannakian reconstruction, when an algebraic object is recovered from the totality of its representations.

No wonder then that a recent article by Mateusz Stroiński, Module categories, internal bimodules and Tambara modules, piqued my interest. It turns out that Tambara modules can be thought of as horizontal arrows in a double category, which is also a proarrow equipment. I will try to sketch the contents of this paper and illustrate it using Haskell code.

The slogan is that Tambara modules are to monoidal functors as profunctors are to functors.

The main advantage of the double-categorical setting for Tambara modules is that it works out of the box for enriched categories.

Monoidal functors redux

For simplicity, I’ll be using a simplified version of a strict monoidal functor, which omits the object constraint from the definition of a monoidal category:

Here, alpha' is the inverse of alpha.

Monoidal functors compose:

and the composition is again a monoidal functor:

Notice the use of type application to help the Haskell type checker.

Tambara modules

A Tambara module is a profunctor that is compatible with the action of a monoidal category. If you interpret a profunctor as a proof-relevant relation, a Tambara module has the property that, whenever two objects are related, they remain related when you “multiply” them by the same object m. Multiplication in this case means the action of a monoidal category \mathbf M. In other words, a Tambara module is a profunctor J \colon A^{op} \times B \to \mathbf{Set} equipped with the transformation:

\lambda_{m a b} \colon J a b \to J (m \triangleright_1 a) (m \triangleright_2 b)

that is natural in a and b, and dinatural in m. The action interacts nicely with the monoidal structure:

\lambda_{(m \otimes n) a b} = \lambda_{m (n \triangleright a) (n \triangleright b)} \circ \lambda_{n a b}

\lambda_{1 a b} = id

(modulo some associators and unitors).

We can illustrate this in Haskell as a typeclass:

I chose to concentrate on the left action of the actegory, but it’s easy to define the right action, or both. For simplicity, I’m using the same action for both arguments. In full generality, we would have two separate actegories.

The simplest example of a Tambara module is the hom-functor:

Here I used the bifunctoriality of the action.

Tambara modules form a category, in which action-preserving natural transformations act as morphisms.

The usual profunctor composition using coends also works on Tambara modules.

The result of composition is again a Tambara module:

Taken together, we have a bicategory \mathbf{Tam}, where actegories are 0-cells, Tambara modules with coend composition are 1-cells, and natural transformations between them are 2-cells.

Moreover, endo-Tambara modules, that is Tambara modules that operate within a single category, form a monoidal bicategory, with profunctor composition acting as a tensor product and a hom-functor acting as a unit.

Tambara Equipment

We have previously studied a double category \mathbb{P}rof in which categories are 0-cells, profunctors are horizontal arrows, functors are vertical arrows, and natural transformations form 2-cells. This double category happens to be a proarrow equipment, which relates functors to representable profunctors in a specific way.

It turns out that there is an analogous construction with actegories as 0-cells, Tambara modules as horizontal 1-cells, and monoidal functors as vertical 1-cells. To form 2-cells, we first have to be able to lift (or restrict) a Tambara module along a pair of monoidal functors. We define a lifting of J as the profunctor:

\langle a, c \rangle \mapsto J(f a)(g c)

or, using placeholders, J(f -) (g =). In Haskell this is:

The lifting of a Tambara module is again a Tambara module with the structure map defined as a composition:

J (f a) (g c) \xrightarrow{\lambda_{m (f a) (g c)}} J (m \triangleright_2 f a) (m \triangleright_2 g c) \xrightarrow {J \alpha^{-1} \alpha} J (f (m \triangleright_1 a)) (g (m \triangleright_1 c))

The second arrow lifts the (invertible) monoidal functor structure map to J:

\alpha \colon m \triangleright_2 g c \to g (m \triangleright_1 c)

Here’s the same thing in Haskell:

I used type applications to select the correct left action corresponding to act2.

A 2-cell is then a natural transformation from H to the lifting of J.

Or, in Haskell:

The companion and the conjoint are representable profunctors, that are automatically Tambara modules as long as f is a monoidal functor (the Identity functor is trivially monoidal):

The unit and counit cells for the companion and the conjoint are reasonably easy to define (see the linked code).

Free Tambara and Optics

There is an obvious forgetful functor from \mathbf{Tamb} to \mathbf{Prof} (it forgets the action). This functor has a left adjoint. For any profunctor J, it produces a free Tambara module given by the following coend:

(\Phi J) \, s t = \int^m \int^{x y} C(s, m \triangleright x) \times J x y \times C(m \triangleright y, t)

In Haskell, we model it as an existential data type:

The result is indeed a Tambara module:

It so happens that optics can be defined as a free Tambara module acting on a representable profunctor \Theta_{a b} x y = C(x, a) \times C(b, y). Indeed, applying the Yoneda reduction, we get:

O\, s t a b = \int^{m} C(s, m \triangleright a) \times C(m \triangleright b, t)

In Haskell

giving us:

which, by Yoneda reduction, is isomorphic to:

Internalizing monoidal actions

A monoidal action, seen as a functor \mathbf M \to [C, C], can be internalized in M if it has a right adjoint:

C(m \triangleright a, t) \cong \mathbf M (m, \{a, t \} )

If you think of the action as “multiplication,” the adjunction is reminiscent of currying, and the right adjoint plays to role on the “internal hom.”

In Haskell, we define a typeclass:

We can use this adjunction to simplify multiplicative optics:

\int^{m} C(s, m \triangleright a) \times C(m \triangleright b, t) \cong \int^{m} C(s, m \triangleright a) \times \mathbf M (m, \{b, t \})

Using the Yoneda reduction (a.k.a, “integrating” over m), this is equivalent to:

O \, s t a b \cong C(s, \{b, t \} \triangleright a )

With additive optics, we use the right adjoint to coproduct to produce hom-sets in a product category.

Both tricks are used in simplifying traversals, which are optics generated by polynomial functors:

\int^{c \colon [\mathbb N , Set]} C(s, \sum_n c_n \times a^n) \times C (\sum_m c_m \times b^m, t)

Here, c is a natural-number-indexed family of objects with a monoidal structure given by convolution; and a^n and b^n are powers.

We first use the coproduct adjunction:

C (\sum_m c_m \times b^m, t) \cong \prod_m C(c_m \times b^m, t)

and follow it by the currying adjunction. We get the formula for a traversal:

T \, s t a b \cong Set(s, \sum_n Set(b^n, t) \times a^n)

The result can be illustrated in Haskell by replacing powers with lists:

Internal monoid

The endo-hom \{a, a\} is automatically a monoid in \mathbf M. Indeed, we can define monoid multiplication as:

\mathbf M(\{a, a\} \otimes \{a, a\}, \{a, a\}) \cong C\big((\{a, a\} \otimes \{a, a\}) \triangleright a, a\big)

The right hand side can be implemented as a double application of the counit of the adjunction:

\epsilon_{a a} = \{a, a\} \triangleright a \to a

In Haskell, we first define a monoid internal to a monoidal category:

and show that hom a a is an instance of it:

where counit of the adjunction is the eval function:

Haskell code for this blog post is available here.

联系我们 contact @ memedata.com