Profunctor

This section is empty…

Definition

A profunctor between categories C and D is defined to be a functor Dop×C→Set, and is denoted C↛D.

In Haskell, this is approximated as

  -- lmap (f . g) = lmap f . lmap g
  -- rmap (f . g) = rmap g . rmap f
  -- lmap id = id
  -- rmap id = id
  class Profunctor p where
    lmap :: (a -> a') -> p a b -> p a' b
    rmap :: (b -> b') -> p a b' -> p a b

Strong

  class Profunctor p => Strong p where
    strong :: p a b -> p (a, c) (b, c)

In Haskell, every Functor is strong with respect to (,).

Example: Forget

Forget is an example of a profunctor that is not strong:

  newtype Forget' r a b = Forget' { runForget :: a -> r }

  instance Profunctor (Forget' r) where
    dimap f _ (Forget' p) = Forget' (p . f)

Mapping

  -- Laws:
  --   map' . rmap f ≡ rmap (fmap f) . map'
  --   map' . map' ≡ dimap Compose getCompose . map'
  --   dimap Identity runIdentity . map' ≡ id

  class (Traversing p, Closed p) => Mapping p where
    map' :: Functor f => p a b -> p (f a) (f b)
    roam :: ((a -> b) -> s -> t) -> p a b -> p s t

Two observations:

Closed

  -- Laws:
  --   lmap (. f) . closed ≡ rmap (. f) . closed
  --   closed . closed ≡ dimap uncurry curry . closed
  --   dimap const ($()) . closed ≡ id
  class Profunctor p => Closed p where
    closed :: p a b -> p (x -> a) (x -> y)

The first law is very interesting. The base Profunctor laws do not define any relation between lmap and rmap. Closed, however,

(quiver) \begin{tikzcd} {p\;a\;b} \\ {p\;(x \to a)\;(x \to b)} \\ {p\;a\;b} \arrow["{\text{closed}}", from=1-1, to=2-1] \arrow["{\text{lmap}\;(\circ f)}"', from=2-1, to=3-1] \arrow["{\text{rmap}\;(\circ f)}", shift left=3, from=2-1, to=3-1] \end{tikzcd} ParseError: No such environment: tikzcd at position 7: \begin{̲t̲i̲k̲z̲c̲d̲}̲ {p\;a\;b} \\ …

Inbox

References