cocomonad
Safe HaskellNone
LanguageHaskell2010

Control.Monad.CoComonad

Description

Monad transformer from Comonad.

This module provides the CoT monad transformer, which adds a "comonadic context" to another monad.

What does CoT do?

For any Comonad f and Monad g, CoT f g is a monad that combines the original effect of the monad g with the ability to use the comonad f as a context. For example:

  • CoT (Env e) g is a monad with the g effect and access to a value of type e.

  • CoT (Store s) g is a monad with the g effect and access to a context that holds a store of s values, which can be queried or updated.

    • This corresponds exactly to what StateT s g provides, and indeed, CoT (Store s) g is isomorphic to StateT s g: see fromStateT and toStateT.
  • Similarly, CoT (Traced m) g is isomorphic to WriterT m g.

The "contextual" effects introduced by CoT can be composed freely. Any nested use of the CoT monad transformer can be flattened into a single layer using the Day functor to combine the contexts:

uncurryCoT :: Functor g => CoT f1 (CoT f2 g) ~> CoT (Day f1 f2) g
curryCoT   :: Functor g => CoT (Day f1 f2) g ~> CoT f1 (CoT f2 g)

Relation to other types

As a Functor, CoT f g is isomorphic to the Curried f g type. However, since Curried has an Applicative instance that is not compatible with the Monad instance of CoT, a new type was necessary to support the monad behavior described here.

The non-transformer version, Co f, is isomorphic to the Co f type from the Control.Monad.Co module (which is actually the source of inspiration for this module!).

The name collision is unfortunate — the author has not yet decided on a final name for this monad transformer. Suggestions are welcome!

For category theory nerds

The construction of monad CoT f g, given Comonad f and Monad g, can be explained via Kleisli category.

Recall that providing a Monad instance for a functor m is equivalent to providing a Category (Kleisli m) instance.

-- The type of Kleisli category morphisms
newtype Kleisli m a b = Kl { runKl :: a -> m b }

-- Derive Category (Kleisli m) from Monad m
instance Monad m => Category (Kleisli m) where
  id :: Kleisli m a a
  id = Kl return
  (.) :: Kleisli m b c -> Kleisli m a b -> Kleisli m a c
  Kl f . Kl g = Kl (f <=< g)

