pipes-3.2.0: Control/Proxy/Trans/Codensity.hs
{-| This module provides the proxy transformer equivalent of 'CodensityT'.
The base 'Proxy' implementations suffer a quadratic time complexity if
you repeatedly left-associate the monad bind operation. You can recover
linear time complexity just by adding 'runCodensityK' right after
'runProxy', which transforms the base 'Proxy' implementation to use
continuation-passing style:
> -- Before:
> runProxy $ ...
>
> -- After:
> runProxy $ runCodensityK $ ...
Everything will still type-check if you you wrote your code to be
polymorphic over the base 'Proxy'.
Note that even though 'CodensityP' has better time complexity for
left-associated binds, it has worse constant factors for everything else
(about 6x slower on pure benchmarks), because:
* You cannot optimize it using rewrite rules
* It has a slower composition operation
So only use it if you actually need it, which is typically only the case if
you left associate your monad binds on the order of hundreds of times. Even
better: only wrap the problematic portions of the pipeline in
'runCodensityK' so that the performance of the rest of the pipeline does not
suffer.
-}
{-# LANGUAGE KindSignatures, PolymorphicComponents #-}
module Control.Proxy.Trans.Codensity (
-- * Codensity Proxy Transformer
CodensityP,
runCodensityP,
runCodensityK
) where
import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))
import Control.Monad (MonadPlus(mzero, mplus))
import Control.Monad.IO.Class (MonadIO(liftIO))
import Control.Monad.Morph (MFunctor(hoist))
import Control.Monad.Trans.Class (MonadTrans(lift))
import Control.Proxy.Class (
Proxy(request, respond, (->>), (>>~)),
ProxyInternal(return_P, (?>=), lift_P, liftIO_P, hoist_P),
MonadPlusP(mzero_P, mplus_P) )
import Control.Proxy.Morph (PFunctor(hoistP))
import Control.Proxy.ListT (ListT((>\\), (//>)))
import Control.Proxy.Trans (ProxyTrans(liftP))
-- | The 'Codensity' proxy transformer
newtype CodensityP p a' a b' b (m :: * -> *) r
= CodensityP { unCodensityP
:: forall x . (Monad m, Proxy p)
=> (r -> p a' a b' b m x) -> p a' a b' b m x }
{- The type class instances only satisfy their laws if you hide the constructor
for 'CodensityP'.
Normally you would not have to hide it and you could rely on parametricity to
guarantee that 'CodensityP p' is isomorphic to 'p'. However, the 'MFunctor'
and 'PFunctor' type classes require including class constraints within the
constructor, which breaks parametricity and makes it possible to define
'CodensityP' values which break the laws for the following type class
instances.
-}
instance (Proxy p, Monad m) => Functor (CodensityP p a' a b' b m) where
fmap f p = CodensityP (\k ->
unCodensityP p (\a ->
k (f a)) )
instance (Proxy p, Monad m) => Applicative (CodensityP p a' a b' b m) where
pure = return
fp <*> xp = CodensityP (\k ->
unCodensityP fp (\f ->
unCodensityP xp (\x ->
k (f x) ) ) )
instance (Proxy p, Monad m) => Monad (CodensityP p a' a b' b m) where
return = return_P
(>>=) = (?>=)
instance (Proxy p) => MonadTrans (CodensityP p a' a b' b) where
lift = lift_P
instance (Proxy p) => MFunctor (CodensityP p a' a b' b) where
hoist = hoist_P
instance (Proxy p, MonadIO m) => MonadIO (CodensityP p a' a b' b m) where
liftIO = liftIO_P
instance (MonadPlusP p, Monad m) => Alternative (CodensityP p a' a b' b m) where
empty = mzero
(<|>) = mplus
instance (MonadPlusP p, Monad m) => MonadPlus (CodensityP p a' a b' b m) where
mzero = mzero_P
mplus = mplus_P
instance (Proxy p) => ProxyInternal (CodensityP p) where
return_P = \r -> CodensityP (\k -> k r)
m ?>= f = CodensityP (\k ->
unCodensityP m (\a ->
unCodensityP (f a) k ) )
lift_P m = CodensityP (\k -> lift_P m ?>= k)
hoist_P nat p = CodensityP (\k ->
hoist_P nat (unCodensityP p return_P) ?>= k)
liftIO_P m = CodensityP (\k -> liftIO_P m ?>= k)
instance (MonadPlusP p) => MonadPlusP (CodensityP p) where
mzero_P = CodensityP (\_ -> mzero_P)
mplus_P m1 m2 = CodensityP (\k ->
mplus_P (unCodensityP m1 k) (unCodensityP m2 k) )
instance (Proxy p) => Proxy (CodensityP p) where
fb' ->> p = CodensityP (\k ->
((\b' -> unCodensityP (fb' b') return_P) ->> unCodensityP p return_P)
?>= k )
p >>~ fb = CodensityP (\k ->
(unCodensityP p return_P >>~ (\b -> unCodensityP (fb b) return_P))
?>= k )
request = \a' -> CodensityP (\k -> request a' ?>= k)
respond = \b -> CodensityP (\k -> respond b ?>= k)
instance (ListT p) => ListT (CodensityP p) where
fb' >\\ p = CodensityP (\k ->
((\b' -> unCodensityP (fb' b') return_P) >\\ unCodensityP p return_P)
?>= k )
p //> fb = CodensityP (\k ->
(unCodensityP p return_P //> (\b -> unCodensityP (fb b) return_P))
?>= k )
instance ProxyTrans CodensityP where
liftP p = CodensityP (\k -> p ?>= k)
instance PFunctor CodensityP where
hoistP nat p = CodensityP (\k -> nat (unCodensityP p return_P) ?>= k)
-- | Run a 'CodensityP' proxy, converting, converting it back to the base proxy
runCodensityP
:: (Monad m, Proxy p) => CodensityP p a' a b' b m r -> p a' a b' b m r
runCodensityP p = unCodensityP p return_P
{-# INLINABLE runCodensityP #-}
{-| Run a 'CodensityP' \'@K@\'leisli arrow, converting it back to the base proxy
-}
runCodensityK
:: (Monad m, Proxy p)
=> (q -> CodensityP p a' a b' b m r) -> (q -> p a' a b' b m r)
runCodensityK k q = runCodensityP (k q)
{-# INLINABLE runCodensityK #-}