proarrow-0.1.0.0: src/Proarrow/Category/Instance/Kleisli.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | The __Kleisli category__ of a 'Promonad' @p@: objects are those of the base category (wrapped
-- in 'KL') and a morphism @'KL' a '~>' 'KL' b@ is an element @p a b@, composed with @p@'s own
-- composition. Terminal\/initial objects, (co)products, monoidal and
-- 'Proarrow.Category.Monoidal.CopyDiscard.CopyDiscard' structure lift from the base category when
-- @p@ cooperates (e.g. is a 'Proarrow.Category.Monoidal.MonoidalProfunctor').
module Proarrow.Category.Instance.Kleisli
( KLEISLI (..)
, Kleisli (..)
, arr
, KleisliFree (..)
, KleisliForget (..)
, LIFTEDF
, pattern LiftF
) where
import Proarrow.Adjunction (Proadjunction)
import Proarrow.Adjunction qualified as Adj
import Proarrow.Category.Enriched.Dagger (DaggerProfunctor (..))
import Proarrow.Category.Enriched.Thin (DecidableProfunctor (..), mapDecision)
import Proarrow.Category.Enriched.Thin qualified as T
import Proarrow.Category.Monoidal (Monoidal (..), MonoidalProfunctor (..), SymMonoidal (..))
import Proarrow.Category.Monoidal.Cartesian (Cartesian)
import Proarrow.Category.Monoidal.CopyDiscard (CopyDiscard (..))
import Proarrow.Category.Monoidal.Distributive (Distributive (..), DistributiveProfunctor)
import Proarrow.Colimit.BinaryCoproduct (Coprod, HasBinaryCoproducts (..), codiag, (++))
import Proarrow.Colimit.Initial (HasInitialObject (..))
import Proarrow.Core
( CAT
, CategoryOf (..)
, Profunctor (..)
, Promonad (..)
, UN
, WrappedOb
, dimapDefault
, lmap
, rmap
, type (+->)
)
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..), diag)
import Proarrow.Limit.Terminal (HasTerminalObject (..))
import Proarrow.Monoid (CocommutativeComonoid, Comonoid (..))
import Proarrow.Object (tgt, pattern Obj, type Obj)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Representable (RepCostar (..), Representable (..), repUniv)
import Proarrow.Promonad (Comonad, Monad)
type data KLEISLI (p :: CAT k) = KL k
-- | The arrows of the promonad @p@, wrapped as a category on the 'KLEISLI'-wrapped kind.
type Kleisli :: CAT (KLEISLI p)
data Kleisli (a :: KLEISLI p) b where
Kleisli :: {unKleisli :: p a b} -> Kleisli (KL a :: KLEISLI p) (KL b)
instance (Promonad p) => Profunctor (Kleisli :: CAT (KLEISLI p)) where
dimap = dimapDefault
r \\ Kleisli p = r \\ p
arr :: (Promonad p) => a ~> b -> Kleisli (KL a :: KLEISLI p) (KL b)
arr f = Kleisli (rmap f id) \\ f
-- | Every promonad makes a category.
instance (Promonad p) => CategoryOf (KLEISLI p) where
type (~>) = Kleisli
type Ob a = WrappedOb KL a
instance (Promonad p) => Promonad (Kleisli :: CAT (KLEISLI p)) where
id = Kleisli id
Kleisli f . Kleisli g = Kleisli (f . g)
-- | The terminal object lifts to the co-Kleisli category of a 'Comonad': there @p a b@ is
-- @p '%%' a '~>' b@, so @p a (-)@ is representable and preserves limits. A bare 'Promonad' is not
-- enough. At the constant promonad @'Proarrow.Profunctor.Instance.HaskValue.HaskValue' c@ every
-- element of @c@ is an arrow into the terminal object, so uniqueness fails.
instance (HasTerminalObject k, Comonad p) => HasTerminalObject (KLEISLI (p :: k +-> k)) where
type TerminalObject @(KLEISLI (p :: k +-> k)) = KL (TerminalObject :: k)
terminate = arr terminate
-- | Dually, the initial object lifts to the Kleisli category of a 'Monad': there @p a b@ is
-- @a '~>' p '%' b@, so the presheaf @p (-) z@ is representable and takes colimits in @k@ to limits.
instance (HasInitialObject k, Monad p) => HasInitialObject (KLEISLI (p :: k +-> k)) where
type InitialObject @(KLEISLI (p :: k +-> k)) = KL (InitialObject :: k)
initiate = arr initiate
-- | Products lift for the same reason as the terminal object: for a 'Comonad' @p a (-)@ is
-- representable, and @'lmap' 'diag' (f '**' g)@ is then the canonical mediating map.
instance (Cartesian k, Comonad p, MonoidalProfunctor p) => HasBinaryProducts (KLEISLI (p :: k +-> k)) where
type a && b = KL (UN KL a && UN KL b)
withObProd @(KL a) @(KL b) r = withObProd @k @a @b r
fst @(KL a) @(KL b) = arr (fst @_ @a @b)
snd @(KL a) @(KL b) = arr (snd @_ @a @b)
Kleisli f &&& Kleisli g = Kleisli (lmap diag (f ** g)) \\ f
-- | Coproducts lift for the same reason as the initial object: for a 'Monad' @p (-) z@ is a
-- representable presheaf.
instance
(HasBinaryCoproducts k, Monad p, MonoidalProfunctor (Coprod p))
=> HasBinaryCoproducts (KLEISLI (p :: k +-> k))
where
type a || b = KL (UN KL a || UN KL b)
withObCoprod @(KL a) @(KL b) r = withObCoprod @k @a @b r
lft @(KL a) @(KL b) = arr (lft @_ @a @b)
rgt @(KL a) @(KL b) = arr (rgt @_ @a @b)
Kleisli f ||| Kleisli g = Kleisli (rmap codiag (f ++ g)) \\ f
instance (Promonad p, MonoidalProfunctor p) => MonoidalProfunctor (Kleisli :: CAT (KLEISLI (p :: k +-> k))) where
one = Kleisli one
Kleisli f ** Kleisli g = Kleisli (f ** g)
-- | If the promonad is a monoidal profunctor, then its Kleisli category is a monoidal category.
instance (Promonad p, MonoidalProfunctor p) => Monoidal (KLEISLI (p :: k +-> k)) where
type Unit @(KLEISLI (p :: k +-> k)) = KL (Unit :: k)
type a ** b = KL (UN KL a ** UN KL b)
withOb2 @(KL a) @(KL b) r = withOb2 @k @a @b r
leftUnitor = arr leftUnitor
leftUnitorInv = arr leftUnitorInv
rightUnitor = arr rightUnitor
rightUnitorInv = arr rightUnitorInv
associator @(KL a) @(KL b) @(KL c) = arr (associator @k @a @b @c)
associatorInv @(KL a) @(KL b) @(KL c) = arr (associatorInv @k @a @b @c)
instance (Promonad p, MonoidalProfunctor p, SymMonoidal k) => SymMonoidal (KLEISLI (p :: k +-> k)) where
swap @(KL a) @(KL b) = arr (swap @k @a @b)
instance (Promonad p, MonoidalProfunctor p, CopyDiscard k, Ob (a :: KLEISLI p)) => Comonoid (a :: KLEISLI (p :: k +-> k)) where
counit = discard
comult = copy
instance
(Promonad p, MonoidalProfunctor p, CopyDiscard k, Ob (a :: KLEISLI p))
=> CocommutativeComonoid (a :: KLEISLI (p :: k +-> k))
instance (Promonad p, MonoidalProfunctor p, CopyDiscard k) => CopyDiscard (KLEISLI (p :: k +-> k)) where
copy = arr copy
discard = arr discard
instance (Distributive k, Monad p, DistributiveProfunctor p) => Distributive (KLEISLI (p :: k +-> k)) where
distL @(KL a) @(KL b) @(KL c) = arr (distL @k @a @b @c)
distR @(KL a) @(KL b) @(KL c) = arr (distR @k @a @b @c)
absorbL @(KL a) = arr (absorbL @k @a)
absorbR @(KL a) = arr (absorbR @k @a)
instance (DaggerProfunctor p, Promonad p) => DaggerProfunctor (Kleisli :: CAT (KLEISLI p)) where
dagger (Kleisli p) = Kleisli (dagger p)
instance (T.ThinProfunctor p, Promonad p) => T.ThinProfunctor (Kleisli :: CAT (KLEISLI p)) where
type HasArrow (Kleisli :: CAT (KLEISLI p)) (KL a) (KL b) = T.HasArrow p a b
arr = Kleisli T.arr
withArr (Kleisli p) r = T.withArr p r
instance (DecidableProfunctor p, Promonad p) => DecidableProfunctor (Kleisli :: CAT (KLEISLI p)) where
type Holds (Kleisli :: CAT (KLEISLI p)) (KL a) (KL b) = Holds p a b
decide @(KL a) @(KL b) = mapDecision Kleisli (decide @p @a @b)
toHolds (Kleisli p) r = toHolds p r
-- | The Kleisli category has the objects of @k@, numbered the same way, so a Kleisli category of a
-- decidable promonad on an enumerable category is itself enumerable, and so can be searched, or
-- closed ("Proarrow.Category.Enriched.Thin.Composition").
instance (T.Indexed k) => T.Indexed (KLEISLI (p :: CAT k)) where
type Index (a :: KLEISLI p) = T.Index (UN KL a)
type At (KLEISLI (p :: CAT k)) i = T.FmapWrap KL (T.At k i)
instance (T.Finite k) => T.Finite (KLEISLI (p :: CAT k)) where
type Objects (KLEISLI (p :: CAT k)) = T.MapWrap KL (T.Objects k)
finite = T.wrapFinite @KL
withAtLookup = T.withWrapAtLookup @KL
instance (T.Enumerable k, Promonad p) => T.Enumerable (KLEISLI (p :: CAT k)) where
withIndex @(KL a) r = T.withIndex @k @a r
atOb i = case T.atOb @k i of
T.AtJust -> T.AtJust
T.AtNothing -> T.AtNothing
-- | The free half of the Kleisli adjunction ('Proadjunction' below), embedding @k@ into the
-- Kleisli category of @p@.
type KleisliFree :: forall (p :: k +-> k) -> k +-> KLEISLI p
data KleisliFree p a b where
KleisliFree :: p a b -> KleisliFree p (KL a) b
instance (Promonad p) => Profunctor (KleisliFree p) where
dimap (Kleisli l) r (KleisliFree p) = KleisliFree (rmap r p . l)
r \\ KleisliFree p = r \\ p
-- | The forgetful half of the Kleisli adjunction, mapping Kleisli objects back to @k@.
type KleisliForget :: forall (p :: k +-> k) -> KLEISLI p +-> k
data KleisliForget p a b where
KleisliForget :: p a b -> KleisliForget p a (KL b)
instance (Promonad p) => Profunctor (KleisliForget p) where
dimap l (Kleisli r) (KleisliForget p) = KleisliForget (r . lmap l p)
r \\ KleisliForget p = r \\ p
instance (Promonad p) => Proadjunction (KleisliFree p) (KleisliForget p) where
unit = KleisliForget id :.: KleisliFree id
counit (KleisliFree p :.: KleisliForget q) = Kleisli (q . p)
-- | Categories lifted by a representable profunctor: @f % a ~> f % b@ are kleisli categories on promonads induced by @f@.
type LIFTEDF (f :: j +-> k) = KLEISLI (RepCostar f :.: f)
unlift :: (Representable f) => Kleisli (KL a :: LIFTEDF f) (KL b) -> (f % a ~> f % b, Obj a, Obj b)
unlift (Kleisli (RepCostar f :.: g)) = (index g . f, Obj, tgt g)
pattern LiftF
:: (Representable (f :: j +-> k)) => (Ob (a :: j), Ob b) => (f % a ~> f % b) -> Kleisli (KL a :: LIFTEDF f) (KL b)
pattern LiftF f <- (unlift -> (f, Obj, Obj))
where
LiftF f = Kleisli (RepCostar f :.: repUniv)
{-# COMPLETE LiftF #-}