packages feed

proarrow-0.1.0.0: src/Proarrow/Profunctor/Instance/Adj.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | The 'Adj' newtype marks a profunctor as the heteromorphism profunctor of an adjunction: because left
-- adjoints preserve colimits and right adjoints preserve limits, @Adj p@ is a distributive monoidal
-- profunctor. Also proves that every adjunction between Hask endofunctors is equivalent to the
-- curry\/uncurry adjunction ('haskAdjIsCurryAdj').
module Proarrow.Profunctor.Instance.Adj where

import Data.Kind (Type)
import Prelude (const)

import Proarrow.Adjunction (Adjunction)
import Proarrow.Category.Monoidal (Monoidal (..), MonoidalProfunctor (..))
import Proarrow.Category.Monoidal.Cartesian (Cartesian)
import Proarrow.Colimit.BinaryCoproduct (Coprod (..), HasBinaryCoproducts (..), HasCoproducts)
import Proarrow.Colimit.Initial (HasInitialObject (..))
import Proarrow.Core (Profunctor (..), Promonad (..), lmap, rmap, type (+->))
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Limit.Terminal (HasTerminalObject (..))
import Proarrow.Object (pattern Objs)
import Proarrow.Optic (PIso, iso)
import Proarrow.Optic.Getter (review, view)
import Proarrow.Profunctor.Corepresentable (Corepresentable (..), corepObj)
import Proarrow.Profunctor.Representable (Representable (..), repObj)

-- | Preservation of limits and colimits makes the adjunction heteromorphism a distributive profunctor.
newtype Adj p a b = Adj (p a b)
  deriving newtype (Profunctor, Representable, Corepresentable)

instance (Cartesian j, Cartesian k, Corepresentable p) => MonoidalProfunctor (Adj p :: j +-> k) where
  one = cotabulate terminate \\ corepObj @p @TerminalObject
  Adj @_ @x l@Objs ** Adj @_ @y r@Objs =
    withOb2 @_ @x @y
      ( cotabulate
          ( coindex @p @(x ** y) (lmap (fst @_ @x @y) l)
              &&& coindex @p @(x ** y) (lmap (snd @_ @x @y) r)
          )
      )

instance (HasCoproducts j, HasCoproducts k, Representable p) => MonoidalProfunctor (Coprod (Adj p :: j +-> k)) where
  one = tabulate initiate \\ repObj @p @InitialObject
  Coprod (Adj @_ @_ @x l@Objs) ** Coprod (Adj @_ @_ @y r@Objs) =
    withObCoprod @_ @x @y
      ( Coprod
          ( Adj
              ( tabulate
                  ( index @p @_ @(x || y) (rmap (lft @_ @x @y) l)
                      ||| index @p @_ @(x || y) (rmap (rgt @_ @x @y) r)
                  )
              )
          )
      )

-- | Every adjunction between Hask endofunctors is equivalent to the curry-uncurry adjunction.
haskAdjIsCurryAdj
  :: forall p a b a' b'
   . (Adjunction (p :: Type +-> Type)) => PIso (p %% () -> a -> b) (p %% () -> a' -> b') (p a b) (p a' b')
haskAdjIsCurryAdj =
  iso (\kab -> tabulate \a -> index @p (cotabulate (`kab` a)) ()) (\p k a -> coindex p (corepMap @p (\() -> a) k))

instance (Adjunction p) => Promonad (Adj p :: Type +-> Type) where
  id = Adj (view (haskAdjIsCurryAdj @p) (const id))
  Adj l . Adj r = Adj (view (haskAdjIsCurryAdj @p) (\k -> review haskAdjIsCurryAdj l k . review haskAdjIsCurryAdj r k))