pipes-3.0.0: Control/Proxy/Prelude/Kleisli.hs
{-# LANGUAGE Rank2Types #-}
-- | Utility functions for Kleisli arrows
module Control.Proxy.Prelude.Kleisli (
-- * Core utility functions
foreverK,
replicateK,
liftK,
hoistK,
raiseK,
) where
import Control.MFunctor (MFunctor(hoist))
import Control.Monad.Trans.Class (MonadTrans(lift))
{-| Compose a \'@K@\'leisli arrow with itself forever
Use 'foreverK' to abstract away the following common recursion pattern:
> p a = do
> ...
> a' <- respond b
> p a'
Using 'foreverK', you can instead write:
> p = foreverK $ \a -> do
> ...
> respond b
-}
foreverK :: (Monad m) => (a -> m a) -> (a -> m b)
foreverK k = let r = \a -> k a >>= r in r
{- foreverK uses 'let' to avoid a space leak.
See: http://hackage.haskell.org/trac/ghc/ticket/5205 -}
-- | Repeat a \'@K@\'leisli arrow multiple times
replicateK :: (Monad m) => Int -> (a -> m a) -> (a -> m a)
replicateK n0 k = go n0 where
go n
| n < 1 = return
| n == 1 = k
| otherwise = \a -> k a >>= go (n - 1)
{-| Convenience function equivalent to @(lift .)@
> liftK f >=> liftK g = liftK (f >=> g)
>
> liftK return = return
-}
liftK :: (Monad m, MonadTrans t) => (a -> m b) -> (a -> t m b)
liftK k a = lift (k a)
-- liftK = (lift .)
{-| Convenience function equivalent to @(hoist f .)@
> hoistK f p1 >-> hoistK f p2 = hoistK f (p1 >-> p2)
>
> hoistK f idT = idT
> hoistK f p1 >=> hoistK f p2 = hoistK f (p1 >=> p2)
>
> hoistK f return = return
> hoistK f . hoistK g = hoistK (f . g)
>
> hoistK id = id
-}
hoistK
:: (Monad m, MFunctor t)
=> (forall a . m a -> n a) -> ((b' -> t m b) -> (b' -> t n b))
hoistK k p a' = hoist k (p a')
-- hoistK k = (hoist k .)
{-| Convenience function equivalent to @(hoist lift .)@
> raiseK p1 >-> raiseK p2 = raiseK (p1 >-> p2)
>
> raiseK idT = idT
> raiseK p1 >=> raiseK p2 = raiseK (p1 >=> p2)
>
> raiseK return = return
-}
raiseK
:: (Monad m, MFunctor t1, MonadTrans t2) => (q -> t1 m r) -> (q -> t1 (t2 m) r)
raiseK = (hoist lift .)