packages feed

proarrow-0.1.0.0: src/Proarrow/Profunctor/Representable.hs

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

-- | Representable profunctors: profunctors of the shape /functor followed by hom/, identifying @p a b@
-- with @a ~> p % b@. Since a functor between different kinds cannot be written directly as a Haskell data
-- type, representable profunctors (with their functorial action '%') are how this library encodes such
-- functors; 'Rep' packages any 'Proarrow.Functor.FunctorForRep' as its representable profunctor.
module Proarrow.Profunctor.Representable where

import Data.Kind (Constraint)

import Proarrow.Category.Enriched.Thin (DecidableProfunctor (..), Thin, ThinProfunctor (..), mapDecision)
import Proarrow.Category.Instance.Bool (Booleans (..))
import Proarrow.Category.Instance.Opposite (OPPOSITE (..), Op (..))
import Proarrow.Category.Instance.Product ((:**:) (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Instance.Unit ()
import Proarrow.Core (CategoryOf (..), Hom, Profunctor (..), Promonad (..), lmap, rmap, (:~>), type (+->))
import Proarrow.Functor (FunctorForRep (..), Presheaf, withMappedOb)
import Proarrow.Object (Obj, obj, src, tgt)
import Proarrow.Optic (PIso, iso)
import Proarrow.Profunctor.Corepresentable (Corep (..), Corepresentable (..), corepUniv, dimapCorep, withObCorep)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Identity (Id (..))

infixl 8 %

-- | A profunctor is representable if @p ? a@ as a presheaf is representable in a functorial way over @a@.
type Representable :: forall {j} {k}. j +-> k -> Constraint
class (Profunctor p) => Representable (p :: j +-> k) where
  type p % (a :: j) :: k
  index :: p a b -> a ~> p % b
  tabulate :: (Ob b) => (a ~> p % b) -> p a b
  tabulate f = lmap f repUniv
  repMap :: (a ~> b) -> p % a ~> p % b
  repMap @a f = index @p (rmap f (repUniv @p @a)) \\ f
  repUniv :: (Ob a) => p (p % a) a
  repUniv @a = tabulate (repObj @p @a)
  {-# MINIMAL index, ((tabulate, repMap) | repUniv) #-}

instance Representable (->) where
  type (->) % a = a
  index f = f
  tabulate f = f
  repMap f = f
  repUniv = id

instance Representable Booleans where
  type Booleans % x = x
  index = id
  tabulate = id
  repMap = id

instance (Representable p, Representable q) => Representable (p :**: q) where
  type (p :**: q) % '(a, b) = '(p % a, q % b)
  index (p :**: q) = index p :**: index q
  tabulate (f :**: g) = tabulate f :**: tabulate g
  repMap (f :**: g) = repMap @p f :**: repMap @q g
  repUniv = repUniv :**: repUniv

instance (CategoryOf k) => Representable (Id :: k +-> k) where
  type Id % a = a
  index = unId
  tabulate = Id
  repMap = id

instance (Representable p, Representable q) => Representable (p :.: q) where
  type (p :.: q) % a = p % (q % a)
  index (p :.: q) = repMap @p (index q) . index p
  tabulate :: forall a b. (Ob b) => (a ~> ((p :.: q) % b)) -> (:.:) p q a b
  tabulate f = withObRep @q @b (tabulate f :.: tabulate id)
  repMap f = repMap @p (repMap @q f)

repObj :: forall p a. (Representable p, Ob a) => Obj (p % a)
repObj = repMap @p (obj @a)

withObRep :: forall p a r. (Representable p, Ob a) => ((Ob (p % a)) => r) -> r
withObRep r = r \\ repObj @p @a

dimapRep :: forall p a b c d. (Representable p) => (c ~> a) -> (b ~> d) -> p a b -> p c d
dimapRep l r = tabulate @p . dimap l (repMap @p r) . index \\ r

tabulated :: forall p a a' b b'. (Representable p, Ob b) => PIso (a ~> p % b) (a' ~> p % b') (p a b) (p a' b')
tabulated = iso tabulate index

-- | A representable presheaf is a contravariant representable functor in the Haskell sense.
type RepresentablePresheaf (f :: Presheaf k) = Representable f

type Key (f :: Presheaf k) = f % '()
tabulatedPresheaf :: (RepresentablePresheaf f, Ob a) => PIso (a ~> Key f) (a' ~> Key f) (f a '()) (f a' '())
tabulatedPresheaf = tabulated

instance (Representable p) => Corepresentable (Op p) where
  type Op p %% OP a = OP (p % a)
  coindex (Op f) = Op (index f)
  cotabulate (Op f) = Op (tabulate f)
  corepMap (Op f) = Op (repMap @p f)
  corepUniv = Op repUniv

instance (Corepresentable p) => Representable (Op p) where
  type Op p % OP a = OP (p %% a)
  index (Op f) = Op (coindex f)
  tabulate (Op f) = Op (cotabulate f)
  repMap (Op f) = Op (corepMap @p f)
  repUniv = Op corepUniv

-- | The corepresenting functor of @p@ repackaged with the opposite variance: a value is an arrow
-- @a '~>' p '%%' b@, making @'CorepStar' p@ 'Representable'.
type CorepStar :: (k +-> j) -> (j +-> k)
data CorepStar p a b where
  CorepStar :: (Ob b) => {unCorepStar :: a ~> p %% b} -> CorepStar p a b

instance (Corepresentable p) => Profunctor (CorepStar p) where
  dimap = dimapRep
  r \\ CorepStar f = r \\ f
instance (Corepresentable p) => Representable (CorepStar p) where
  type CorepStar p % a = p %% a
  index (CorepStar f) = f
  tabulate = CorepStar
  repMap = corepMap @p

mapCorepStar :: (Corepresentable p, Corepresentable q) => p ~> q -> CorepStar q ~> CorepStar p
mapCorepStar (Prof n) = Prof \(CorepStar @a f) -> CorepStar (coindex (n (corepUniv @_ @a)) . f)

-- | The representing functor of @p@ repackaged with the opposite variance: a value is an arrow
-- @p '%' a '~>' b@, making @'RepCostar' p@ 'Corepresentable'.
type RepCostar :: (k +-> j) -> (j +-> k)
data RepCostar p a b where
  RepCostar :: (Ob a) => {unRepCostar :: p % a ~> b} -> RepCostar p a b

instance (Representable p) => Profunctor (RepCostar p) where
  dimap = dimapCorep
  r \\ RepCostar f = r \\ f
instance (Representable p) => Corepresentable (RepCostar p) where
  type RepCostar p %% a = p % a
  coindex (RepCostar f) = f
  cotabulate = RepCostar
  corepMap = repMap @p
instance (Representable p, Thin j) => ThinProfunctor (RepCostar p :: j +-> k) where
  type HasArrow (RepCostar p :: j +-> k) a b = HasArrow (Hom j) (p % a) b
  arr @a = withObRep @p @a (RepCostar arr)
  withArr (RepCostar f) r = withArr f r

instance (Representable p, DecidableProfunctor (Hom j)) => DecidableProfunctor (RepCostar p :: j +-> k) where
  type Holds (RepCostar p :: j +-> k) a b = Holds (Hom j) (p % a) b
  decide @a @b = withObRep @p @a (mapDecision RepCostar (decide @(Hom j) @(p % a) @b))
  toHolds (RepCostar f) r = toHolds f r

-- | @'CorepStar' p a b@ holds in a thin category exactly when @a ≤ p %% b@.
instance (Corepresentable p, Thin k) => ThinProfunctor (CorepStar p :: j +-> k) where
  type HasArrow (CorepStar p :: j +-> k) a b = HasArrow (Hom k) a (p %% b)
  arr @_ @b = withObCorep @p @b (CorepStar arr)
  withArr (CorepStar f) r = withArr f r

instance (Corepresentable p, DecidableProfunctor (Hom k)) => DecidableProfunctor (CorepStar p :: j +-> k) where
  type Holds (CorepStar p :: j +-> k) a b = Holds (Hom k) a (p %% b)
  decide @a @b = withObCorep @p @b (mapDecision CorepStar (decide @(Hom k) @a @(p %% b)))
  toHolds (CorepStar f) r = toHolds f r

mapRepCostar :: (Representable p, Representable q) => p ~> q -> RepCostar q ~> RepCostar p
mapRepCostar (Prof n) = Prof \(RepCostar @a f) -> RepCostar (f . index (n (repUniv @_ @a)))

flipRep :: forall p. (Representable p) => (~>) :~> p -> RepCostar p :~> (~>)
flipRep n p = coindex p . index (n (src p))

unflipRep :: forall p. (Representable p) => RepCostar p :~> (~>) -> (~>) :~> p
unflipRep n f = tabulate (n (RepCostar (repMap @p f))) \\ f

flipCorep :: forall p. (Corepresentable p) => (~>) :~> p -> CorepStar p :~> (~>)
flipCorep n p = coindex (n (tgt p)) . index p

unflipCorep :: forall p. (Corepresentable p) => CorepStar p :~> (~>) -> (~>) :~> p
unflipCorep n f = cotabulate (n (CorepStar (corepMap @p f))) \\ f

-- | The representable profunctor of a functor-for-representation @f@ ('FunctorForRep'): a value
-- @'Rep' f a b@ is an arrow @a '~>' f \@ b@. This is the profunctor encoding of
-- functors used throughout the library.
type Rep :: (j +-> k) -> j +-> k
data Rep f a b where
  Rep :: forall b f a. (Ob b) => {unRep :: a ~> f @ b} -> Rep f a b

instance (FunctorForRep f) => Profunctor (Rep f) where
  dimap = dimapRep
  r \\ Rep f = r \\ f
instance (FunctorForRep f) => Representable (Rep f) where
  type Rep f % a = f @ a
  index (Rep f) = f
  tabulate = Rep
  repMap = fmap @f
instance (FunctorForRep f, Thin k) => ThinProfunctor (Rep f :: j +-> k) where
  type HasArrow (Rep f :: j +-> k) a b = HasArrow (Hom k) a (f @ b)
  arr @_ @b = withMappedOb @f @b (Rep arr)
  withArr (Rep f) r = withArr f r

instance (FunctorForRep f, DecidableProfunctor (Hom k)) => DecidableProfunctor (Rep f :: j +-> k) where
  type Holds (Rep f :: j +-> k) a b = Holds (Hom k) a (f @ b)
  decide @a @b = withMappedOb @f @b (mapDecision Rep (decide @(Hom k) @a @(f @ b)))
  toHolds (Rep f) r = toHolds f r

rep :: forall f a b a' b'. (FunctorForRep f, Ob b) => PIso (a ~> f @ b) (a' ~> f @ b') (Rep f a b) (Rep f a' b')
rep = tabulated

instance (FunctorForRep f, Promonad (Corep f)) => Promonad (RepCostar (Rep f)) where
  id @b = RepCostar (unCorep (id @(Corep f) @b))
  RepCostar @a l . RepCostar @b r = RepCostar (unCorep (Corep @a @f l . Corep @b @f r))