packages feed

proarrow-0.1.0.0: src/Proarrow/Profunctor/Instance/PastroTambara.hs

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

-- | 'Pastro' and 'Tambara' are the free and cofree 'Prostrong' profunctors for an optic flavor @w@ (the
-- 'HasFree' and 'HasCofree' instances for @'Prostrong' w@): @Pastro w r@ sandwiches @r@ between an
-- existential witness pair, while @Tambara w r@ provides strength against every witness pair at once.
module Proarrow.Profunctor.Instance.PastroTambara where

import Prelude (($))

import Proarrow.Category.Instance.Opposite (OPPOSITE (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Core (CategoryOf (..), OB, Profunctor (..), Promonad (..), src, tgt, (//), (:~>), type (+->))
import Proarrow.Functor (Functor (..))
import Proarrow.Optic (ExOptic (..), FLAVOR, Flavor, Prostrong (..))
import Proarrow.Profunctor.Cofree (HasCofree (..), cofreeComp)
import Proarrow.Profunctor.Corepresentable (Corepresentable (..))
import Proarrow.Profunctor.Free (HasFree (..), freeComp)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Costar (Costar, pattern Costar)
import Proarrow.Profunctor.Instance.Identity (Id (..))
import Proarrow.Profunctor.Instance.Ran (Ran (..), runRan, type (|>))
import Proarrow.Profunctor.Instance.Rift (Rift (..), runRift, type (<|))
import Proarrow.Profunctor.Instance.Star (Star, pattern Star)
import Proarrow.Profunctor.Instance.Yoneda (Yo (..))

-- | The free 'Prostrong' profunctor for the flavor @w@: the profunctor @r@ sandwiched between an
-- existential @w@-witness pair.
type Pastro :: FLAVOR j k -> j +-> k -> j +-> k
data Pastro w r a b where
  Pastro
    :: forall {k} {j} (p :: k +-> k) (q :: j +-> j) w r a b
     . (w p q, Profunctor p, Profunctor q) => (p :.: r :.: q) a b -> Pastro w r a b

pastro :: forall {j} {k} (w :: FLAVOR j k) (p :: j +-> k). (Profunctor p, Flavor w) => p :~> Pastro w p
pastro p = Pastro (Id id :.: p :.: Id id) \\ p

unpastro :: forall {j} {k} (w :: FLAVOR j k) (p :: j +-> k). (Prostrong w p) => Pastro w p :~> p
unpastro (Pastro fpg) = proact @w fpg

instance (CategoryOf j, CategoryOf k, Profunctor p) => Profunctor (Pastro t p :: j +-> k) where
  dimap l r (Pastro fpg) = Pastro (dimap l r fpg)
  r \\ Pastro fpg = r \\ fpg
instance (Flavor w, Profunctor p) => Prostrong w (Pastro w p :: j +-> k) where
  proact (p :.: Pastro (p' :.: r :.: q') :.: q) = Pastro ((p :.: p') :.: r :.: (q' :.: q))

instance (Flavor w) => HasFree (Prostrong w :: OB (j +-> k)) where
  type Free (Prostrong w) p = Pastro w p
  lift = Prof pastro
  foldMap n = Prof unpastro . map n

instance Functor (Pastro t) where
  map (Prof n) = Prof \(Pastro (f :.: p :.: g)) -> Pastro (f :.: n p :.: g)
instance (Flavor w) => Promonad (Star (Pastro w) :: (j +-> k) +-> (j +-> k)) where
  id = Star (Prof pastro)
  Star n . Star m = Star (freeComp @(Prostrong w) n m)

fromExOptic
  :: forall {j} {k} w (a :: k) (b :: j)
   . (CategoryOf j, CategoryOf k) => ExOptic w a b :~> (Pastro w (Yo a (OP b)) :: j +-> k)
fromExOptic (ExOptic f g) = Pastro (f :.: Yo (tgt f) (src g) :.: g)

-- | The cofree 'Prostrong' profunctor for the flavor @w@: strength against every @w@-witness pair
-- at once.
type Tambara :: FLAVOR j k -> j +-> k -> j +-> k
data Tambara w r a b where
  Tambara
    :: forall {j} {k} (w :: FLAVOR j k) (r :: j +-> k) a b
     . (Ob a, Ob b)
    => (forall (p :: k +-> k) (q :: j +-> j). (w p q, Profunctor p, Profunctor q) => (q |> r <| p) a b)
    -> Tambara w r a b

mkTambara
  :: (Ob a, Ob b)
  => (forall (p :: k +-> k) (q :: j +-> j) x y. (w p q, Profunctor p, Profunctor q) => p x a -> q b y -> r x y)
  -> Tambara w r a b
mkTambara f = Tambara (Rift \p -> p // Ran \q -> f p q)

runTambara :: (w p q, Profunctor p, Profunctor q) => ((Ob a) => p x a) -> ((Ob b) => q b y) -> Tambara w r a b -> r x y
runTambara p q (Tambara qrp) = runRan q $ runRift p qrp

tambara :: forall {j} {k} w (p :: j +-> k). (Prostrong w p) => p :~> Tambara w p
tambara r = mkTambara (\p q -> proact @w (p :.: r :.: q)) \\ r

untambara
  :: forall {j} {k} w (p :: j +-> k). (Profunctor p, Flavor w) => Tambara w p :~> p
untambara = runTambara @w @Id @Id (Id id) (Id id)

instance (Profunctor p) => Profunctor (Tambara w p :: j +-> k) where
  dimap l r (Tambara n) = Tambara (dimap l r n) \\ l \\ r
  r \\ Tambara{} = r

instance (Flavor w, Profunctor p) => Prostrong w (Tambara w p :: j +-> k) where
  proact (p :.: n :.: q) = mkTambara (\p' q' -> runTambara (p' :.: p) (q :.: q') n) \\ p \\ q

instance (Flavor w) => HasCofree (Prostrong w :: OB (j +-> k)) where
  type Cofree (Prostrong w) p = Tambara w p
  lower = Prof untambara
  unfoldMap n = map n . Prof tambara

instance Functor (Tambara w :: (j +-> k) -> (j +-> k)) where
  map (Prof n) = Prof \t -> t // mkTambara \p q -> n (runTambara p q t)
instance (Flavor w) => Promonad (Costar (Tambara w) :: (j +-> k) +-> (j +-> k)) where
  id = Costar (Prof untambara)
  Costar n . Costar m = Costar (cofreeComp @(Prostrong w) n m)

-- | @Pastro t@ ⊣ @Tambara t@
instance Corepresentable (Star (Tambara w) :: (j +-> k) +-> (j +-> k)) where
  type Star (Tambara w) %% p = Pastro w p
  coindex (Star (Prof n)) = Prof \(Pastro @p @q (p :.: r :.: q)) -> case n r of m -> runTambara @w @p @q p q m
  corepUniv = Star (Prof \r -> r // mkTambara \p q -> Pastro (p :.: r :.: q))