Profunctor Equipment in Haskell

Hacker News Top Tools

Summary

This blog post provides a toy Haskell implementation of profunctor equipment, including natural transformations and composition, to make category-theoretic concepts accessible to programmers.

No content available
Original Article
View Cached Full Text

Cached at: 05/18/26, 06:53 AM

# Profunctor Equipment in Haskell Source: [https://bartoszmilewski.com/2026/05/16/profunctor-equipment-in-haskell/](https://bartoszmilewski.com/2026/05/16/profunctor-equipment-in-haskell/) Previously:[Profunctor Equipment](https://bartoszmilewski.com/2026/04/24/profunctor-equipment/)\. To make things more palatable for programmers, I decided to provide a toy implementation of some of the equipments in Haskell\. The advantage of this encoding is that it can be verified by the compiler, and I still trust the compiler more than I trust the AI\. A more adequate implementation would require a full\-blown dependently typed language, but if we restrict ourselves to just a single category and work only with endo\-functors and endo\-profunctors, we can get at least some intuitions\. If you want to see a more elaborate version, see the[proarrows](https://github.com/sjoerdvisscher/proarrow/)library by Sjoerd Visscher\. The only 0\-cell I’ll be using is the Haskell category of types and functions\. For vertical 1\-cells I’ll use the standard library implementation of`Functor`, and for horizontal ones I’ll use`Profunctor`\. A 2\-cell in![\mathbb{P}rof](https://s0.wp.com/latex.php?latex=%5Cmathbb%7BP%7Drof&bg=ffffff&fg=29303b&s=0&c=20201002): [![](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-18-at-16.48.46.png?resize=414%2C404&ssl=1)](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-18-at-16.48.46.png?ssl=1) is implemented as a natural transformation: ``` type Cell f g h j = forall a c . h a c -> j (f a) (g c) ``` The`forall`serves as a universal quantifier\. The horizontal composition of such cells is given by: ``` 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 khcomp fg_hj fg_jk hac = dimap getCompose Compose $ fg_jk (fg_hj hac) ``` I used the library definition of functor composition: ``` newtype Compose f g a = Compose { getCompose :: f (g a) } ``` Vertical composition of cells uses a more elaborate profunctor composition: ``` 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 composition is defined using a coend\. In Haskell, we implement a coend: ![\int^x P \langle x, c\rangle \times Q \langle d, x \rangle](https://s0.wp.com/latex.php?latex=%5Cint%5Ex+P+%5Clangle+x%2C+c%5Crangle+%5Ctimes+Q+%5Clangle+d%2C+x+%5Crangle&bg=ffffff&fg=29303b&s=0&c=20201002) as an existential type: ``` data Procompose p q d c where Procompose :: p x c -> q d x -> Procompose p q d c ``` Here, x is a type that’s not in the argument list, so it’s interpreted using the existential counterpart of`forall`\. This is the horizontal unit cell: ``` type Hunit p = Cell Identity Identity p phUnit :: Profunctor p => Hunit phUnit = dimap runIdentity Identity ``` and here’s its vertical counterpart: ``` type Vunit f a b = Cell f f (->) (->)vUnit :: Functor f => Vunit f a bvUnit = fmap ``` I used the library implementation of the`Identity`functor, and the type constructor`\(\-\>\)`for the hom\-profunctor–the unit of profunctor composition\. The unit laws are satisfied up to isomorphism\. The companion and the conjoint are synonyms of the library types`Costar`and`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 ctype Conjoint f d c = Star f d c ``` The companion unit and counit cells: [![](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-19-at-19.15.45.png?resize=510%2C224&ssl=1)](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-19-at-19.15.45.png?ssl=1) are given by, respectively: ``` type CompUnit f = Cell Identity f (->) (Costar f)compUnit :: Functor f => CompUnit fcompUnit h = Costar (fmap (h . runIdentity)) ``` and ``` type CompCoUnit f = Cell f Identity (Costar f) (->)compCoUnit :: Functor f => CompCoUnit f compCoUnit (Costar h) = Identity . h ``` Similarly for the conjoint: [![](https://i0.wp.com/bartoszmilewski.com/wp-content/uploads/2026/04/Screenshot-2026-04-19-at-21.59.56.png?resize=510%2C224&ssl=1)](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)) ``` and: ``` type ConjCoUnit f = Cell Identity f (Star f) (->)conjCoUnit :: Functor f => ConjCoUnit f conjCoUnit (Star h) = h . runIdentity ``` More advanced constructions would require the definition of categories internal to Hask and the use of dependent types\. Haskell code is available[here](https://v15.next.forgejo.org/BartoszMilewski/Categories/src/branch/main/Equipment.hs)\.

Similar Articles

Tambara Equipment

Hacker News Top

This article explains Tambara modules in category theory, their relation to monoidal functors and profunctor optics, and illustrates the concepts using Haskell code, drawing from a recent paper by Mateusz Stroiński.

Coalgebras and Automata

Lobsters Hottest

An introductory literate Haskell document exploring the relationship between coalgebras and automata, demonstrating how state machines can be modeled using fold and unfold operations within category theory.

Actegories

Hacker News Top

An article explaining the concept of actegories in category theory and their role in programming optics such as lenses and prisms, with Haskell implementations.

Data types à la carte (2008)

Lobsters Hottest

This paper presents a technique for composing data types and functions from independent components, and extends the approach to combine free monads, enabling a modular structuring of Haskell's IO monad.

Data-directed programming in Haskell (SICP2.4.3)

Lobsters Hottest

This article demonstrates implementing data-directed programming in Haskell for complex number operations, following the approach from SICP 2.4.3 to avoid modifying generic functions when adding new representations.