-- Derive Monad m from Category (Kleisli m)
-- (pseudocode, can't be real Haskell)
instance Category (Kleisli m) => Monad m where
  return = runKl id
  ma >>= k = runKl (Kl k . Kl (const ma)) ()

Kleisli (CoT f g) is isomorphic to the following Arr f g.

type Arr f g a b = forall r. (a, f r) -> g (b, r)

Here's the transformations showing the isomorphism between Kleisli (CoT f g) and Arr f g.

Kleisli (CoT f g) a b
 ≅ a -> CoT f g b
 ≅ a -> ∀r. f r -> g (b, r)
 ≅ ∀r. a -> f r -> g (b, r)
 ≅ ∀r. (a, f r) -> g (b, r)
 ~ Arr f g a b

Arr f g can be made into Category with the following identity and composition. The Monad (CoT f g) instance is the one derived from this construction.

idArr :: (Comonad f, Monad g) => Arr f g a a
idArr (a, fr) = pure (a, extract fr)

compArr :: (Comonad f, Monad g) => Arr f g a b -> Arr f g b c -> Arr f g a c
compArr t u (a, fr) =
  t (a, duplicate fr) >>= u

Diagramatically, idArr is the following composite

\[ \require{AMScd} \begin{CD} (a, \_) \circ f @>>{\mathop{\mathtt{fmap}} \mathop{\mathtt{extract}}}> (a, \_) @>>{\mathtt{pure}}> g \circ (a, \_) \end{CD} \]

and compArr t u is the following composite.

\[ \require{AMScd} \begin{CD} (a, \_) \circ f @>>{\mathop{\mathtt{fmap}} \mathop{\mathtt{duplicate}}}> (a, \_) \circ f \circ f @>>{t}> g \circ (b, \_) \circ f @>>{\mathop{\mathtt{fmap}} u}> g \circ g \circ (c, \_) @>>{\mathop{\mathtt{join}}}> g \circ (c, \_) \end{CD} \]

Proving category laws for (idArr, compArr) will be simple enough.

Synopsis

The CoT monad transformer

newtype CoT (f :: Type -> Type) (g :: Type -> Type) a Source #

CoT f g is a Monad which is based on Monad g but can use Comonad f as a context.

Constructors

CoT 

Fields

  • runCoT :: forall r. f r -> g (a, r)
     

Instances

Instances details
MFunctor (CoT f :: (Type -> Type) -> Type -> Type) Source # 
Instance details

Defined in Control.Monad.CoComonad

Methods

hoist :: Monad m => (forall a. m a -> n a) -> CoT f m b -> CoT f n b #

Comonad f => MonadTrans (CoT f) Source # 
Instance details

Defined in Control.Monad.CoComonad

Methods

lift :: Monad m => m a -> CoT f m a #

(Comonad f, MonadIO g) => MonadIO (CoT f g) Source # 
Instance details

Defined in Control.Monad.CoComonad

Methods

liftIO :: IO a -> CoT f g a #

(Comonad f, Monad g) => Applicative (CoT f g) Source # 
Instance details

Defined in Control.Monad.CoComonad

Methods

pure :: a -> CoT f g a #

(<*>) :: CoT f g (a -> b) -> CoT f g a -> CoT f g b #

liftA2 :: (a -> b -> c) -> CoT f g a -> CoT f g b -> CoT f g c #

(*>) :: CoT f g a -> CoT f g b -> CoT f g b #

(<*) :: CoT f g a -> CoT f g b -> CoT f g a #

Functor g => Functor (CoT f g) Source # 
Instance details

Defined in Control.Monad.CoComonad

Methods

fmap :: (a -> b) -> CoT f g a -> CoT f g b #

(<$) :: a -> CoT f g b -> CoT f g a #

(Comonad f, Monad g) => Monad (CoT f g) Source # 
Instance details

Defined in Control.Monad.CoComonad

Methods

(>>=) :: CoT f g a -> (a -> CoT f g b) -> CoT f g b #

(>>) :: CoT f g a -> CoT f g b -> CoT f g b #

return :: a -> CoT f g a #

Manipulating comonadic part

contrahoist :: forall f k (g :: Type -> Type) a. (forall x. f x -> k x) -> CoT k g a -> CoT f g a Source #

Change of the Comonad part.

contrahoist φ is a monad morphism CoT k g ~> CoT f g if φ :: f ~> k is a comonad morphism.

elimCoT :: forall (f :: Type -> Type) (g :: Type -> Type). (Applicative f, Functor g) => CoT f g ~> g Source #

When the context f can be constructed as pure x : f a, CoT f can be removed by supplying the "pure" context.

There are two notable special cases of elimCoT:

  -- If the context f is Identity comonad, elimCoT is monad isomorphism and
  -- is the inverse of lift.
  elimCoT :: (Functor g) => CoT Identity g ~> g
  
  -- If the context f is NonEmpty comonad, elimCoT is monad morphism,
  -- and elimCoT . lift = id holds.
  elimCoT :: (Functor g) => CoT NonEmpty g ~> g
  

curryCoT :: forall (g :: Type -> Type) (f1 :: Type -> Type) (f2 :: Type -> Type). Functor g => CoT (Day f1 f2) g ~> CoT f1 (CoT f2 g) Source #

The inverse of uncurryCo.

uncurryCoT :: forall (g :: Type -> Type) (f1 :: Type -> Type) (f2 :: Type -> Type). Functor g => CoT f1 (CoT f2 g) ~> CoT (Day f1 f2) g Source #

Nesting of CoT f1 and CoT f2 can be represented as a single CoT (Day f1 f2) using Day to combine two comonads.

This is also a monad isomorphism.

eitherCoT :: forall (f1 :: Type -> Type) (g :: Type -> Type) (f2 :: Type -> Type) x. Product (CoT f1 g) (CoT f2 g) x -> CoT (Sum f1 f2) g x Source #

Product of CoT into the same monad is CoT from the sum of comonads.

Compare it with either function:

either  :: (a -> c) -> (b -> c) -> (Either a b -> c)
either' :: (a -> c, b -> c) -> (Either a b -> c)

eitherCoT is a monad isomorphism (witnessed by uneitherCoT.)

uneitherCoT :: forall (f1 :: Type -> Type) (f2 :: Type -> Type) (g :: Type -> Type) x. CoT (Sum f1 f2) g x -> Product (CoT f1 g) (CoT f2 g) x Source #

The inverse of eitherCoT.

Representing Reader, Writer, or State effects

toReaderT :: forall (g :: Type -> Type) e a. Monad g => CoT (Env e) g a -> ReaderT e g a Source #

toWriterT :: forall (g :: Type -> Type) m a. Monad g => CoT (Traced m) g a -> WriterT m g a Source #

toStateT :: forall (g :: Type -> Type) s a. Monad g => CoT (Store s) g a -> StateT s g a Source #

fromReaderT :: forall e (f :: Type -> Type) (g :: Type -> Type) a. (ComonadEnv e f, Monad g) => ReaderT e g a -> CoT f g a Source #

fromWriterT :: forall m (f :: Type -> Type) (g :: Type -> Type) a. (ComonadTraced m f, Monad g) => WriterT m g a -> CoT f g a Source #

fromStateT :: forall s (f :: Type -> Type) (g :: Type -> Type) a. (ComonadStore s f, Monad g) => StateT s g a -> CoT f g a Source #

asking :: forall e (f :: Type -> Type) (g :: Type -> Type). (ComonadEnv e f, Monad g) => CoT f g e Source #

telling :: forall m (f :: Type -> Type) (g :: Type -> Type). (Monoid m, ComonadTraced m f, Monad g) => m -> CoT f g () Source #

stating :: forall s (f :: Type -> Type) (g :: Type -> Type) a. (ComonadStore s f, Monad g) => (s -> (a, s)) -> CoT f g a Source #

Conversion

toCurried :: forall (g :: Type -> Type) (f :: Type -> Type). Functor g => CoT f g ~> Curried f g Source #

As a mere Functor, CoT f g is isomorphic to Curried f g.

fromCurried :: forall (f :: Type -> Type) (g :: Type -> Type). Functor f => Curried f g ~> CoT f g Source #

The inverse of toCurried

Non-transformer version

type Co (f :: Type -> Type) = CoT f Identity Source #

Non-transformer version of CoT.

co :: (forall r. f r -> (a, r)) -> Co f a Source #

runCo :: Co f a -> f r -> (a, r) Source #

generalize :: Monad m => Identity a -> m a #

A function that generalizes the Identity base monad to be any monad.

Conversion

toCodensityCo :: forall (f :: Type -> Type). Functor f => Co f ~> Co f Source #

fromCodensityCo :: forall (f :: Type -> Type). Functor f => Co f ~> Co f Source #