proarrow-0.1.0.0: src/Proarrow/Adjunction.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Adjunctions as heteromorphisms: an 'Adjunction' is a profunctor that is both 'Representable' (by the
-- right adjoint) and 'Corepresentable' (by the left adjoint), giving 'unitRep'\/'counitRep' and
-- 'leftAdjunct'\/'rightAdjunct'. Also the profunctor-level notion 'Proadjunction', and the interaction
-- of adjoints with (co)limits.
module Proarrow.Adjunction where
import Data.Kind (Constraint)
import Prelude (type (~))
import Proarrow.Category.Enriched.Thin (Thin, ThinProfunctor)
import Proarrow.Category.Instance.Opposite (OPPOSITE (..), Op (..))
import Proarrow.Category.Instance.Product (Fst, Snd, (:**:) (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Instance.Rep (COREP, COREPK, REP, REPK)
import Proarrow.Category.Instance.Sub (SUBCAT (..), Sub (..))
import Proarrow.Colimit (HasColimits (..))
import Proarrow.Core (CAT, CategoryOf (..), Profunctor (..), Promonad (..), UN, lmap, rmap, (//), (:~>), type (+->))
import Proarrow.Functor (Functor (..), FunctorForRep (..))
import Proarrow.Limit (HasLimits (..), mapLimit)
import Proarrow.Object (pattern Objs)
import Proarrow.Optic (PIso, iso, re)
import Proarrow.Profunctor.Corepresentable
( Corep
, Corepresentable (..)
, corepUniv
, cotabulated
)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Costar (Costar, pattern Costar)
import Proarrow.Profunctor.Instance.Identity (Id (..))
import Proarrow.Profunctor.Instance.Star (Star, pattern Star)
import Proarrow.Profunctor.Representable
( CorepStar (..)
, Rep
, RepCostar (..)
, Representable (..)
, mapRepCostar
, repUniv
, tabulated
)
import Proarrow.Promonad (Procomonad (..), bind, extend, extract, return)
import Proarrow.Tools.Laws (ProLaw (..), ProLaws (..), (=:=))
-- | Adjunctions as heteromorphisms.
class (Representable p, Corepresentable p) => Adjunction p
instance (Representable p, Corepresentable p) => Adjunction p
-- | One direction of the adjunction isomorphism: transpose an arrow out of the left adjoint,
-- @p '%%' a '~>' b@, to an arrow into the right adjoint, @a '~>' p '%' b@.
leftAdjunct :: forall p a b. (Adjunction p, Ob a) => (p %% a ~> b) -> a ~> p % b
leftAdjunct = index . cotabulate @p
-- | The other direction of the adjunction isomorphism, inverse to 'leftAdjunct'.
rightAdjunct :: forall p a b. (Adjunction p, Ob b) => a ~> p % b -> (p %% a ~> b)
rightAdjunct = coindex . tabulate @p
-- | The isomorphism between @leftAdjunct@ and @rightAdjunct@.
adjuncted
:: forall p a b a' b'. (Adjunction p, Ob a, Ob b') => PIso (b ~> p % a) (b' ~> p % a') (p %% b ~> a) (p %% b' ~> a')
adjuncted = tabulated @p . re cotabulated
-- | The monad induced by an adjunction as a representable promonad.
type AdjMonad p = p :.: CorepStar p
unitRep :: forall p a. (Adjunction p, Ob a) => a ~> AdjMonad p % a
unitRep = return @(AdjMonad p) @a
bindRep :: forall p a b. (Adjunction p, Ob b) => a ~> AdjMonad p % b -> AdjMonad p % a ~> AdjMonad p % b
bindRep = bind @(AdjMonad p) @b
-- | The comonad induced by an adjunction as a corepresentable promonad.
type AdjComonad p = RepCostar p :.: p
counitRep :: forall p a. (Adjunction p, Ob a) => AdjComonad p %% a ~> a
counitRep = extract @(AdjComonad p) @a
extendRep :: forall p a b. (Adjunction p, Ob a) => (AdjComonad p %% a ~> b) -> AdjComonad p %% a ~> AdjComonad p %% b
extendRep = extend @(AdjComonad p) @a @b
-- | The left adjoint of @((->) a)@ is @(,) a@.
instance Corepresentable (Star ((->) a)) where
type Star ((->) a) %% b = (a, b)
cotabulate f = Star \a b -> f (b, a)
coindex (Star f) (a, b) = f b a
corepMap = map
-- | The right adjoint of @(,) a@ is @((->) a)@.
instance Representable (Costar ((,) a)) where
type Costar ((,) a) % b = a -> b
tabulate f = Costar \(a, b) -> f b a
index (Costar g) a b = g (b, a)
repMap = map
class (p % a ~ p %% a, p % (p %% a) ~ p % (p % a), p %% (p % a) ~ p % (p % a)) => SelfAdjointPoint p a
instance (p % a ~ p %% a, p % (p %% a) ~ p % (p % a), p %% (p % a) ~ p % (p % a)) => SelfAdjointPoint p a
-- | Self-adjoint functors
class (forall a. (Ob a) => SelfAdjointPoint p a, Adjunction p) => SelfAdjoint p
instance (forall a. (Ob a) => SelfAdjointPoint p a, Adjunction p) => SelfAdjoint p
-- | Involution functors are self-adjoint functors where the unit and counit form an isomorphism.
class (SelfAdjoint p) => Involution p where
involuted :: forall a a'. (Ob a, Ob a') => PIso a a' (p % (p % a)) (p % (p % a'))
involuted = iso (unitRep @p) (counitRep @p)
instance (CategoryOf k) => Involution (Id :: CAT k)
class (p %% a ~ RepCostar p % a, p % (p %% a) ~ p % (RepCostar p % a)) => AmbidextrousEqRep p a
instance (p %% a ~ RepCostar p % a, p % (p %% a) ~ p % (RepCostar p % a)) => AmbidextrousEqRep p a
class (p % a ~ CorepStar p %% a, p %% (p % a) ~ p %% (CorepStar p %% a)) => AmbidextrousEqCorep p a
instance (p % a ~ CorepStar p %% a, p %% (p % a) ~ p %% (CorepStar p %% a)) => AmbidextrousEqCorep p a
class
( Adjunction p
, Representable (RepCostar p)
, Corepresentable (CorepStar p)
, forall a. (Ob a) => AmbidextrousEqRep p a
, forall a. (Ob a) => AmbidextrousEqCorep p a
) =>
Ambidextrous p
instance
( Adjunction p
, Representable (RepCostar p)
, Corepresentable (CorepStar p)
, forall a. (Ob a) => AmbidextrousEqRep p a
, forall a. (Ob a) => AmbidextrousEqCorep p a
)
=> Ambidextrous p
class (Ambidextrous p) => AdjointEquivalence (p :: j +-> k) where
unitIso :: (Ob a, Ob a') => PIso a a' (AdjMonad p % a) (AdjMonad p % a')
unitIso @a @a' = iso (unitRep @p @a) (counitRep @(RepCostar p) @a')
counitIso :: (Ob a, Ob a') => PIso (AdjComonad p %% a) (AdjComonad p %% a') a a'
counitIso @a @a' = iso (counitRep @p @a) (unitRep @(CorepStar p) @a')
type GaloisConnection (p :: j +-> k) = (ThinProfunctor p, Thin j, Thin k, Adjunction p)
-- | Adjunctions between two profunctors.
type Proadjunction :: forall {j} {k}. j +-> k -> k +-> j -> Constraint
class (Profunctor p, Profunctor q) => Proadjunction (p :: j +-> k) (q :: k +-> j) where
unit :: (Ob a) => (q :.: p) a a -- (~>) :~> q :.: p
counit :: p :.: q :~> (~>)
-- | 'Proadjunction' with the right adjoint first, so that the laws about elements of the left
-- adjoint can be indexed by it.
type LeftProadjoint :: forall {j} {k}. k +-> j -> j +-> k -> Constraint
class (Proadjunction p q) => LeftProadjoint q p
instance (Proadjunction p q) => LeftProadjoint q p
-- | The zigzag law for elements of the right adjoint @q@: going through the 'unit' and back through
-- the 'counit' is the identity.
instance ProLaws (Proadjunction p) where
proLaws =
[ ProLaw "zigzag" \ @q @a q _ _ -> case unit @p @q @a of
uq :.: up -> q =:= rmap (counit (up :.: q)) uq
]
-- | The zigzag law for elements of the left adjoint @p@.
instance ProLaws (LeftProadjoint q) where
proLaws =
[ ProLaw "zigzag" \ @p @_ @b p _ _ -> case unit @p @q @b of
uq :.: up -> p =:= lmap (counit (p :.: uq)) up
]
unit' :: forall p q. (Proadjunction p q) => (~>) :~> q :.: p
unit' (f :: a ~> b) = rmap f (unit @p @q @a) \\ f
flipMate :: forall p q. (Proadjunction p q) => (~>) :~> p -> q :~> (~>)
flipMate n q = counit (n id :.: q) \\ q
unflipMate :: forall p q. (Proadjunction p q) => q :~> (~>) -> (~>) :~> p
unflipMate n f = case unit' @p @q f of q :.: p -> lmap (n q) p
instance (Representable p) => Proadjunction p (RepCostar p) where
unit = corepUniv :.: repUniv
counit (f :.: g) = coindex g . index f
instance (Corepresentable p) => Proadjunction (CorepStar p) p where
unit = corepUniv :.: repUniv
counit (f :.: g) = coindex g . index f
instance (FunctorForRep f) => Proadjunction (Rep f) (Corep f) where
unit = corepUniv :.: repUniv
counit (f :.: g) = coindex g . index f
instance (Functor f) => Proadjunction (Star f) (Costar f) where
unit = corepUniv :.: repUniv
counit (f :.: g) = coindex g . index f
instance (Proadjunction l1 r1, Proadjunction l2 r2) => Proadjunction (l1 :.: l2) (r2 :.: r1) where
unit :: forall a. (Ob a) => ((r2 :.: r1) :.: (l1 :.: l2)) a a
unit = case unit @l2 @r2 @a of
r2 :.: l2 ->
l2 // case unit @l1 @r1 of
r1 :.: l1 -> (r2 :.: r1) :.: (l1 :.: l2)
counit ((l1 :.: l2) :.: (r2 :.: r1)) = counit (rmap (counit (l2 :.: r2)) l1 :.: r1)
-- | Adjunctions pair up over the product category.
instance (Proadjunction p1 q1, Proadjunction p2 q2) => Proadjunction (p1 :**: p2) (q1 :**: q2) where
unit @a = case unit @p1 @q1 @(Fst @ a) of
u1 :.: v1 -> case unit @p2 @q2 @(Snd @ a) of
u2 :.: v2 -> (u1 :**: u2) :.: (v1 :**: v2)
counit ((l1 :**: l2) :.: (r1 :**: r2)) = counit (l1 :.: r1) :**: counit (l2 :.: r2)
instance (CategoryOf k) => Proadjunction (Id :: CAT k) Id where
unit = Id id :.: Id id
counit (Id f :.: Id g) = g . f
instance (Proadjunction q p) => Proadjunction (Op p) (Op q) where
unit = case unit @q @p of q :.: p -> Op p :.: Op q
counit (Op q :.: Op p) = Op (counit (p :.: q))
instance (Proadjunction p q) => Promonad (q :.: p) where
id = unit
(q :.: p) . (q' :.: p') = rmap (counit (p' :.: q)) q' :.: p
instance (Proadjunction p q) => Procomonad (p :.: q) where
proextract = counit
produplicate (p :.: q) = p // case unit of q' :.: p' -> (p :.: q') :.: (p' :.: q)
-- (~>) :~> Colimit j c :.: r
-- <=>
-- j :~> c :.: r
-- <=>
-- (~>) :~> c :.: Limit j r
-- | The heteromorphisms witnessing the @'Colimit' j@ ⊣ @'Limit' j@ adjunction: a natural
-- transformation @j ':~>' c ':.:' r@, read as an arrow from the corepresentable @c@ to the
-- representable @r@.
type LimitAdj :: (a +-> b) -> REPK a k +-> COREPK b k
data LimitAdj j c r where
LimitAdj :: (Corepresentable c, Representable r) => (j :~> c :.: r) -> LimitAdj j (COREP c) (REP r)
instance (Profunctor j) => Profunctor (LimitAdj j) where
dimap (Sub (Op (Prof l))) (Sub (Prof r)) (LimitAdj n) = LimitAdj (\j -> case n j of c :.: d -> l c :.: r d)
r \\ LimitAdj f = r \\ f
-- | @Colimit j@ ⊣ @Limit j@
instance (HasLimits j k) => Representable (LimitAdj (j :: a +-> b) :: REPK a k +-> COREPK b k) where
type LimitAdj j % r = COREP (RepCostar (Limit j (UN SUB r)))
index @c (LimitAdj n) =
Sub
( Op
( Prof \(RepCostar @o l) ->
cotabulate
( l
. index
( limitUniv @j @k
(\(g :.: j) -> case n j of c :.: r -> lmap (coindex c . index g) r)
(repUniv @(CorepStar (UN OP (UN SUB c))) @o)
)
)
)
)
repUniv @(SUB r) = LimitAdj (\j -> RepCostar (index @r (limit (repUniv :.: j))) :.: repUniv \\ j)
repMap (Sub n) = Sub (Op (mapRepCostar (mapLimit @j n)))
instance (HasColimits j k) => Corepresentable (LimitAdj (j :: a +-> b) :: REPK a k +-> COREPK b k) where
type LimitAdj j %% c = REP (CorepStar (Colimit j (UN OP (UN SUB c))))
corepUniv @(SUB (OP r)) = LimitAdj (\j -> corepUniv :.: CorepStar (coindex @r (colimit (j :.: corepUniv))) \\ j)
coindex @_ @r (LimitAdj n) =
Sub
( Prof \(CorepStar @o l) ->
tabulate
( coindex
( colimitUniv @j @k
(\(j :.: p) -> case n j of c :.: r -> rmap (coindex p . index r) c)
(corepUniv @(RepCostar (UN SUB r)) @o)
)
. l
)
)
rightAdjointPreservesLimits
:: forall {k} {k'} {i} {a} (j :: i +-> a) (p :: k +-> k') (d :: i +-> k)
. (Adjunction p, Representable d, HasLimits j k, HasLimits j k')
=> Limit j (p :.: d) :~> p :.: Limit j d
rightAdjointPreservesLimits lim@Objs =
corepUniv @p
:.: limitUniv @j @k @d
(\((f' :.: lim') :.: j) -> case limit @j @k' @(p :.: d) (lim' :.: j) of g' :.: d -> lmap (coindex g' . index f') d)
(repUniv @(CorepStar p) :.: lim)
rightAdjointPreservesLimitsInv
:: forall {k} {k'} {i} {a} (p :: k +-> k') (d :: i +-> k) (j :: i +-> a)
. (Representable p, Representable d, HasLimits j k, HasLimits j k')
=> p :.: Limit j d :~> Limit j (p :.: d)
rightAdjointPreservesLimitsInv = limitUniv @j @k' @(p :.: d) (\((p :.: lim) :.: j) -> p :.: limit (lim :.: j))
leftAdjointPreservesColimits
:: forall {k} {k'} {i} {a} (p :: k' +-> k) (d :: k +-> i) (j :: a +-> i)
. (Adjunction p, Corepresentable d, HasColimits j k, HasColimits j k')
=> Colimit j (d :.: p) :~> Colimit j d :.: p
leftAdjointPreservesColimits colim@Objs =
colimitUniv @j @k @d
(\(j :.: (colim' :.: g')) -> case colimit @j @k' @(d :.: p) (j :.: colim') of d :.: f' -> rmap (coindex g' . index f') d)
(colim :.: corepUniv @(RepCostar p))
:.: repUniv @p
leftAdjointPreservesColimitsInv
:: forall {k} {k'} {i} {a} (p :: k' +-> k) (d :: k +-> i) (j :: a +-> i)
. (Corepresentable p, Corepresentable d, HasColimits j k, HasColimits j k')
=> Colimit j d :.: p :~> Colimit j (d :.: p)
leftAdjointPreservesColimitsInv = colimitUniv @j @k' @(d :.: p) (\(j :.: (colim :.: p)) -> colimit (j :.: colim) :.: p)