proarrow-0.1.0.0: src/Proarrow/Category/Instance/Duploid.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | The __duploid__ of an adjunction (Munch-Maccagnoni): objects are the positive ('P') and
-- negative ('N') objects of the adjunction's two categories, and a hom @x '~>' y@ is an element
-- @adj ('Pos' x) ('Neg' y)@ of the adjunction profunctor. Composition is biased by the polarity of
-- the middle object ('(•)' through the positive side, '(◦)' through the negative side) and is
-- __not associative in general__. The 'Promonad' instance is a deliberate abuse.
module Proarrow.Category.Instance.Duploid where
import Data.Kind (Constraint)
import Proarrow.Adjunction (AdjMonad, Adjunction)
import Proarrow.Category.Monoidal (Monoidal (..), MonoidalProfunctor (..), StrongMonoidalCorep, SymMonoidal (..))
import Proarrow.Colimit.BinaryCoproduct (HasBinaryCoproducts (..))
import Proarrow.Core
( CAT
, CategoryOf (..)
, Kind
, Profunctor (..)
, Promonad (..)
, dimapDefault
, lmap
, obj
, rmap
, ($)
, (//)
, type (+->)
)
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Profunctor.Corepresentable (Corepresentable (..), corepUniv, withObCorep)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Representable (CorepStar (..), Representable (..), repUniv, withObRep)
type DUPLOID :: forall {n} {p}. n +-> p -> Kind
type data DUPLOID (adj :: n +-> p) = N n | P p
data SDuploidObj x where
SN :: (Ob x) => SDuploidObj (N x)
SP :: (Ob x) => SDuploidObj (P x)
type IsPN :: forall {n} {p} {adj}. DUPLOID (adj :: n +-> p) -> Constraint
class IsPN x where
pn :: SDuploidObj x
withPosOb :: ((Ob (Pos x)) => r) -> r
withNegOb :: ((Ob (Neg x)) => r) -> r
instance (Ob x, Corepresentable adj) => IsPN (P x :: DUPLOID adj) where
pn = SP
withPosOb r = r
withNegOb r = withObCorep @adj @x r
instance (Ob x, Representable adj) => IsPN (N x :: DUPLOID adj) where
pn = SN
withPosOb r = withObRep @adj @x r
withNegOb r = r
type family Pos (x :: DUPLOID (adj :: n +-> p)) :: p where
Pos (P a) = a
Pos (N a :: DUPLOID adj) = adj % a
type family Neg (x :: DUPLOID (adj :: n +-> p)) :: n where
Neg (P a :: DUPLOID adj) = adj %% a
Neg (N a) = a
type Duploid :: CAT (DUPLOID adj)
data Duploid x y where
Duploid :: forall {adj} (x :: DUPLOID adj) y. (Ob x, Ob y) => adj (Pos x) (Neg y) -> Duploid x y
instance (Adjunction adj) => Profunctor (Duploid :: CAT (DUPLOID adj)) where
dimap = dimapDefault
r \\ Duploid{} = r
-- | ATTENTION: a duploid is not associative, so not really a promonad/category!
instance (Adjunction adj) => Promonad (Duploid :: CAT (DUPLOID adj)) where
id @x = Duploid case pn @x of
SP -> corepUniv
SN -> repUniv
g@(Duploid @y _) . f = case pn @y of
SP -> g • f
SN -> g ◦ f
-- | The duploid of an adjunction, with polarized objects. Deliberately unlawful: composition is
-- polarity-biased and not associative (see the warning on the 'Promonad' instance above).
instance (Adjunction adj) => CategoryOf (DUPLOID adj) where
type (~>) = Duploid
type Ob x = IsPN x
(•) :: forall {adj} (x :: DUPLOID adj) y z. (Corepresentable adj) => P y ~> z -> x ~> P y -> x ~> z
Duploid g • Duploid f = Duploid (rmap (coindex g) f)
(◦) :: forall {adj} (x :: DUPLOID adj) y z. (Representable adj) => N y ~> z -> x ~> N y -> x ~> z
Duploid g ◦ Duploid f = Duploid (lmap (index f) g)
fromThunkable :: forall {adj} (x :: DUPLOID adj) y. (Adjunction adj, Ob x, Ob y) => Pos x ~> Pos y -> x ~> y
fromThunkable f = Duploid (case pn @y of SN -> tabulate f; SP -> cotabulate (corepMap @adj f)) \\ f
fromLinear :: forall {adj} (x :: DUPLOID adj) y. (Adjunction adj, Ob x, Ob y) => Neg x ~> Neg y -> x ~> y
fromLinear f = Duploid (case pn @x of SN -> tabulate (repMap @adj f); SP -> cotabulate f) \\ f
type Dn x = P (Pos x)
down :: forall {adj} (x :: DUPLOID adj). (Adjunction adj, Ob x) => x ~> Dn x
down = withPosOb @x $ Duploid corepUniv
undown :: forall {adj} (x :: DUPLOID adj). (Adjunction adj, Ob x) => Dn x ~> x
undown = withPosOb @x $ fromThunkable (obj @(Pos x))
mapDown :: forall {adj} (x :: DUPLOID adj) y. (Adjunction adj) => x ~> y -> Dn x ~> (Dn y :: DUPLOID adj)
mapDown f = down . f . undown \\ f
type Up x = N (Neg x)
unup :: forall {adj} (x :: DUPLOID adj). (Adjunction adj, Ob x) => Up x ~> x
unup = withNegOb @x $ Duploid repUniv
up :: forall {adj} (x :: DUPLOID adj). (Adjunction adj, Ob x) => x ~> Up x
up = withNegOb @x $ fromLinear (obj @(Neg x))
mapUp :: forall {adj} (x :: DUPLOID adj) y. (Adjunction adj) => x ~> y -> Up x ~> (Up y :: DUPLOID adj)
mapUp f = up . f . unup \\ f
instance (Adjunction (adj :: n +-> p), StrongMonoidalCorep adj) => MonoidalProfunctor (Duploid :: CAT (DUPLOID adj)) where
one = id
(**) @_ @x2 @_ @y2 f g =
f // g // case (down . f, down . g) of
(Duploid fp, Duploid gp) ->
withPosOb @x2 $
withPosOb @y2 $
withOb2 @_ @(Pos x2) @(Pos y2) $
withObCorep @adj @(Pos x2 ** Pos y2) $
case lmap (index fp) (repUniv @(AdjMonad adj) @(Pos x2))
** lmap (index gp) (repUniv @(AdjMonad adj) @(Pos y2)) of
fg :.: CorepStar h -> Duploid (rmap h fg) \\ fg
instance (Adjunction (adj :: n +-> p), StrongMonoidalCorep adj) => Monoidal (DUPLOID adj) where
type x ** y = P (Pos x ** Pos y)
type Unit = P Unit
withOb2 @a @b r = withPosOb @a $ withPosOb @b $ withOb2 @_ @(Pos a) @(Pos b) r
leftUnitor @a = withPosOb @a $ withOb2 @_ @Unit @(Pos a) $ fromThunkable (leftUnitor @_ @(Pos a))
leftUnitorInv @a = withPosOb @a $ withOb2 @_ @Unit @(Pos a) $ fromThunkable (leftUnitorInv @_ @(Pos a))
rightUnitor @a = withPosOb @a $ withOb2 @_ @(Pos a) @Unit $ fromThunkable (rightUnitor @_ @(Pos a))
rightUnitorInv @a = withPosOb @a $ withOb2 @_ @(Pos a) @Unit $ fromThunkable (rightUnitorInv @_ @(Pos a))
associator @a @b @c =
withPosOb @a $
withPosOb @b $
withPosOb @c $
withOb2 @_ @(Pos a) @(Pos b) $
withOb2 @_ @(Pos a ** Pos b) @(Pos c) $
withOb2 @_ @(Pos b) @(Pos c) $
withOb2 @_ @(Pos a) @(Pos b ** Pos c) $
fromThunkable (associator @_ @(Pos a) @(Pos b) @(Pos c))
associatorInv @a @b @c =
withPosOb @a $
withPosOb @b $
withPosOb @c $
withOb2 @_ @(Pos a) @(Pos b) $
withOb2 @_ @(Pos a ** Pos b) @(Pos c) $
withOb2 @_ @(Pos b) @(Pos c) $
withOb2 @_ @(Pos a) @(Pos b ** Pos c) $
fromThunkable (associatorInv @_ @(Pos a) @(Pos b) @(Pos c))
type StrongSymMonAdj (adj :: n +-> p) = (Adjunction adj, StrongMonoidalCorep adj, SymMonoidal p)
instance (StrongSymMonAdj adj) => SymMonoidal (DUPLOID adj) where
swap @a @b =
withPosOb @a $
withPosOb @b $
withOb2 @_ @(Pos a) @(Pos b) $
withOb2 @_ @(Pos b) @(Pos a) $
fromThunkable (swap @_ @(Pos a) @(Pos b))
instance (HasBinaryCoproducts p, Adjunction adj) => HasBinaryCoproducts (DUPLOID (adj :: n +-> p)) where
type a || b = P (Pos a || Pos b)
withObCoprod @a @b r = withPosOb @a $ withPosOb @b $ withObCoprod @p @(Pos a) @(Pos b) r
lft @a @b = withPosOb @a $ withPosOb @b $ withObCoprod @p @(Pos a) @(Pos b) $ fromThunkable (lft @p @(Pos a) @(Pos b))
rgt @a @b = withPosOb @a $ withPosOb @b $ withObCoprod @p @(Pos a) @(Pos b) $ fromThunkable (rgt @p @(Pos a) @(Pos b))
Duploid @x @a f ||| Duploid @y g =
withPosOb @x $
withPosOb @y $
withObCoprod @p @(Pos x) @(Pos y) $
withNegOb @a $
Duploid (tabulate (index f ||| index g))
instance (HasBinaryProducts n, Adjunction adj) => HasBinaryProducts (DUPLOID (adj :: n +-> p)) where
type a && b = N (Neg a && Neg b)
withObProd @a @b r = withNegOb @a $ withNegOb @b $ withObProd @n @(Neg a) @(Neg b) r
fst @a @b = withNegOb @a $ withNegOb @b $ withObProd @n @(Neg a) @(Neg b) $ fromLinear (fst @n @(Neg a) @(Neg b))
snd @a @b = withNegOb @a $ withNegOb @b $ withObProd @n @(Neg a) @(Neg b) $ fromLinear (snd @n @(Neg a) @(Neg b))
Duploid @a @x f &&& Duploid @_ @y g =
withNegOb @x $
withNegOb @y $
withObProd @n @(Neg x) @(Neg y) $
withPosOb @a $
Duploid (cotabulate (coindex f &&& coindex g))