packages feed

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

{-# LANGUAGE AllowAmbiguousTypes #-}

-- | 'Costar' embeds a functor @f@ as a profunctor with the functor on the source side:
-- @Costar f a b = f a ~> b@. It is the corepresentable profunctor of @f@, dual to
-- 'Proarrow.Profunctor.Instance.Star.Star'.
module Proarrow.Profunctor.Instance.Costar where

import Control.Monad qualified as P
import Data.Functor.Compose (Compose (..))
import Prelude qualified as P

import Proarrow.Category.Enriched.Thin (DecidableProfunctor (..), Thin, ThinProfunctor (..), mapDecision)
import Proarrow.Category.Instance.Nat (Nat' (..), type (.->) (..))
import Proarrow.Category.Instance.Opposite (OPPOSITE (..), Op (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Monoidal (Monoidal (..), MonoidalProfunctor (..), withOb2)
import Proarrow.Category.Monoidal.Cartesian (Cartesian)
import Proarrow.Category.Monoidal.Distributive (Cotraversable (..), Traversable (..))
import Proarrow.Core (CategoryOf (..), Hom, Profunctor (..), Promonad (..), rmap, (//), (:~>), type (+->))
import Proarrow.Functor (Functor (..), Prelude (..), withObF)
import Proarrow.Limit.BinaryProduct (HasBinaryProducts (..))
import Proarrow.Limit.Terminal (HasTerminalObject (..))
import Proarrow.Profunctor.Corepresentable (Corepresentable (..), dimapCorep)
import Proarrow.Profunctor.Instance.Composition ((:.:) (..))
import Proarrow.Profunctor.Instance.Product ((:*:) (..))
import Proarrow.Profunctor.Instance.Star (Star, pattern Star)
import Proarrow.Promonad (Procomonad (..))

type Costar' :: OPPOSITE (j .-> k) -> k +-> j
data Costar' f a b where
  Costar' :: (Ob a) => f a ~> b -> Costar' (OP (NT f)) a b

type Costar f = Costar' (OP (NT f))
pattern Costar :: () => (Ob a) => (f a ~> b) -> Costar f a b
pattern Costar f = Costar' f
{-# COMPLETE Costar #-}
unCostar :: Costar f a b -> f a ~> b
unCostar (Costar f) = f

instance (Functor f) => Profunctor (Costar f) where
  dimap = dimapCorep
  r \\ Costar f = r \\ f

instance Functor Costar' where
  map (Op (Nat' n)) = Prof \(Costar f) -> Costar (f . n)

instance (Functor f) => Corepresentable (Costar f) where
  type Costar f %% a = f a
  coindex = unCostar
  cotabulate = Costar
  corepMap = map

instance (Profunctor p) => Promonad (Costar ((:*:) p)) where
  id = Costar (Prof \(_ :*: q) -> q)
  Costar (Prof l) . Costar (Prof r) = Costar (Prof \(p :*: a) -> l (p :*: r (p :*: a)))

instance (P.Monad m) => Procomonad (Costar (Prelude m)) where
  proextract (Costar f) = f . Prelude . P.pure
  produplicate (Costar f) = Costar unPrelude :.: Costar (f . Prelude . P.join . unPrelude)

composeCostar :: (Functor g) => Costar f :.: Costar g :~> Costar (Compose g f)
composeCostar (Costar f :.: Costar g) = Costar (g . map f . getCompose)

-- | Every functor between cartesian categories is a colax monoidal functor.
instance (Cartesian j, Cartesian k, Functor (f :: j -> k)) => MonoidalProfunctor (Costar f) where
  one = withObF @f @(Unit :: j) (Costar terminate)
  Costar @a f ** Costar @b g = withOb2 @j @a @b (Costar (f . map (fst @j @a @b) &&& g . map (snd @j @a @b)))

instance (Functor t, Traversable (Star t)) => Cotraversable (Costar t) where
  cotraverse (p :.: Costar f) = p // Costar id :.: case traverse (Star id :.: p) of p' :.: Star g -> rmap (f . g) p'

instance (Functor f, Thin j) => ThinProfunctor (Costar f :: j +-> k) where
  type HasArrow (Costar f :: j +-> k) a b = HasArrow (Hom j) (f a) b
  arr = Costar arr
  withArr (Costar f) r = withArr f r

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