Let be a (unital commutative, skeletal) Quantale and be -categories. A -profunctor from to , written , is a -functor
where is regarded as enriched in itself. Concretely (7S Exercise 4.9): a function such that
In ordinary category theory (DaoFP §8.3, §17.1), a profunctor is a Functor : it maps a pair of objects to a set and a pair of arrows to a function — “simultaneously a producer and a consumer”.
Sources: 7 Sketches §4.2 (Definition 4.8, Examples 4.11, 4.13, Remark 4.16, Exercises 4.9, 4.10, 4.12, 4.15, 4.17), §4.3, §4.5; DaoFP §8.3 (“Profunctors”), §8.4 (the Hom Functor is a profunctor), §17.1 (“Profunctors”, “Collages”, “Profunctors as relations”, “Profunctor composition in Haskell”), §17.2, §17.8 (Bicategory of Profunctors), §18 (Tambara modules, profunctor optics), §20.2 (enriched profunctors); Kittenlab Lecture 14 (-relations).
Examples
- -profunctors are feasibility relations between preorders — bridges between cities (Example 4.11): iff there is a path from through , across a bridge, and through to .
- Cost-profunctors between Lawvere metric spaces are bridges labelled by length: is the shortest path from through , over a bridge, through to (Example 4.13: , , ; 7S Exercise 4.15). Remark 4.16: with the matrix of bridge lengths ( where there is none), by min-plus matrix multiplication (7S Exercise 4.17).
- -profunctors (DaoFP): the Hom Functor is the model — “a profunctor provides additional bridges between objects, on top of the hom-sets already there”; a profunctor is a proof-relevant relation: each element of is a proof that is related to , compatible with the structure of the categories (if and are nonempty then relatedness transfers). In Haskell
class Profunctor p where dimap :: (s -> a) -> (b -> t) -> p a b -> p s t, with(->)as the prime instance (dimap f g h = g . h . f): “in programming, all non-trivial profunctors are variations on the function type”. The Exponential Object is functorial as a profunctor. - Companions and conjoints of functors: , ; the unit profunctor .
- Enriched profunctors for a monoidal closed (DaoFP §20.2).
Composition
Profunctors compose by a sum over a middle object — “in general, we say two objects are related by the composite relation if there exists an object in the middle related to both”: for a quantale,
(Definition 4.21, matrix multiplication), giving the Category of Profunctors . For -profunctors the naive sum over-counts when middle objects are connected by morphisms; the correct composite is the Coend (DaoFP §17.2). In Haskell, data Procompose p q a b where Procompose :: q a x -> p x b -> Procompose p q a b works thanks to parametricity: “the two arguments are a pair of proofs, one that is related to , and one that is related to ” — like charging your phone through a friend who owns a charger. Composition is associative only up to isomorphism in general, making a bicategory whose monads are prearrows; for skeletal quantales one gets an honest category.
Collages
Any profunctor glues its two categories into one: the Collage (DaoFP: cograph), with objects and as the “heteromorphisms” from to . Conversely a category with a functor to the Walking Arrow splits as a collage (DaoFP Exercise 17.1.2).
Further
- is a Compact Closed Category with (Theorem 4.63): profunctors are exactly what is needed to interpret feedback wiring diagrams in Co-design. Kittenlab’s notes write and a barred arrow for profunctors, as here with .
- Kan extensions and Day Convolution are computed by (co)ends of profunctors; Tambara modules are profunctors with extra structure that classify optics (DaoFP Ch. 18).
- 7 Sketches §4.6: profunctors generalize binary relations; a “delightful exposition” of profunctors, equipments, companions and conjoints is [Shu08; Shu10].
Docs: Kittenlab Lecture 14
Builds on: Cost (CostPre), Enriched Category (VCategory), Matrix Multiplication in a Quantale (qmul), Weighted Graph (distances) — run those notes’ Julia code first.
# a V-profunctor between finite V-categories as a matrix; composition by quantale matrix multiplication
struct VProfunctor{T}
X::VCategory; Y::VCategory; Φ::Matrix{T} # Φ[i, j] = Φ(X.objects[i], Y.objects[j])
end
function is_profunctor(P::VProfunctor)
V = P.X.base
all(leq(V, otimes(V, otimes(V, P.X.hom[i′, i], P.Φ[i, j]), P.Y.hom[j, j′]), P.Φ[i′, j′])
for i in axes(P.Φ, 1), i′ in axes(P.Φ, 1), j in axes(P.Φ, 2), j′ in axes(P.Φ, 2))
end
compose(P::VProfunctor, Q::VProfunctor) = VProfunctor(P.X, Q.Y, qmul(P.X.base, P.Φ, Q.Φ)) # (Φ;Ψ)(p,r) = ⋁_q Φ(p,q) ⊗ Ψ(q,r)
# Example 4.13 (Cost): Φ = d_X * M_Φ * d_Y via min-plus products
MX = [0.0 Inf 3 Inf; 2 0 Inf 5; Inf 3 0 Inf; Inf Inf 4 0]
MΦ = [Inf Inf Inf; 11 Inf Inf; Inf Inf Inf; Inf 9 Inf]
MY = [0.0 4 3; 3 0 Inf; Inf 4 0]
Φ = qmul(CostPre(), qmul(CostPre(), distances(MX), MΦ), distances(MY)) # Φ[2,1] == 11, Φ[1,3] == 20, Φ[3,2] == 17-- a profunctor C ⇸ D is a functor Dᵒᵖ × C ⥤ Type (Mathlib's convention puts the contravariant variable first)
example (C D : Type) [CategoryTheory.Category C] [CategoryTheory.Category D] : Type _ := Dᵒᵖ × C ⥤ Type
#check CategoryTheory.Functor.hom -- the hom-functor Cᵒᵖ × C ⥤ Type as the prototypical profunctor
-- monotone Bool-profunctors between preorders:
def BoolProf (X Y : Type) [Preorder X] [Preorder Y] := (Xᵒᵈ × Y) →o Bool{-# LANGUAGE GADTs, RankNTypes #-}
-- DaoFP §8.3: profunctors
class Profunctor p where
dimap :: (s -> a) -> (b -> t) -> (p a b -> p s t)
instance Profunctor (->) where
dimap f g h = g . h . f -- pre-compose with f, post-compose with g
-- DaoFP §17.1: composition via an existential middle object (a pair of "proofs")
data Procompose p q a b where
Procompose :: q a x -> p x b -> Procompose p q a b
instance (Profunctor p, Profunctor q) => Profunctor (Procompose p q) where
dimap l r (Procompose qax pxb) = Procompose (dimap l id qax) (dimap id r pxb)
mapOut :: Procompose p q a b -> (forall x. q a x -> p x b -> c) -> c
mapOut (Procompose qax pxb) f = f qax pxb