packages feed

proarrow-0.1.0.0: src/Proarrow/Category/Instance/Coproduct.hs

-- | The coproduct (disjoint union) of two categories: the kind @'COPRODUCT' j k@ tags objects with
-- 'L' or 'R', and @p ':++:' q@ is the corresponding coproduct of profunctors, with no arrows between
-- the two sides. (Co)equalizers, pullbacks\/pushouts, (co)representability and dagger structure all
-- lift componentwise.
module Proarrow.Category.Instance.Coproduct where

import Data.Kind (Constraint)
import Prelude (type (~))

import Proarrow.Category.Enriched.Dagger (DaggerProfunctor (..))
import Proarrow.Category.Topos (HasEpiMonoFactorization (..))
import Proarrow.Colimit.Coequalizer (HasCoequalizers (..))
import Proarrow.Colimit.Pushout (HasPushouts (..))
import Proarrow.Core (CategoryOf (..), Profunctor (..), Promonad (..), type (+->))
import Proarrow.Functor (FunctorForRep (..))
import Proarrow.Limit.Equalizer (HasEqualizers (..))
import Proarrow.Limit.Pullback (HasPullbacks (..))
import Proarrow.Profunctor.Corepresentable (Corepresentable (..))
import Proarrow.Profunctor.Representable (Representable (..))

type data COPRODUCT j k = L j | R k

type (:++:) :: (j1 +-> k1) -> (j2 +-> k2) -> COPRODUCT j1 j2 +-> COPRODUCT k1 k2
data (:++:) p q a b where
  InjL :: p a b -> (p :++: q) (L a) (L b)
  InjR :: q a b -> (p :++: q) (R a) (R b)

type IsLR :: forall {j} {k}. COPRODUCT j k -> Constraint
class IsLR (a :: COPRODUCT j k) where
  lrCase :: (forall b. (a ~ L b, Ob b) => r) -> (forall b. (a ~ R b, Ob b) => r) -> r
instance (Ob a) => IsLR (L a :: COPRODUCT j k) where
  lrCase l _ = l
instance (Ob a) => IsLR (R a :: COPRODUCT j k) where
  lrCase _ r = r

instance (Profunctor p, Profunctor q) => Profunctor (p :++: q) where
  dimap (InjL f) (InjL g) (InjL p) = InjL (dimap f g p)
  dimap (InjR f) (InjR g) (InjR q) = InjR (dimap f g q)
  dimap InjL{} InjR{} p = case p of {}
  dimap InjR{} InjL{} q = case q of {}
  r \\ InjL p = r \\ p
  r \\ InjR q = r \\ q

-- | The coproduct of two promonads.
instance (Promonad p, Promonad q) => Promonad (p :++: q) where
  id @a = lrCase @a (InjL id) (InjR id)
  InjL p . InjL q = InjL (p . q)
  InjR q . InjR r = InjR (q . r)

-- | The coproduct of two categories.
instance (CategoryOf j, CategoryOf k) => CategoryOf (COPRODUCT j k) where
  type (~>) @(COPRODUCT j k) = (~>) @j :++: (~>) @k
  type Ob (a :: COPRODUCT j k) = IsLR a

instance (Representable p, Representable q) => Representable (p :++: q) where
  type (p :++: q) % L a = L (p % a)
  type (p :++: q) % R a = R (q % a)
  index (InjL p) = InjL (index p)
  index (InjR q) = InjR (index q)
  repUniv @a = lrCase @a (InjL (repUniv @p)) (InjR (repUniv @q))

instance (Corepresentable p, Corepresentable q) => Corepresentable (p :++: q) where
  type (p :++: q) %% L a = L (p %% a)
  type (p :++: q) %% R a = R (q %% a)
  coindex (InjL f) = InjL (coindex f)
  coindex (InjR f) = InjR (coindex f)
  corepUniv @a = lrCase @a (InjL (corepUniv @p)) (InjR (corepUniv @q))

instance (DaggerProfunctor p, DaggerProfunctor q) => DaggerProfunctor (p :++: q) where
  dagger = \case
    InjL f -> InjL (dagger f)
    InjR f -> InjR (dagger f)

-- | Morphisms of 'COPRODUCT' never cross sides, so this is a straight case split reusing either
-- @j@'s or @k@'s own equalizer.
instance (HasEqualizers j, HasEqualizers k) => HasEqualizers (COPRODUCT j k) where
  equalize (InjL f) (InjL g) k = equalize f g \e -> k (InjL e)
  equalize (InjR f) (InjR g) k = equalize f g \e -> k (InjR e)
  factorEqualizer (InjL incl) (InjL h) = InjL (factorEqualizer incl h)
  factorEqualizer (InjR incl) (InjR h) = InjR (factorEqualizer incl h)

-- | Dual to the 'HasEqualizers' instance above.
instance (HasCoequalizers j, HasCoequalizers k) => HasCoequalizers (COPRODUCT j k) where
  coequalize (InjL f) (InjL g) k = coequalize f g \c -> k (InjL c)
  coequalize (InjR f) (InjR g) k = coequalize f g \c -> k (InjR c)
  factorCoequalizer (InjL q) (InjL h) = InjL (factorCoequalizer q h)
  factorCoequalizer (InjR q) (InjR h) = InjR (factorCoequalizer q h)

instance (HasPullbacks j, HasPullbacks k) => HasPullbacks (COPRODUCT j k) where
  pullback (InjL f) (InjL g) k = pullback f g \p1 p2 -> k (InjL p1) (InjL p2)
  pullback (InjR f) (InjR g) k = pullback f g \p1 p2 -> k (InjR p1) (InjR p2)
  factorPullback (InjL p1) (InjL p2) (InjL k1) (InjL k2) = InjL (factorPullback p1 p2 k1 k2)
  factorPullback (InjR p1) (InjR p2) (InjR k1) (InjR k2) = InjR (factorPullback p1 p2 k1 k2)

instance (HasPushouts j, HasPushouts k) => HasPushouts (COPRODUCT j k) where
  pushout (InjL f) (InjL g) k = pushout f g \p1 p2 -> k (InjL p1) (InjL p2)
  pushout (InjR f) (InjR g) k = pushout f g \p1 p2 -> k (InjR p1) (InjR p2)
  factorPushout (InjL p1) (InjL p2) (InjL k1) (InjL k2) = InjL (factorPushout p1 p2 k1 k2)
  factorPushout (InjR p1) (InjR p2) (InjR k1) (InjR k2) = InjR (factorPushout p1 p2 k1 k2)

instance (HasPushouts j, HasEqualizers j, HasPushouts k, HasEqualizers k) => HasEpiMonoFactorization (COPRODUCT j k)

data family Lft :: j +-> COPRODUCT j k
instance (CategoryOf j, CategoryOf k) => FunctorForRep (Lft :: j +-> COPRODUCT j k) where
  type Lft @ a = L a
  fmap = InjL

data family Rgt :: k +-> COPRODUCT j k
instance (CategoryOf j, CategoryOf k) => FunctorForRep (Rgt :: k +-> COPRODUCT j k) where
  type Rgt @ a = R a
  fmap = InjR

data family Codiag :: COPRODUCT k k +-> k
instance (CategoryOf k) => FunctorForRep (Codiag :: COPRODUCT k k +-> k) where
  type Codiag @ L a = a
  type Codiag @ R a = a
  fmap = \case
    InjL f -> f
    InjR g -> g