packages feed

proarrow-0.1.0.0: src/Proarrow/Promonad/Reader.hs

-- | The reader promonad over a monoidal category: @'Reader' ('OP' r) a b@ is a map @r '**' a '~>' b@.
-- It is 'Corepresentable' by @r '**' -@ (the coreader comonad) and, in a symmetric closed category,
-- 'Representable' by @r '~~>' -@ (the reader monad); a 'Comonoid' @r@ makes it a 'Promonad'. 'ReaderT'
-- is the corresponding transformer.
module Proarrow.Promonad.Reader where

import Prelude (($))

import Proarrow.Adjunction qualified as Adj
import Proarrow.Category.Instance.Nat (Nat (..))
import Proarrow.Category.Instance.Opposite (OPPOSITE (..), Op (..))
import Proarrow.Category.Instance.Prof (Prof (..))
import Proarrow.Category.Monoidal
  ( Monoidal (..)
  , MonoidalProfunctor (..)
  , SymMonoidal (..)
  , Tensor
  , first
  , leftUnitorInvWith
  , leftUnitorWith
  , second
  , swap'
  , swapInner
  , unitObj
  )
import Proarrow.Category.Monoidal.Cartesian (Cartesian)
import Proarrow.Category.Monoidal.Closed (Closed (..), uncurry)
import Proarrow.Category.Monoidal.Distributive (Cotraversable (..))
import Proarrow.Category.Monoidal.Strength (Strong (..))
import Proarrow.Core (CategoryOf (..), Profunctor (..), Promonad (..), lmap, obj, rmap, src, (//), (:~>), type (+->))
import Proarrow.Functor (Functor (..))
import Proarrow.Monoid (Comonoid (..), Monoid (..))
import Proarrow.Profunctor.Corepresentable (Corepresentable (..))
import Proarrow.Profunctor.Instance.Composition (compComp, (:.:) (..))
import Proarrow.Profunctor.Instance.Day (Day (..))
import Proarrow.Profunctor.Instance.Star (Star, pattern Star)
import Proarrow.Profunctor.Representable (Representable (..))
import Proarrow.Promonad (Procomonad (..))
import Proarrow.Promonad.Writer (Writer (..), WriterT (..))

-- | The reader promonad for an environment @r@: an arrow from @a@ to @b@ is a map
-- @r '**' a '~>' b@ consuming the environment. The index is 'OP'-wrapped, as it acts
-- contravariantly.
data Reader r a b where
  Reader :: forall a b r. (Ob a) => r ** a ~> b -> Reader (OP r) a b

instance (Ob (r :: k), Monoidal k) => Profunctor (Reader (OP r) :: k +-> k) where
  dimap l r (Reader f) = Reader (r . f . second @r l) \\ r \\ l
  r \\ Reader f = r \\ f

-- | The coreader comonad given the Promonad instance.
-- Together with the 'Representable' instance this gives the curry/uncurry adjunction.
instance (Ob (r :: k), Monoidal k) => Corepresentable (Reader (OP r) :: k +-> k) where
  type Reader (OP r) %% a = r ** a
  coindex (Reader f) = f
  cotabulate = Reader
  corepMap = second @r

-- | The reader monad given the Promonad instance.
instance (Ob (r :: k), SymMonoidal k, Closed k) => Representable (Reader (OP r) :: k +-> k) where
  type Reader (OP r) % a = r ~~> a
  index (Reader @a @b f) = curry @_ @a @r @b (f . swap @_ @a @r)
  tabulate @b @a f = Reader (uncurry @r @b f . swap @_ @r @a) \\ f
  repMap f = f ^^^ obj @r

instance (Monoidal k) => Functor (Reader :: OPPOSITE k -> k +-> k) where
  map (Op f) = f // Prof \(Reader @a g) -> Reader (g . first @a f)

instance (Comonoid (r :: k), Monoidal k) => Promonad (Reader (OP r) :: k +-> k) where
  id = Reader (leftUnitorWith (counit @r))
  Reader g . Reader @a f = Reader (g . second @r f . associator @k @r @r @a . first @a (comult @r))

instance (Monoid (r :: k), Monoidal k) => Procomonad (Reader (OP r) :: k +-> k) where
  proextract (Reader f) = f . leftUnitorInvWith (mempty @r)
  produplicate (Reader @a f) = Reader id :.: Reader (f . first @a (mappend @r) . associatorInv @k @r @r @a) \\ f

instance (Ob (r :: k), SymMonoidal k) => Strong Tensor (Reader (OP r) :: k +-> k) where
  act @a @x (Reader g) =
    withOb2 @k @a @x $
      Reader ((obj @a ** g) . associator @k @a @r @x . first @x (swap @_ @r @a) . associatorInv @k @r @a @x)

-- | Note: This is only premonoidal, not monoidal, unless the comonoid is cocommutative.
instance (Comonoid (r :: k), SymMonoidal k) => MonoidalProfunctor (Reader (OP r) :: k +-> k) where
  one = id \\ unitObj @k
  Reader @x1 @x2 f ** Reader @y1 @y2 g =
    f //
      g //
        withOb2 @_ @x1 @y1 $
          withOb2 @_ @x2 @y2 $
            Reader
              ( second @x2 g
                  . associator @k @x2 @r @y1
                  . ((swap' (obj @r) f . associator @k @r @r @x1 . first @x1 (comult @r)) ** obj @y1)
                  . associatorInv @k @r @x1 @y1
              )

-- | A version of cotraverse specialized to `Reader`, with fewer requirements on @p@.
cotraverseReader
  :: forall {k} p (r :: k). (Strong Tensor p, Ob r) => p :.: Reader (OP r) :~> Reader (OP r) :.: p
cotraverseReader (p :.: Reader f) = let rp = act @Tensor @p @r p in Reader (src rp) :.: rmap f rp \\ rp \\ p

instance (Comonoid (r :: k), Monoidal k) => Cotraversable (Reader (OP r) :: k +-> k) where
  cotraverse = cotraverseReader

-- Reader is not Traversable

instance (Ob (r :: k), Monoidal k) => Adj.Proadjunction (Writer r :: k +-> k) (Reader (OP r)) where
  unit @a = withOb2 @k @r @a $ Reader id :.: Writer id
  counit (Writer f :.: Reader g) = g . f

dayCounit :: forall {k} (r :: k). (Ob r, Cartesian k) => Writer r `Day` Reader (OP r) ~> (~>)
dayCounit = Prof \(Day @_ @d @e f (Writer p) (Reader q) g) -> g . second @d q . associator @k @d @r @e . first @e (swap @k @r @d . p) . f

readerComp
  :: forall {k} (r :: k) (s :: k). (SymMonoidal k, Ob r, Ob s) => Reader (OP r) :.: Reader (OP s) ~> Reader (OP (r ** s))
readerComp = withOb2 @k @r @s $
  Prof \(Reader @a f :.: Reader g) -> Reader (g . second @s f . associator @k @s @r @a . first @a (swap @k @r @s))

readerDay
  :: forall {k} (r :: k) (s :: k). (SymMonoidal k, Ob r, Ob s) => Reader (OP r) `Day` Reader (OP s) ~> Reader (OP (r ** s))
readerDay = withOb2 @k @r @s $
  Prof \(Day @c @_ @e @_ f (Reader p) (Reader q) g) -> Reader (g . (p ** q) . swapInner @r @s @c @e . second @(r ** s) f) \\ f

type ReaderT :: OPPOSITE k -> k +-> k -> k +-> k
newtype ReaderT r p a b where
  ReaderT :: (Reader r :.: p) a b -> ReaderT r p a b

runReaderT :: (Profunctor p) => ReaderT (OP r) p a b -> p (r ** a) b
runReaderT (ReaderT (Reader f :.: p)) = lmap f p

ask :: (Promonad p, Monoidal k, Ob (r :: k)) => ReaderT (OP r) p Unit r
ask = ReaderT (Reader rightUnitor :.: id)

answer
  :: forall {k} p r a b. (Promonad p, Monoidal k, Comonoid (r :: k)) => ReaderT (OP r) p a b -> ReaderT (OP r) p (r ** a) b
answer (ReaderT (Reader f :.: p)) = withOb2 @k @r @a $ ReaderT (Reader (f . leftUnitorWith @(r ** a) (counit @r)) :.: p)

local :: forall {k} a b p r. (Monoidal k, Ob (r :: k)) => r ~> r -> ReaderT (OP r) p a b -> ReaderT (OP r) p a b
local f (ReaderT (Reader r :.: p)) = ReaderT (Reader (r . first @a f) :.: p)

deriving newtype instance (Profunctor p, Monoidal k, Ob (r :: k)) => Profunctor (ReaderT (OP r) p)
deriving newtype instance (Representable p, Ob (r :: k), SymMonoidal k, Closed k) => Representable (ReaderT (OP r) p)
deriving newtype instance (Corepresentable p, Ob (r :: k), Monoidal k) => Corepresentable (ReaderT (OP r) p)
deriving newtype instance
  (MonoidalProfunctor p, Comonoid (r :: k), SymMonoidal k) => MonoidalProfunctor (ReaderT (OP r) p)
deriving newtype instance (Cotraversable p, Comonoid (r :: k), Monoidal k) => Cotraversable (ReaderT (OP r) p)

instance (Strong Tensor p, Ob (r :: k), SymMonoidal k) => Strong Tensor (ReaderT (OP r) p) where
  act @a (ReaderT p) = ReaderT (act @Tensor @_ @a p)

instance (Monoidal k, Ob r) => Functor (ReaderT r :: k +-> k -> k +-> k) where
  map n@Prof{} = Prof \(ReaderT p) -> ReaderT (unProf (map n) p)

instance (Monoidal k) => Functor (ReaderT :: OPPOSITE k -> k +-> k -> k +-> k) where
  map f = f // Nat $ Prof \(ReaderT p) -> ReaderT (unProf (unNat (map (map f))) p)

instance (Comonoid (r :: k), Monoidal k, Strong Tensor p, Promonad p) => Promonad (ReaderT (OP r) p) where
  id = ReaderT (id :.: id)
  ReaderT l . ReaderT r = ReaderT (compComp cotraverseReader l r)

-- | ReaderT is a monad on profunctors, i.e. we have @p ~> ReaderT p@ and @ReaderT (ReaderT p) ~> ReaderT p@.
instance (Comonoid r, Monoidal k) => Promonad (Star (ReaderT (OP r) :: k +-> k -> k +-> k)) where
  id = Star $ Prof \p -> ReaderT (id :.: p) \\ p
  Star l . Star r = Star $ Prof (\(ReaderT (f :.: ReaderT (g :.: p))) -> ReaderT ((g . f) :.: p)) . map l . r

instance (Adj.Proadjunction p q, Ob (r :: k), Monoidal k) => Adj.Proadjunction (WriterT r p) (ReaderT (OP r) q) where
  unit @a = case Adj.unit @_ @_ @a of l :.: r -> ReaderT l :.: WriterT r
  counit (WriterT l :.: ReaderT r) = Adj.counit (l :.: r)