packages feed

proarrow-0.1.0.0: src/Proarrow/Limit/Power.hs

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

-- | Powers (cotensors) of a category enriched in @v@: 'Powered' provides @a '^' n@, characterized by the
-- isomorphism between @a '~>' b '^' n@ and @n '~>' 'HomObj' v a b@ ('power'\/'unpower').
module Proarrow.Limit.Power where

import Data.Kind (Type)
import Prelude (($), type (~))

import Proarrow.Category.Enriched (Enriched, EnrichedProfunctor (..), GenArrow (..), HomObj, comp)
import Proarrow.Category.Enriched.Finitary (Elt (..))
import Proarrow.Category.Instance.FinHask (FINHASK, arr)
import Proarrow.Category.Instance.Opposite (OPPOSITE (..))
import Proarrow.Category.Instance.Product ((:**:) (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Instance.Unit qualified as U
import Proarrow.Category.Monoidal (SymMonoidal (..), leftUnitorInvWith)
import Proarrow.Category.Monoidal.Cartesian (Cartesian)
import Proarrow.Category.Monoidal.Closed (Closed (..), uncurry)
import Proarrow.Core (CategoryOf (..), Ob, Profunctor (..), Promonad (..), obj, (//), type (+->))
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Limit.Terminal (HasTerminalObject, TerminalObject, terminate)
import Proarrow.Profunctor.Representable (Representable (..))

-- | Categories powered over @v@.
class (Enriched v k) => Powered v k where
  type (a :: k) ^ (n :: v) :: k
  withObPower :: (Ob (a :: k), Ob (n :: v)) => ((Ob (a ^ n)) => r) -> r
  power :: (Ob (a :: k), Ob b) => (n ~> HomObj v a b) -> a ~> (b ^ n)
  unpower :: (Ob (b :: k), Ob n) => a ~> (b ^ n) -> n ~> HomObj v a b

mapBase :: forall {k} {v} (n :: v) (a :: k) b. (Powered v k, Ob n) => a ~> b -> a ^ n ~> b ^ n
mapBase f =
  f //
    withObPower @v @k @a @n
      ( power @v @k @(a ^ n) @b @n
          (let g = unpower @v @k @a @n id in g // comp @v @(a ^ n) @a @b . leftUnitorInvWith (underlying @v f) . g)
      )

mapPower :: forall {k} {v} (a :: k) (n :: v) m. (Powered v k, Ob a) => (n ~> m) -> a ^ m ~> a ^ n
mapPower f = withObPower @v @k @a @m (power @v @k @(a ^ m) @a @n (unpower @v @k @a id . f)) \\ f

selfPowered :: forall {v} (a :: v) b n. (Closed v, SymMonoidal v, Ob a, Ob b) => n ~> (a ~~> b) -> a ~> (n ~~> b)
selfPowered f = curry @_ @a @n @b (uncurry @a f . swap @_ @a @n) \\ f

selfUnpowered :: forall {v} (a :: v) b n. (Closed v, SymMonoidal v, Ob n, Ob b) => a ~> (n ~~> b) -> n ~> (a ~~> b)
selfUnpowered f = curry @_ @n @a @b (uncurry @n f . swap @_ @n @a) \\ f

instance Powered Type Type where
  type a ^ n = n -> a
  withObPower r = r
  power f a n = f n a
  unpower f n a = f a n

instance (Enriched v (), HomObj v '() '() ~ TerminalObject, HasTerminalObject v) => Powered v () where
  type a ^ n = '()
  withObPower r = r
  power _ = U.Unit
  unpower U.Unit = terminate

class (HomObj v '(a1, a2) '(b1, b2) ~ (HomObj v a1 b1 && HomObj v a2 b2)) => HomObjIsProduct v a1 a2 b1 b2
instance (HomObj v '(a1, a2) '(b1, b2) ~ (HomObj v a1 b1 && HomObj v a2 b2)) => HomObjIsProduct v a1 a2 b1 b2
instance
  ( Powered v j
  , Powered v k
  , Enriched v (j, k)
  , forall (a :: (j, k)) (b :: (j, k)) a1 a2 b1 b2
     . (Ob a, a ~ '(a1, a2), Ob b, b ~ '(b1, b2)) => HomObjIsProduct v a1 a2 b1 b2
  , Cartesian v
  )
  => Powered v (j, k)
  where
  type '(a1, a2) ^ n = '(a1 ^ n, a2 ^ n)
  withObPower @'(a, b) @n r = withObPower @v @j @a @n (withObPower @v @k @b @n r)
  power @'(a1, a2) @'(b1, b2) f =
    withProObj @v @(~>) @a1 @b1 $
      withProObj @v @(~>) @a2 @b2 $
        power @v @j @a1 @b1 (fst @_ @(HomObj v a1 b1) @(HomObj v a2 b2) . f)
          :**: power @v @k @a2 @b2 (snd @_ @(HomObj v a1 b1) @(HomObj v a2 b2) . f)
  unpower @'(b1, b2) @n (f :**: g) = unpower @v @j @b1 @n f &&& unpower @v @k @b2 @n g \\ f \\ g

data (p :^: n) a b where
  Power :: (Ob a, Ob b) => {unPower :: n -> p a b} -> (p :^: n) a b
instance (Profunctor p) => Profunctor (p :^: n) where
  dimap l r (Power f) = l // r // Power \n -> dimap l r (f n)
  r \\ Power{} = r
instance (CategoryOf j, CategoryOf k) => Powered Type (j +-> k) where
  type a ^ n = a :^: n
  withObPower r = r
  power f = Prof \p -> p // Power \n -> unProf (f n) p
  unpower (Prof f) n = Prof \p -> unPower (f p) n

-- | Kept with the class: 'FINHASK' is above "Proarrow.Category.Enriched", which this module needs.
instance Powered FINHASK FINHASK where
  type a ^ n = n ~~> a
  withObPower @a @n r = withObExp @_ @a @n r
  power @a @b f = selfPowered @a @b (arr unElt . f)
  unpower f = (\g -> arr Elt . g) (selfUnpowered f) \\ f

instance (Powered v k, Ob (n :: v)) => Representable (GenArrow (OP (n :: v)) :: k +-> k) where
  type GenArrow (OP n) % a = a ^ n
  index (GenArrow @a @b f) = power @v @k @a @b f
  repUniv @a = withObPower @v @k @a @n (GenArrow (unpower @v @k @a (obj @(a ^ n))))