pipes-3.2.0: Control/Proxy/Morph.hs
{-| A proxy morphism is a natural transformation:
> morph :: forall r . p a' a b' b m r -> q a' a b' b n r
... that defines a functor between five categories:
* Functor between Kleisli categories:
> morph p1 >=> morph p2 = morph (p1 >=> p2)
>
> morph return = return
* Functor between 'P.Proxy' composition categories:
> morph p1 >-> morph p2 = morph (p1 >-> p2)
>
> morph idT = idT
> morph p1 >~> morph p2 = morph (p1 >~> p2)
>
> morph coidT = coidT
* Functor between 'ListT' Kleisli categories:
> morph p1 \>\ morph p2 = morph (p2 \>\ p2)
>
> morph request = request
> morph p1 />/ morph p2 = morph (p2 />/ p2)
>
> morph respond = respond
Examples of proxy morphisms include:
* 'liftP' (from 'ProxyTrans')
* 'squashP' (See below)
* @'hoistP' f@ (See below) if @f@ is a proxy morphism
* @(f . g)@, if @f@ and @g@ are both proxy morphisms
* 'id'
Proxy morphisms commonly arise when manipulating existing proxy transformer
code for compatibility purposes. The 'PFUnctor', 'ProxyTrans', and 'PMonad'
classes define standard ways to change proxy transformer stacks:
* 'liftP' introduces a new proxy transformer layer of any type:.
* 'squashP' flattens two identical monad transformer layers into a single
layer of the same type.
* 'hoistP' maps proxy morphisms to modify deeper layers of the proxy
transformer stack.
-}
{-# LANGUAGE KindSignatures, Rank2Types #-}
module Control.Proxy.Morph (
-- * Functors over Proxies
PFunctor(..),
-- * Monads over Proxies
PMonad(..),
squashP
) where
import Control.Proxy.Class (Proxy)
import Control.Proxy.Trans (ProxyTrans(liftP))
{-| A functor in the category of proxies, using 'hoistP' as the analog of
'fmap':
> hoistP f . hoistP g = hoistP (f . g)
>
> hoistP id = id
-}
class PFunctor (t
:: (* -> * -> * -> * -> (* -> *) -> * -> *)
-> * -> * -> * -> * -> (* -> *) -> * -> * ) where
{-| Lift a proxy morphism from @p1@ to @p2@ into a proxy morphism from
@(t p1)@ to @(t p2)@
-}
hoistP
:: (Monad m, Proxy p1)
=> (forall r1 . p1 a' a b' b m r1 -> p2 a' a b' b n r1) -- ^ Proxy morphism
-> ( t p1 a' a b' b m r2 -> t p2 a' a b' b n r2)
{-| A monad in the category of monads, using 'liftP' from 'ProxyTrans' as the
analog of 'return' and 'embedP' as the analog of ('=<<'):
> embedP liftP = id
>
> embedP f (liftP p) = f p
>
> embed g (embed f t) = embed (\p -> embed g (f p)) t
-}
class (PFunctor t, ProxyTrans t) => PMonad t where
{-| Embed a newly created 'PMonad' layer within an existing layer
'embedP' is analogous to ('=<<')
-}
embedP
:: (Monad n, Proxy p2)
=> (forall r1 . p1 a' a b' b m r1 -> t p2 a' a b' b n r1)
-> ( t p1 a' a b' b m r2 -> t p2 a' a b' b n r2)
{-| Squash to 'PMonad' layers into a single layer
'squashP' is analogous to 'join'
-}
squashP
:: (Monad m, Proxy p, PMonad t)
=> t (t p) a' a b' b m r -> t p a' a b' b m r
squashP = embedP id
{-# INLINABLE squashP #-}