packages feed

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

-- | The writer promonad: @'Writer' w a b@ is a map @a '~>' w '**' b@, 'Representable' by @w '**' -@.
-- A 'Monoid' @w@ makes it a 'Promonad' (the writer monad), and in a compact closed category it is also
-- 'Corepresentable' (the cowriter comonad). 'WriterT' is the corresponding transformer, with 'tell'.
module Proarrow.Promonad.Writer where

import Prelude (($))

import Proarrow.Category.Instance.Nat (Nat (..))
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.CompactClosed (CompactClosed (..), combineDual)
import Proarrow.Category.Monoidal.Distributive (Traversable (..))
import Proarrow.Category.Monoidal.StarAutonomous (ExpSA, StarAutonomous (..), expSA)
import Proarrow.Category.Monoidal.Strength (Strong (..))
import Proarrow.Core (CategoryOf (..), Profunctor (..), Promonad (..), lmap, obj, rmap, tgt, (//), (:~>), 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 (..), dimapRep)
import Proarrow.Promonad (Procomonad (..))

-- | The writer promonad over @w@: an arrow from @a@ to @b@ is a map @a '~>' w '**' b@, emitting
-- output alongside the result.
data Writer w a b where
  Writer :: (Ob b) => a ~> w ** b -> Writer w a b

instance (Ob (w :: k), Monoidal k) => Profunctor (Writer w :: k +-> k) where
  dimap = dimapRep
  r \\ Writer f = r \\ f

-- | The writer monad given the Promonad instance.
instance (Ob (w :: k), Monoidal k) => Representable (Writer w :: k +-> k) where
  type Writer w % a = w ** a
  index (Writer f) = f
  tabulate = Writer
  repMap = second @w

-- | The cowriter comonad given the Promonad instance.
instance (Ob (w :: k), CompactClosed k) => Corepresentable (Writer w :: k +-> k) where
  type Writer w %% a = ExpSA w a
  coindex (Writer @b @a f) =
    withObDual @k @w
      ( withObDual @k @a $
          leftUnitorWith (dualityCounit @k @w)
            . associatorInv @k @(Dual w) @w @b
            . (obj @(Dual w) ** (f . doubleNeg @k @a))
            . distribDual @k @w @(Dual a)
      )
      \\ f
  cotabulate @a f =
    withObDual @k @w
      ( withObDual @k @a $
          withObDual @k @(Dual a) $
            Writer
              ( (obj @w ** (f . combineDual @w @(Dual a)))
                  . associator @k @w @(Dual w) @(Dual (Dual a))
                  . leftUnitorInvWith (dualityUnit @k @w)
                  . doubleNegInv @k @a
              )
      )
      \\ f
  corepMap f = expSA f (obj @w)

instance (Monoidal k) => Functor (Writer :: k -> k +-> k) where
  map f = f // Prof \(Writer @b g) -> Writer ((f ** obj @b) . g)

instance (Monoid (w :: k), Monoidal k) => Promonad (Writer w :: k +-> k) where
  id = Writer (leftUnitorInvWith (mempty @w))
  Writer @c g . Writer f = Writer (first @c (mappend @w) . associatorInv @k @w @w @c . second @w g . f)

instance (Comonoid (w :: k), Monoidal k) => Procomonad (Writer w :: k +-> k) where
  proextract (Writer f) = leftUnitorWith (counit @w) . f
  produplicate (Writer @b f) = Writer (associator @k @w @w @b . first @b (comult @w) . f) :.: Writer id \\ f

instance (Ob (w :: k), SymMonoidal k) => Strong Tensor (Writer w :: k +-> k) where
  act @b @_ @y (Writer g) =
    withOb2 @k @b @y $
      Writer (associator @k @w @b @y . first @y (swap @_ @b @w) . associatorInv @k @b @w @y . (obj @b ** g))

-- | This is only premonoidal, not monoidal, unless the monoid is commutative.
instance (Monoid (w :: k), SymMonoidal k) => MonoidalProfunctor (Writer w :: k +-> k) where
  one = id \\ unitObj @k
  Writer @x2 @x1 f ** Writer @y2 @y1 g =
    f //
      g //
        withOb2 @_ @x1 @y1 $
          withOb2 @_ @x2 @y2 $
            Writer
              ( associator @k @w @x2 @y2
                  . first @y2 (first @x2 (mappend @w) . associatorInv @k @w @w @x2 . swap' f (obj @w))
                  . associatorInv @k @x1 @w @y2
                  . second @x1 g
              )

-- | A version of traverse specialized to `Writer`, with fewer requirements on @p@.
traverseWriter :: forall {k} p (w :: k). (Strong Tensor p, Ob w) => Writer w :.: p :~> p :.: Writer w
traverseWriter (Writer f :.: p) = let wp = act @Tensor @p @w p in lmap f wp :.: Writer (tgt wp) \\ wp \\ p

instance (Monoid (w :: k), Monoidal k) => Traversable (Writer w :: k +-> k) where
  traverse = traverseWriter

-- Writer is not Cotraversable

writerComp :: forall {k} (r :: k) (s :: k). (SymMonoidal k, Ob r, Ob s) => Writer r :.: Writer s ~> Writer (r ** s)
writerComp = withOb2 @k @r @s $
  Prof \(Writer f :.: Writer @b g) -> Writer (associatorInv @k @r @s @b . second @r g . f)

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

type WriterT :: k -> k +-> k -> k +-> k
newtype WriterT w p a b where
  WriterT :: (p :.: Writer w) a b -> WriterT w p a b

runWriterT :: (Profunctor p) => WriterT w p a b -> p a (w ** b)
runWriterT (WriterT (p :.: Writer f)) = rmap f p

tell :: (Promonad p, Monoidal k, Ob (w :: k)) => WriterT w p w Unit
tell = WriterT (id :.: Writer rightUnitorInv)

listen :: forall {k} p w a b. (Promonad p, Monoidal k, Comonoid (w :: k)) => WriterT w p a b -> WriterT w p a (w ** b)
listen (WriterT (p :.: Writer f)) = withOb2 @k @w @b $ WriterT (p :.: Writer (associator @k @w @w @b . first @b (comult @w) . f))

censor :: forall {k} p w a b. (Monoidal k, Ob (w :: k)) => w ~> w -> WriterT w p a b -> WriterT w p a b
censor f (WriterT (p :.: Writer w)) = WriterT (p :.: Writer (first @b f . w))

deriving newtype instance (Profunctor p, Monoidal k, Ob (w :: k)) => Profunctor (WriterT w p)
deriving newtype instance (Representable p, Ob (w :: k), Monoidal k) => Representable (WriterT w p)
deriving newtype instance (Corepresentable p, Ob (w :: k), CompactClosed k) => Corepresentable (WriterT w p)
deriving newtype instance (MonoidalProfunctor p, Monoid (w :: k), SymMonoidal k) => MonoidalProfunctor (WriterT w p)
deriving newtype instance (Traversable p, Monoid (w :: k)) => Traversable (WriterT w p)

instance (Strong Tensor p, Ob (w :: k), SymMonoidal k) => Strong Tensor (WriterT w p) where
  act @a (WriterT p) = WriterT (act @Tensor @_ @a p)

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

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

instance (Monoid (w :: k), Strong Tensor p, Promonad p) => Promonad (WriterT w p) where
  id = WriterT (id :.: id)
  WriterT l . WriterT r = WriterT (compComp traverseWriter l r)

-- | WriterT is a monad on profunctors, with @p ~> WriterT p@ and
-- @WriterT (WriterT p) ~> WriterT p@.
instance (Monoid w, Monoidal k) => Promonad (Star (WriterT w :: k +-> k -> k +-> k)) where
  id = Star $ Prof \p -> WriterT (p :.: id) \\ p
  Star l . Star r = Star $ Prof (\(WriterT (WriterT (p :.: f) :.: g)) -> WriterT (p :.: (g . f))) . map l . r