pipes 2.5.0 → 3.0.0
raw patch · 30 files changed
+4656/−3743 lines, 30 filesdep −index-corePVP ok
version bump matches the API change (PVP)
Dependencies removed: index-core
API changes (from Hackage documentation)
- Control.Frame: (<-<) :: Monad m => Frame c m (M b) C r -> Frame b m (M a) C r -> Frame c m (M a) C r
- Control.Frame: (>->) :: Monad m => Frame b m (M a) C r -> Frame c m (M b) C r -> Frame c m (M a) C r
- Control.Frame: FrameC :: Frame b m (M a) C r -> FrameC m r a b
- Control.Frame: [Await] :: (a -> x (O a)) -> FrameF b x (O a)
- Control.Frame: [Close] :: x C -> FrameF b x (O a)
- Control.Frame: [Yield] :: b -> x i -> FrameF b x i
- Control.Frame: [unFrameC] :: FrameC m r a b -> Frame b m (M a) C r
- Control.Frame: await :: (Monad m) => Frame b m (M a) (M a) a
- Control.Frame: awaitF :: (Monad m) => Frame b m (M a) (M a) (Maybe a)
- Control.Frame: catchD :: (Monad m) => m () -> Frame b m i j r -> Frame b m i j r
- Control.Frame: catchF :: (Monad m) => m () -> Frame b m (M a) j r -> Frame b m (M a) j r
- Control.Frame: close :: (Monad m) => Frame b m (M a) C ()
- Control.Frame: data C
- Control.Frame: data FrameF b x i
- Control.Frame: data O a
- Control.Frame: finallyD :: (Monad m) => m () -> Frame b m i j r -> Frame b m i j r
- Control.Frame: finallyF :: (Monad m) => m () -> Frame b m (M a) j r -> Frame b m (M a) j r
- Control.Frame: idF :: (Monad m) => Frame a m (M a) C r
- Control.Frame: infixr 9 >->
- Control.Frame: instance Control.IMonad.Core.IFunctor (Control.Frame.FrameF b)
- Control.Frame: instance GHC.Base.Monad m => Control.Category.Category (Control.Frame.FrameC m r)
- Control.Frame: newtype FrameC m r a b
- Control.Frame: runFrame :: (Monad m) => Stack m r -> m r
- Control.Frame: type Frame b m i j r = IFreeT (FrameF (m (), b)) (U m) (r := j) i
- Control.Frame: type M a = O (Maybe a)
- Control.Frame: type Stack m r = Frame C m (M ()) C r
- Control.Frame: yield :: (Monad m) => b -> Frame b m i i ()
- Control.Frame: yieldF :: (Monad m) => m () -> b -> Frame b m i i ()
- Control.IMonad.Trans.Free: IFreeT :: m (IFreeF f r (IFreeT f m r)) i -> IFreeT f m r i
- Control.IMonad.Trans.Free: Return :: (r i) -> IFreeF f r i
- Control.IMonad.Trans.Free: Wrap :: (f x i) -> IFreeF f r i
- Control.IMonad.Trans.Free: [runIFreeT] :: IFreeT f m r i -> m (IFreeF f r (IFreeT f m r)) i
- Control.IMonad.Trans.Free: data IFreeF f r (x :: * -> *) i
- Control.IMonad.Trans.Free: instance (Control.IMonad.Core.IFunctor f, Control.IMonad.Core.IMonad m) => Control.IMonad.Core.IFunctor (Control.IMonad.Trans.Free.IFreeT f m)
- Control.IMonad.Trans.Free: instance (Control.IMonad.Core.IFunctor f, Control.IMonad.Core.IMonad m) => Control.IMonad.Core.IMonad (Control.IMonad.Trans.Free.IFreeT f m)
- Control.IMonad.Trans.Free: instance Control.IMonad.Core.IFunctor f => Control.IMonad.Trans.IMonadTrans (Control.IMonad.Trans.Free.IFreeT f)
- Control.IMonad.Trans.Free: liftF :: (IFunctor f, IMonad m) => f r :-> IFreeT f m r
- Control.IMonad.Trans.Free: newtype IFreeT f m r i
- Control.IMonad.Trans.Free: wrap :: (IMonad m) => f (IFreeT f m r) :-> IFreeT f m r
- Control.MFunctor: mapT :: (MFunctor t, Monad m, Monad n) => (forall a. m a -> n a) -> t m b -> t n b
- Control.Pipe.Core: (<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r
- Control.Pipe.Core: (>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r
- Control.Pipe.Core: Await :: (a -> Pipe a b m r) -> Pipe a b m r
- Control.Pipe.Core: M :: (m (Pipe a b m r)) -> Pipe a b m r
- Control.Pipe.Core: PipeC :: Pipe a b m r -> PipeC m r a b
- Control.Pipe.Core: Pure :: r -> Pipe a b m r
- Control.Pipe.Core: Yield :: b -> (Pipe a b m r) -> Pipe a b m r
- Control.Pipe.Core: [unPipeC] :: PipeC m r a b -> Pipe a b m r
- Control.Pipe.Core: await :: Pipe a b m a
- Control.Pipe.Core: data C
- Control.Pipe.Core: data Pipe a b m r
- Control.Pipe.Core: idP :: (Monad m) => Pipe a a m r
- Control.Pipe.Core: infixl 9 >+>
- Control.Pipe.Core: infixr 9 <+<
- Control.Pipe.Core: instance Control.Monad.Trans.Class.MonadTrans (Control.Pipe.Core.Pipe a b)
- Control.Pipe.Core: instance GHC.Base.Monad m => Control.Category.Category (Control.Pipe.Core.PipeC m r)
- Control.Pipe.Core: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Pipe.Core.Pipe a b m)
- Control.Pipe.Core: instance GHC.Base.Monad m => GHC.Base.Functor (Control.Pipe.Core.Pipe a b m)
- Control.Pipe.Core: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Pipe.Core.Pipe a b m)
- Control.Pipe.Core: newtype PipeC m r a b
- Control.Pipe.Core: pipe :: (Monad m) => (a -> b) -> Pipe a b m r
- Control.Pipe.Core: runPipe :: (Monad m) => Pipeline m r -> m r
- Control.Pipe.Core: type Consumer b = Pipe b C
- Control.Pipe.Core: type Pipeline = Pipe () C
- Control.Pipe.Core: type Producer b = Pipe () b
- Control.Pipe.Core: yield :: b -> Pipe a b m ()
- Control.Proxy.Class: class Channel p where p1 >-> p2 = p2 <-< p1 p1 <-< p2 = p2 >-> p1
- Control.Proxy.Core: M :: (m (Proxy a' a b' b m r)) -> Proxy a' a b' b m r
- Control.Proxy.Core: Pure :: r -> Proxy a' a b' b m r
- Control.Proxy.Core: Request :: a' -> (a -> Proxy a' a b' b m r) -> Proxy a' a b' b m r
- Control.Proxy.Core: Respond :: b -> (b' -> Proxy a' a b' b m r) -> Proxy a' a b' b m r
- Control.Proxy.Core: data C
- Control.Proxy.Core: data Proxy a' a b' b m r
- Control.Proxy.Core: discard :: (Monad m) => () -> Proxy () a () C m r
- Control.Proxy.Core: ignore :: (Monad m) => a -> Proxy C () a () m r
- Control.Proxy.Core: instance Control.MFunctor.MFunctor (Control.Proxy.Core.Proxy a' a b' b)
- Control.Proxy.Core: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Proxy.Core.Proxy a' a b' b m)
- Control.Proxy.Core: instance Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Core.Proxy a' a b' b)
- Control.Proxy.Core: instance Control.Proxy.Class.Channel Control.Proxy.Core.Proxy
- Control.Proxy.Core: instance Control.Proxy.Class.Interact Control.Proxy.Core.Proxy
- Control.Proxy.Core: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Proxy.Core.Proxy a' a b' b m)
- Control.Proxy.Core: instance GHC.Base.Monad m => GHC.Base.Functor (Control.Proxy.Core.Proxy a' a b' b m)
- Control.Proxy.Core: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Proxy.Core.Proxy a' a b' b m)
- Control.Proxy.Core: runProxy :: (Monad m) => (() -> Proxy a' () () b m r) -> m r
- Control.Proxy.Core: runProxyK :: (Monad m) => (() -> Proxy a () () b m r) -> (() -> m r)
- Control.Proxy.Core: runSession :: (Monad m) => (() -> Session m r) -> m r
- Control.Proxy.Core: runSessionK :: (Monad m) => (() -> Session m r) -> (() -> m r)
- Control.Proxy.Core: type Client req resp = Proxy req resp () C
- Control.Proxy.Core: type Server req resp = Proxy C () req resp
- Control.Proxy.Core: type Session = Proxy C () () C
- Control.Proxy.Pipe: runPipe :: (Monad m) => Pipeline m r -> m r
- Control.Proxy.Pipe: type Consumer a = Pipe a C
- Control.Proxy.Pipe: type Pipe a b = Proxy () a () b
- Control.Proxy.Pipe: type Producer b = Pipe () b
- Control.Proxy.Prelude.IO: hGetLineD :: Handle -> y' -> Proxy x' x y' String IO r
- Control.Proxy.Prelude.IO: hGetLineU :: Handle -> y' -> Proxy String x y' y IO r
- Control.Proxy.Prelude.Kleisli: mapK :: (Monad m, MonadTrans t) => (a -> m b) -> (a -> t m b)
- Control.Proxy.Trans.Either: instance Control.MFunctor.MFunctor (p a' a b' b) => Control.MFunctor.MFunctor (Control.Proxy.Trans.Either.EitherP e p a' a b' b)
- Control.Proxy.Trans.Either: instance Control.Monad.IO.Class.MonadIO (p a' a b' b m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
- Control.Proxy.Trans.Either: instance Control.Monad.Trans.Class.MonadTrans (p a' a b' b) => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Either.EitherP e p a' a b' b)
- Control.Proxy.Trans.Either: instance Control.Proxy.Class.Channel p => Control.Proxy.Class.Channel (Control.Proxy.Trans.Either.EitherP e p)
- Control.Proxy.Trans.Either: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Applicative (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
- Control.Proxy.Trans.Either: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Functor (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
- Control.Proxy.Trans.Either: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Monad (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
- Control.Proxy.Trans.Either: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.Alternative (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
- Control.Proxy.Trans.Either: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
- Control.Proxy.Trans.Identity: instance Control.MFunctor.MFunctor (p a' a b' b) => Control.MFunctor.MFunctor (Control.Proxy.Trans.Identity.IdentityP p a' a b' b)
- Control.Proxy.Trans.Identity: instance Control.Monad.IO.Class.MonadIO (p a' a b' b m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
- Control.Proxy.Trans.Identity: instance Control.Monad.Trans.Class.MonadTrans (p a' a b' b) => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Identity.IdentityP p a' a b' b)
- Control.Proxy.Trans.Identity: instance Control.Proxy.Class.Channel p => Control.Proxy.Class.Channel (Control.Proxy.Trans.Identity.IdentityP p)
- Control.Proxy.Trans.Identity: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Applicative (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
- Control.Proxy.Trans.Identity: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Functor (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
- Control.Proxy.Trans.Identity: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Monad (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
- Control.Proxy.Trans.Identity: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.Alternative (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
- Control.Proxy.Trans.Identity: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
- Control.Proxy.Trans.Maybe: instance Control.MFunctor.MFunctor (p a' a b' b) => Control.MFunctor.MFunctor (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b)
- Control.Proxy.Trans.Maybe: instance Control.Monad.IO.Class.MonadIO (p a' a b' b m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
- Control.Proxy.Trans.Maybe: instance Control.Monad.Trans.Class.MonadTrans (p a' a b' b) => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b)
- Control.Proxy.Trans.Maybe: instance Control.Proxy.Class.Channel p => Control.Proxy.Class.Channel (Control.Proxy.Trans.Maybe.MaybeP p)
- Control.Proxy.Trans.Maybe: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Alternative (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
- Control.Proxy.Trans.Maybe: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Applicative (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
- Control.Proxy.Trans.Maybe: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Functor (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
- Control.Proxy.Trans.Maybe: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Monad (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
- Control.Proxy.Trans.Maybe: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
- Control.Proxy.Trans.Reader: instance Control.MFunctor.MFunctor (p a' a b' b) => Control.MFunctor.MFunctor (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b)
- Control.Proxy.Trans.Reader: instance Control.Monad.IO.Class.MonadIO (p a' a b' b m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
- Control.Proxy.Trans.Reader: instance Control.Monad.Trans.Class.MonadTrans (p a' a b' b) => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b)
- Control.Proxy.Trans.Reader: instance Control.Proxy.Class.Channel p => Control.Proxy.Class.Channel (Control.Proxy.Trans.Reader.ReaderP i p)
- Control.Proxy.Trans.Reader: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Applicative (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
- Control.Proxy.Trans.Reader: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Functor (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
- Control.Proxy.Trans.Reader: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Monad (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
- Control.Proxy.Trans.Reader: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.Alternative (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
- Control.Proxy.Trans.Reader: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
- Control.Proxy.Trans.State: instance Control.MFunctor.MFunctor (p a' a b' b) => Control.MFunctor.MFunctor (Control.Proxy.Trans.State.StateP s p a' a b' b)
- Control.Proxy.Trans.State: instance Control.Monad.IO.Class.MonadIO (p a' a b' b m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.State.StateP s p a' a b' b m)
- Control.Proxy.Trans.State: instance Control.Monad.Trans.Class.MonadTrans (p a' a b' b) => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.State.StateP s p a' a b' b)
- Control.Proxy.Trans.State: instance Control.Proxy.Class.Channel p => Control.Proxy.Class.Channel (Control.Proxy.Trans.State.StateP s p)
- Control.Proxy.Trans.State: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Applicative (Control.Proxy.Trans.State.StateP s p a' a b' b m)
- Control.Proxy.Trans.State: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Functor (Control.Proxy.Trans.State.StateP s p a' a b' b m)
- Control.Proxy.Trans.State: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Monad (Control.Proxy.Trans.State.StateP s p a' a b' b m)
- Control.Proxy.Trans.State: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.Alternative (Control.Proxy.Trans.State.StateP s p a' a b' b m)
- Control.Proxy.Trans.State: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.MonadPlus (Control.Proxy.Trans.State.StateP s p a' a b' b m)
- Control.Proxy.Trans.Writer: instance Control.MFunctor.MFunctor (p a' a b' b) => Control.MFunctor.MFunctor (Control.Proxy.Trans.Writer.WriterP w p a' a b' b)
- Control.Proxy.Trans.Writer: instance Control.Monad.IO.Class.MonadIO (p a' a b' b m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
- Control.Proxy.Trans.Writer: instance Control.Monad.Trans.Class.MonadTrans (p a' a b' b) => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Writer.WriterP w p a' a b' b)
- Control.Proxy.Trans.Writer: instance Control.Proxy.Class.Channel p => Control.Proxy.Class.Channel (Control.Proxy.Trans.Writer.WriterP w p)
- Control.Proxy.Trans.Writer: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Applicative (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
- Control.Proxy.Trans.Writer: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Functor (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
- Control.Proxy.Trans.Writer: instance GHC.Base.Monad (p a' a b' b m) => GHC.Base.Monad (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
- Control.Proxy.Trans.Writer: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.Alternative (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
- Control.Proxy.Trans.Writer: instance GHC.Base.MonadPlus (p a' a b' b m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
- Control.Proxy.Trans.Writer: instance GHC.Base.Monoid w => Control.Proxy.Trans.ProxyTrans (Control.Proxy.Trans.Writer.WriterP w)
- Data.Closed: data C
+ Control.MFunctor: hoist :: (MFunctor t, Monad m) => (forall a. m a -> n a) -> t m b -> t n b
+ Control.MFunctor: instance Control.MFunctor.MFunctor (Control.Monad.Trans.RWS.Lazy.RWST r w s)
+ Control.MFunctor: instance Control.MFunctor.MFunctor (Control.Monad.Trans.Reader.ReaderT r)
+ Control.MFunctor: instance Control.MFunctor.MFunctor (Control.Monad.Trans.State.Lazy.StateT s)
+ Control.MFunctor: instance Control.MFunctor.MFunctor (Control.Monad.Trans.State.Strict.StateT s)
+ Control.MFunctor: instance Control.MFunctor.MFunctor (Control.Monad.Trans.Writer.Lazy.WriterT w)
+ Control.MFunctor: instance Control.MFunctor.MFunctor (Control.Monad.Trans.Writer.Strict.WriterT w)
+ Control.MFunctor: instance Control.MFunctor.MFunctor Control.Monad.Trans.Identity.IdentityT
+ Control.MFunctor: instance Control.MFunctor.MFunctor Control.Monad.Trans.Maybe.MaybeT
+ Control.MFunctor: raise :: (Monad m, MFunctor t1, MonadTrans t2) => t1 m r -> t1 (t2 m) r
+ Control.PFunctor: class PFunctor (t :: (* -> * -> * -> * -> (* -> *) -> * -> *) -> * -> * -> * -> * -> (* -> *) -> * -> *)
+ Control.PFunctor: hoistP :: (PFunctor t, Monad m, Proxy p) => (forall a' a b' b r1. p a' a b' b m r1 -> q a' a b' b m r1) -> (t p a' a b' b m r2 -> t q a' a b' b m r2)
+ Control.PFunctor: raiseP :: (Monad m, Proxy p, PFunctor t1, ProxyTrans t2) => t1 p a' a b' b m r -> t1 (t2 p) a' a b' b m r
+ Control.Pipe: (<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r
+ Control.Pipe: (>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r
+ Control.Pipe: Await :: (a -> Pipe a b m r) -> Pipe a b m r
+ Control.Pipe: M :: (m (Pipe a b m r)) -> Pipe a b m r
+ Control.Pipe: PipeC :: Pipe a b m r -> PipeC m r a b
+ Control.Pipe: Pure :: r -> Pipe a b m r
+ Control.Pipe: Yield :: b -> (Pipe a b m r) -> Pipe a b m r
+ Control.Pipe: [unPipeC] :: PipeC m r a b -> Pipe a b m r
+ Control.Pipe: await :: Pipe a b m a
+ Control.Pipe: data Pipe a b m r
+ Control.Pipe: idP :: (Monad m) => Pipe a a m r
+ Control.Pipe: infixl 8 >+>
+ Control.Pipe: infixr 8 <+<
+ Control.Pipe: instance Control.Monad.Trans.Class.MonadTrans (Control.Pipe.Pipe a b)
+ Control.Pipe: instance GHC.Base.Monad m => Control.Category.Category (Control.Pipe.PipeC m r)
+ Control.Pipe: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Pipe.Pipe a b m)
+ Control.Pipe: instance GHC.Base.Monad m => GHC.Base.Functor (Control.Pipe.Pipe a b m)
+ Control.Pipe: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Pipe.Pipe a b m)
+ Control.Pipe: newtype PipeC m r a b
+ Control.Pipe: pipe :: (Monad m) => (a -> b) -> Pipe a b m r
+ Control.Pipe: runPipe :: (Monad m) => Pipe () b m r -> m r
+ Control.Pipe: type Consumer a m r = Pipe a C m r
+ Control.Pipe: type Pipeline m r = Pipe () C m r
+ Control.Pipe: type Producer b m r = Pipe () b m r
+ Control.Pipe: yield :: b -> Pipe a b m ()
+ Control.Proxy.Class: (<~<) :: (Monad m, Proxy p) => (b -> p b' b c' c m r) -> (a -> p a' a b' b m r) -> (a -> p a' a c' c m r)
+ Control.Proxy.Class: (>~>) :: (Proxy p, Monad m) => (a -> p a' a b' b m r) -> (b -> p b' b c' c m r) -> (a -> p a' a c' c m r)
+ Control.Proxy.Class: (?>=) :: (Proxy p, Monad m) => p a' a b' b m r -> (r -> p a' a b' b m r') -> p a' a b' b m r'
+ Control.Proxy.Class: class (Proxy p) => MonadIOP p
+ Control.Proxy.Class: class (Proxy p) => MonadPlusP p
+ Control.Proxy.Class: class Proxy p
+ Control.Proxy.Class: coidT :: (Monad m, Proxy p) => a -> p a' a a' a m r
+ Control.Proxy.Class: hoist_P :: (Proxy p, Monad m) => (forall r. m r -> n r) -> (p a' a b' b m r' -> p a' a b' b n r')
+ Control.Proxy.Class: infixl 8 \<\
+ Control.Proxy.Class: infixr 7 <-<
+ Control.Proxy.Class: infixr 8 /</
+ Control.Proxy.Class: liftIO_P :: (MonadIOP p, MonadIO m) => IO r -> p a' a b' b m r
+ Control.Proxy.Class: lift_P :: (Proxy p, Monad m) => m r -> p a' a b' b m r
+ Control.Proxy.Class: mplus_P :: (MonadPlusP p, Monad m) => p a' a b' b m r -> p a' a b' b m r -> p a' a b' b m r
+ Control.Proxy.Class: mzero_P :: (MonadPlusP p, Monad m) => p a' a b' b m r
+ Control.Proxy.Class: return_P :: (Proxy p, Monad m) => r -> p a' a b' b m r
+ Control.Proxy.Core.Correct: Proxy :: m (ProxyF a' a b' b r (ProxyCorrect a' a b' b m r)) -> ProxyCorrect a' a b' b m r
+ Control.Proxy.Core.Correct: Pure :: r -> ProxyF a' a b' b r x
+ Control.Proxy.Core.Correct: Request :: a' -> (a -> x) -> ProxyF a' a b' b r x
+ Control.Proxy.Core.Correct: Respond :: b -> (b' -> x) -> ProxyF a' a b' b r x
+ Control.Proxy.Core.Correct: [unProxy] :: ProxyCorrect a' a b' b m r -> m (ProxyF a' a b' b r (ProxyCorrect a' a b' b m r))
+ Control.Proxy.Core.Correct: data ProxyCorrect a' a b' b m r
+ Control.Proxy.Core.Correct: data ProxyF a' a b' b r x
+ Control.Proxy.Core.Correct: instance Control.MFunctor.MFunctor (Control.Proxy.Core.Correct.ProxyCorrect a' a b' b)
+ Control.Proxy.Core.Correct: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Proxy.Core.Correct.ProxyCorrect a' a b' b m)
+ Control.Proxy.Core.Correct: instance Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Core.Correct.ProxyCorrect a' a b' b)
+ Control.Proxy.Core.Correct: instance Control.Proxy.Class.Interact Control.Proxy.Core.Correct.ProxyCorrect
+ Control.Proxy.Core.Correct: instance Control.Proxy.Class.MonadIOP Control.Proxy.Core.Correct.ProxyCorrect
+ Control.Proxy.Core.Correct: instance Control.Proxy.Class.Proxy Control.Proxy.Core.Correct.ProxyCorrect
+ Control.Proxy.Core.Correct: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Proxy.Core.Correct.ProxyCorrect a' a b' b m)
+ Control.Proxy.Core.Correct: instance GHC.Base.Monad m => GHC.Base.Functor (Control.Proxy.Core.Correct.ProxyCorrect a' a b' b m)
+ Control.Proxy.Core.Correct: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Proxy.Core.Correct.ProxyCorrect a' a b' b m)
+ Control.Proxy.Core.Correct: runPipe :: (Monad m) => ProxyCorrect a' () () b m r -> m r
+ Control.Proxy.Core.Correct: runProxy :: (Monad m) => (() -> ProxyCorrect a' () () b m r) -> m r
+ Control.Proxy.Core.Correct: runProxyK :: (Monad m) => (() -> ProxyCorrect a' () () b m r) -> (() -> m r)
+ Control.Proxy.Core.Fast: M :: (m (ProxyFast a' a b' b m r)) -> ProxyFast a' a b' b m r
+ Control.Proxy.Core.Fast: Pure :: r -> ProxyFast a' a b' b m r
+ Control.Proxy.Core.Fast: Request :: a' -> (a -> ProxyFast a' a b' b m r) -> ProxyFast a' a b' b m r
+ Control.Proxy.Core.Fast: Respond :: b -> (b' -> ProxyFast a' a b' b m r) -> ProxyFast a' a b' b m r
+ Control.Proxy.Core.Fast: data ProxyFast a' a b' b m r
+ Control.Proxy.Core.Fast: instance Control.MFunctor.MFunctor (Control.Proxy.Core.Fast.ProxyFast a' a b' b)
+ Control.Proxy.Core.Fast: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Control.Proxy.Core.Fast.ProxyFast a' a b' b m)
+ Control.Proxy.Core.Fast: instance Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Core.Fast.ProxyFast a' a b' b)
+ Control.Proxy.Core.Fast: instance Control.Proxy.Class.Interact Control.Proxy.Core.Fast.ProxyFast
+ Control.Proxy.Core.Fast: instance Control.Proxy.Class.MonadIOP Control.Proxy.Core.Fast.ProxyFast
+ Control.Proxy.Core.Fast: instance Control.Proxy.Class.Proxy Control.Proxy.Core.Fast.ProxyFast
+ Control.Proxy.Core.Fast: instance GHC.Base.Monad m => GHC.Base.Applicative (Control.Proxy.Core.Fast.ProxyFast a' a b' b m)
+ Control.Proxy.Core.Fast: instance GHC.Base.Monad m => GHC.Base.Functor (Control.Proxy.Core.Fast.ProxyFast a' a b' b m)
+ Control.Proxy.Core.Fast: instance GHC.Base.Monad m => GHC.Base.Monad (Control.Proxy.Core.Fast.ProxyFast a' a b' b m)
+ Control.Proxy.Core.Fast: observe :: (Monad m) => ProxyFast a' a b' b m r -> ProxyFast a' a b' b m r
+ Control.Proxy.Core.Fast: runPipe :: (Monad m) => ProxyFast a' () () b m r -> m r
+ Control.Proxy.Core.Fast: runProxy :: (Monad m) => (() -> ProxyFast a' () () b m r) -> m r
+ Control.Proxy.Core.Fast: runProxyK :: (Monad m) => (() -> ProxyFast a' () () b m r) -> (() -> m r)
+ Control.Proxy.Prelude.Base: allD :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT All m) r
+ Control.Proxy.Prelude.Base: allD_ :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT All m) ()
+ Control.Proxy.Prelude.Base: allU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT All m) r
+ Control.Proxy.Prelude.Base: allU_ :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT All m) ()
+ Control.Proxy.Prelude.Base: anyD :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT Any m) r
+ Control.Proxy.Prelude.Base: anyD_ :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT Any m) ()
+ Control.Proxy.Prelude.Base: anyU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT Any m) r
+ Control.Proxy.Prelude.Base: anyU_ :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT Any m) ()
+ Control.Proxy.Prelude.Base: foldD :: (Monad m, Proxy p, Monoid w) => (a -> w) -> x -> p x a x a (WriterT w m) r
+ Control.Proxy.Prelude.Base: foldU :: (Monad m, Proxy p, Monoid w) => (a' -> w) -> a' -> p a' x a' x (WriterT w m) r
+ Control.Proxy.Prelude.Base: foldlD' :: (Monad m, Proxy p) => (b -> a -> b) -> x -> p x a x a (StateT b m) r
+ Control.Proxy.Prelude.Base: foldlU' :: (Monad m, Proxy p) => (b -> a' -> b) -> a' -> p a' x a' x (StateT b m) r
+ Control.Proxy.Prelude.Base: foldrD :: (Monad m, Proxy p) => (a -> b -> b) -> x -> p x a x a (WriterT (Endo b) m) r
+ Control.Proxy.Prelude.Base: foldrU :: (Monad m, Proxy p) => (a' -> b -> b) -> a' -> p a' x a' x (WriterT (Endo b) m) r
+ Control.Proxy.Prelude.Base: headD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (First a) m) r
+ Control.Proxy.Prelude.Base: headD_ :: (Monad m, Proxy p) => x -> p x a x a (WriterT (First a) m) ()
+ Control.Proxy.Prelude.Base: headU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (First a') m) r
+ Control.Proxy.Prelude.Base: headU_ :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (First a') m) ()
+ Control.Proxy.Prelude.Base: lastD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (Last a) m) r
+ Control.Proxy.Prelude.Base: lastU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (Last a') m) r
+ Control.Proxy.Prelude.Base: lengthD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (Sum Int) m) r
+ Control.Proxy.Prelude.Base: lengthU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (Sum Int) m) r
+ Control.Proxy.Prelude.Base: mergeD :: (Monad m, Proxy p1, Proxy p2, Proxy p3) => () -> Consumer p1 a (Consumer p2 a (Producer p3 a m)) r
+ Control.Proxy.Prelude.Base: productD :: (Monad m, Proxy p, Num a) => x -> p x a x a (WriterT (Product a) m) r
+ Control.Proxy.Prelude.Base: productU :: (Monad m, Proxy p, Num a') => a' -> p a' x a' x (WriterT (Product a') m) r
+ Control.Proxy.Prelude.Base: sumD :: (Monad m, Proxy p, Num a) => x -> p x a x a (WriterT (Sum a) m) r
+ Control.Proxy.Prelude.Base: sumU :: (Monad m, Proxy p, Num a') => a' -> p a' x a' x (WriterT (Sum a') m) r
+ Control.Proxy.Prelude.Base: toListD :: (Monad m, Proxy p) => x -> p x a x a (WriterT [a] m) r
+ Control.Proxy.Prelude.Base: toListU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT [a'] m) r
+ Control.Proxy.Prelude.Base: unitD :: (Monad m, Proxy p) => y' -> p x' x y' () m r
+ Control.Proxy.Prelude.Base: unitU :: (Monad m, Proxy p) => y' -> p () x y' y m r
+ Control.Proxy.Prelude.Base: useB :: (Monad m, Proxy p) => (a -> m r1) -> (a' -> m r2) -> a' -> p a' a a' a m r
+ Control.Proxy.Prelude.Base: useD :: (Monad m, Proxy p) => (a -> m r1) -> x -> p x a x a m r
+ Control.Proxy.Prelude.Base: useU :: (Monad m, Proxy p) => (a' -> m r2) -> a' -> p a' x a' x m r
+ Control.Proxy.Prelude.Base: zipD :: (Monad m, Proxy p1, Proxy p2, Proxy p3) => () -> Consumer p1 a (Consumer p2 b (Producer p3 (a, b) m)) r
+ Control.Proxy.Prelude.IO: hGetLineC :: (Proxy p) => Handle -> () -> CoProducer p String IO ()
+ Control.Proxy.Prelude.IO: hGetLineS :: (Proxy p) => Handle -> () -> Producer p String IO ()
+ Control.Proxy.Prelude.Kleisli: hoistK :: (Monad m, MFunctor t) => (forall a. m a -> n a) -> ((b' -> t m b) -> (b' -> t n b))
+ Control.Proxy.Prelude.Kleisli: liftK :: (Monad m, MonadTrans t) => (a -> m b) -> (a -> t m b)
+ Control.Proxy.Prelude.Kleisli: raiseK :: (Monad m, MFunctor t1, MonadTrans t2) => (q -> t1 m r) -> (q -> t1 (t2 m) r)
+ Control.Proxy.Synonym: data C
+ Control.Proxy.Synonym: type Client (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a' a = p a' a () C
+ Control.Proxy.Synonym: type CoConsumer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) b' = p C () b' ()
+ Control.Proxy.Synonym: type CoPipe (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a' b' = p a' () b' ()
+ Control.Proxy.Synonym: type CoProducer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a' = p a' () () C
+ Control.Proxy.Synonym: type Consumer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a = p () a () C
+ Control.Proxy.Synonym: type Pipe (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a b = p () a () b
+ Control.Proxy.Synonym: type Producer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) b = p C () () b
+ Control.Proxy.Synonym: type Server (p :: * -> * -> * -> * -> (* -> *) -> * -> *) b' b = p C () b' b
+ Control.Proxy.Synonym: type Session (p :: * -> * -> * -> * -> (* -> *) -> * -> *) = p C () () C
+ Control.Proxy.Trans.Either: instance (Control.Proxy.Class.MonadIOP p, Control.Monad.IO.Class.MonadIO m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
+ Control.Proxy.Trans.Either: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.Alternative (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
+ Control.Proxy.Trans.Either: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
+ Control.Proxy.Trans.Either: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Applicative (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
+ Control.Proxy.Trans.Either: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Functor (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
+ Control.Proxy.Trans.Either: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Monad (Control.Proxy.Trans.Either.EitherP e p a' a b' b m)
+ Control.Proxy.Trans.Either: instance Control.PFunctor.PFunctor (Control.Proxy.Trans.Either.EitherP e)
+ Control.Proxy.Trans.Either: instance Control.Proxy.Class.MonadIOP p => Control.Proxy.Class.MonadIOP (Control.Proxy.Trans.Either.EitherP e p)
+ Control.Proxy.Trans.Either: instance Control.Proxy.Class.MonadPlusP p => Control.Proxy.Class.MonadPlusP (Control.Proxy.Trans.Either.EitherP e p)
+ Control.Proxy.Trans.Either: instance Control.Proxy.Class.Proxy p => Control.MFunctor.MFunctor (Control.Proxy.Trans.Either.EitherP e p a' a b' b)
+ Control.Proxy.Trans.Either: instance Control.Proxy.Class.Proxy p => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Either.EitherP e p a' a b' b)
+ Control.Proxy.Trans.Either: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.Proxy (Control.Proxy.Trans.Either.EitherP e p)
+ Control.Proxy.Trans.Identity: identityK :: (q -> p a' a b' b m r) -> (q -> IdentityP p a' a b' b m r)
+ Control.Proxy.Trans.Identity: instance (Control.Proxy.Class.MonadIOP p, Control.Monad.IO.Class.MonadIO m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
+ Control.Proxy.Trans.Identity: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.Alternative (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
+ Control.Proxy.Trans.Identity: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
+ Control.Proxy.Trans.Identity: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Applicative (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
+ Control.Proxy.Trans.Identity: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Functor (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
+ Control.Proxy.Trans.Identity: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Monad (Control.Proxy.Trans.Identity.IdentityP p a' a b' b m)
+ Control.Proxy.Trans.Identity: instance Control.PFunctor.PFunctor Control.Proxy.Trans.Identity.IdentityP
+ Control.Proxy.Trans.Identity: instance Control.Proxy.Class.MonadIOP p => Control.Proxy.Class.MonadIOP (Control.Proxy.Trans.Identity.IdentityP p)
+ Control.Proxy.Trans.Identity: instance Control.Proxy.Class.MonadPlusP p => Control.Proxy.Class.MonadPlusP (Control.Proxy.Trans.Identity.IdentityP p)
+ Control.Proxy.Trans.Identity: instance Control.Proxy.Class.Proxy p => Control.MFunctor.MFunctor (Control.Proxy.Trans.Identity.IdentityP p a' a b' b)
+ Control.Proxy.Trans.Identity: instance Control.Proxy.Class.Proxy p => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Identity.IdentityP p a' a b' b)
+ Control.Proxy.Trans.Identity: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.Proxy (Control.Proxy.Trans.Identity.IdentityP p)
+ Control.Proxy.Trans.Maybe: instance (Control.Proxy.Class.MonadIOP p, Control.Monad.IO.Class.MonadIO m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
+ Control.Proxy.Trans.Maybe: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Alternative (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
+ Control.Proxy.Trans.Maybe: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Applicative (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
+ Control.Proxy.Trans.Maybe: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Functor (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
+ Control.Proxy.Trans.Maybe: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Monad (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
+ Control.Proxy.Trans.Maybe: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b m)
+ Control.Proxy.Trans.Maybe: instance Control.PFunctor.PFunctor Control.Proxy.Trans.Maybe.MaybeP
+ Control.Proxy.Trans.Maybe: instance Control.Proxy.Class.MonadIOP p => Control.Proxy.Class.MonadIOP (Control.Proxy.Trans.Maybe.MaybeP p)
+ Control.Proxy.Trans.Maybe: instance Control.Proxy.Class.Proxy p => Control.MFunctor.MFunctor (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b)
+ Control.Proxy.Trans.Maybe: instance Control.Proxy.Class.Proxy p => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Maybe.MaybeP p a' a b' b)
+ Control.Proxy.Trans.Maybe: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.MonadPlusP (Control.Proxy.Trans.Maybe.MaybeP p)
+ Control.Proxy.Trans.Maybe: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.Proxy (Control.Proxy.Trans.Maybe.MaybeP p)
+ Control.Proxy.Trans.Reader: instance (Control.Proxy.Class.MonadIOP p, Control.Monad.IO.Class.MonadIO m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
+ Control.Proxy.Trans.Reader: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.Alternative (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
+ Control.Proxy.Trans.Reader: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
+ Control.Proxy.Trans.Reader: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Applicative (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
+ Control.Proxy.Trans.Reader: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Functor (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
+ Control.Proxy.Trans.Reader: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Monad (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b m)
+ Control.Proxy.Trans.Reader: instance Control.PFunctor.PFunctor (Control.Proxy.Trans.Reader.ReaderP i)
+ Control.Proxy.Trans.Reader: instance Control.Proxy.Class.MonadIOP p => Control.Proxy.Class.MonadIOP (Control.Proxy.Trans.Reader.ReaderP i p)
+ Control.Proxy.Trans.Reader: instance Control.Proxy.Class.MonadPlusP p => Control.Proxy.Class.MonadPlusP (Control.Proxy.Trans.Reader.ReaderP i p)
+ Control.Proxy.Trans.Reader: instance Control.Proxy.Class.Proxy p => Control.MFunctor.MFunctor (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b)
+ Control.Proxy.Trans.Reader: instance Control.Proxy.Class.Proxy p => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Reader.ReaderP i p a' a b' b)
+ Control.Proxy.Trans.Reader: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.Proxy (Control.Proxy.Trans.Reader.ReaderP i p)
+ Control.Proxy.Trans.State: instance (Control.Proxy.Class.MonadIOP p, Control.Monad.IO.Class.MonadIO m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.State.StateP s p a' a b' b m)
+ Control.Proxy.Trans.State: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.Alternative (Control.Proxy.Trans.State.StateP s p a' a b' b m)
+ Control.Proxy.Trans.State: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.MonadPlus (Control.Proxy.Trans.State.StateP s p a' a b' b m)
+ Control.Proxy.Trans.State: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Applicative (Control.Proxy.Trans.State.StateP s p a' a b' b m)
+ Control.Proxy.Trans.State: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Functor (Control.Proxy.Trans.State.StateP s p a' a b' b m)
+ Control.Proxy.Trans.State: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Monad (Control.Proxy.Trans.State.StateP s p a' a b' b m)
+ Control.Proxy.Trans.State: instance Control.PFunctor.PFunctor (Control.Proxy.Trans.State.StateP s)
+ Control.Proxy.Trans.State: instance Control.Proxy.Class.MonadIOP p => Control.Proxy.Class.MonadIOP (Control.Proxy.Trans.State.StateP s p)
+ Control.Proxy.Trans.State: instance Control.Proxy.Class.MonadPlusP p => Control.Proxy.Class.MonadPlusP (Control.Proxy.Trans.State.StateP s p)
+ Control.Proxy.Trans.State: instance Control.Proxy.Class.Proxy p => Control.MFunctor.MFunctor (Control.Proxy.Trans.State.StateP s p a' a b' b)
+ Control.Proxy.Trans.State: instance Control.Proxy.Class.Proxy p => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.State.StateP s p a' a b' b)
+ Control.Proxy.Trans.State: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.Proxy (Control.Proxy.Trans.State.StateP s p)
+ Control.Proxy.Trans.Writer: instance (Control.Proxy.Class.MonadIOP p, Control.Monad.IO.Class.MonadIO m) => Control.Monad.IO.Class.MonadIO (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
+ Control.Proxy.Trans.Writer: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.Alternative (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
+ Control.Proxy.Trans.Writer: instance (Control.Proxy.Class.MonadPlusP p, GHC.Base.Monad m) => GHC.Base.MonadPlus (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
+ Control.Proxy.Trans.Writer: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Applicative (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
+ Control.Proxy.Trans.Writer: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Functor (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
+ Control.Proxy.Trans.Writer: instance (Control.Proxy.Class.Proxy p, GHC.Base.Monad m) => GHC.Base.Monad (Control.Proxy.Trans.Writer.WriterP w p a' a b' b m)
+ Control.Proxy.Trans.Writer: instance Control.PFunctor.PFunctor (Control.Proxy.Trans.Writer.WriterP w)
+ Control.Proxy.Trans.Writer: instance Control.Proxy.Class.MonadIOP p => Control.Proxy.Class.MonadIOP (Control.Proxy.Trans.Writer.WriterP w p)
+ Control.Proxy.Trans.Writer: instance Control.Proxy.Class.MonadPlusP p => Control.Proxy.Class.MonadPlusP (Control.Proxy.Trans.Writer.WriterP w p)
+ Control.Proxy.Trans.Writer: instance Control.Proxy.Class.Proxy p => Control.MFunctor.MFunctor (Control.Proxy.Trans.Writer.WriterP w p a' a b' b)
+ Control.Proxy.Trans.Writer: instance Control.Proxy.Class.Proxy p => Control.Monad.Trans.Class.MonadTrans (Control.Proxy.Trans.Writer.WriterP w p a' a b' b)
+ Control.Proxy.Trans.Writer: instance Control.Proxy.Class.Proxy p => Control.Proxy.Class.Proxy (Control.Proxy.Trans.Writer.WriterP w p)
+ Control.Proxy.Trans.Writer: instance Control.Proxy.Trans.ProxyTrans (Control.Proxy.Trans.Writer.WriterP w)
- Control.Proxy.Class: (/</) :: (Interact p, Monad m) => (c' -> p b' b x' x m c) -> (b' -> p a' a x' x m b) -> (c' -> p a' a x' x m c)
+ Control.Proxy.Class: (/</) :: (Monad m, Interact p) => (c' -> p b' b x' x m c) -> (b' -> p a' a x' x m b) -> (c' -> p a' a x' x m c)
- Control.Proxy.Class: (<-<) :: (Channel p, Monad m) => (c' -> p b' b c' c m r) -> (b' -> p a' a b' b m r) -> (c' -> p a' a c' c m r)
+ Control.Proxy.Class: (<-<) :: (Monad m, Proxy p) => (c' -> p b' b c' c m r) -> (b' -> p a' a b' b m r) -> (c' -> p a' a c' c m r)
- Control.Proxy.Class: (>->) :: (Channel p, Monad m) => (b' -> p a' a b' b m r) -> (c' -> p b' b c' c m r) -> (c' -> p a' a c' c m r)
+ Control.Proxy.Class: (>->) :: (Proxy p, Monad m) => (b' -> p a' a b' b m r) -> (c' -> p b' b c' c m r) -> (c' -> p a' a c' c m r)
- Control.Proxy.Class: (\<\) :: (Interact p, Monad m) => (b -> p x' x c' c m b') -> (a -> p x' x b' b m a') -> (a -> p x' x c' c m a')
+ Control.Proxy.Class: (\<\) :: (Monad m, Interact p) => (b -> p x' x c' c m b') -> (a -> p x' x b' b m a') -> (a -> p x' x c' c m a')
- Control.Proxy.Class: class Interact p where p1 \>\ p2 = p2 /</ p1 p1 /</ p2 = p2 \>\ p1 p1 />/ p2 = p2 \<\ p1 p1 \<\ p2 = p2 />/ p1
+ Control.Proxy.Class: class Interact p
- Control.Proxy.Class: idT :: (Channel p, Monad m) => a' -> p a' a a' a m r
+ Control.Proxy.Class: idT :: (Monad m, Proxy p) => a' -> p a' a a' a m r
- Control.Proxy.Class: request :: (Interact p, Monad m) => a' -> p a' a x' x m a
+ Control.Proxy.Class: request :: (Proxy p, Monad m) => a' -> p a' a b' b m a
- Control.Proxy.Class: respond :: (Interact p, Monad m) => a -> p x' x a' a m a'
+ Control.Proxy.Class: respond :: (Proxy p, Monad m) => b -> p a' a b' b m b'
- Control.Proxy.Pipe: (<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r
+ Control.Proxy.Pipe: (<+<) :: (Monad m, Proxy p) => Pipe p b c m r -> Pipe p a b m r -> Pipe p a c m r
- Control.Proxy.Pipe: (>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r
+ Control.Proxy.Pipe: (>+>) :: (Monad m, Proxy p) => Pipe p a b m r -> Pipe p b c m r -> Pipe p a c m r
- Control.Proxy.Pipe: await :: (Monad m) => Pipe a b m a
+ Control.Proxy.Pipe: await :: (Monad m, Proxy p) => Pipe p a b m a
- Control.Proxy.Pipe: idP :: (Monad m) => Pipe a a m r
+ Control.Proxy.Pipe: idP :: (Monad m, Proxy p) => Pipe p a a m r
- Control.Proxy.Pipe: pipe :: (Monad m) => (a -> b) -> Pipe a b m r
+ Control.Proxy.Pipe: pipe :: (Monad m, Proxy p) => (a -> b) -> Pipe p a b m r
- Control.Proxy.Pipe: type Pipeline = Pipe () C
+ Control.Proxy.Pipe: type Pipeline (p :: * -> * -> * -> * -> (* -> *) -> * -> *) = p C () () C
- Control.Proxy.Pipe: yield :: (Monad m) => b -> Pipe a b m ()
+ Control.Proxy.Pipe: yield :: (Monad m, Proxy p) => b -> p a' a b' b m ()
- Control.Proxy.Prelude.Base: dropD :: (Monad m) => Int -> () -> Proxy () a () a m r
+ Control.Proxy.Prelude.Base: dropD :: (Monad m, Proxy p) => Int -> () -> Pipe p a a m r
- Control.Proxy.Prelude.Base: dropU :: (Monad m) => Int -> a' -> Proxy a' () a' () m r
+ Control.Proxy.Prelude.Base: dropU :: (Monad m, Proxy p) => Int -> a' -> CoPipe p a' a' m r
- Control.Proxy.Prelude.Base: dropWhileD :: (Monad m) => (a -> Bool) -> () -> Proxy () a () a m r
+ Control.Proxy.Prelude.Base: dropWhileD :: (Monad m, Proxy p) => (a -> Bool) -> () -> Pipe p a a m r
- Control.Proxy.Prelude.Base: dropWhileU :: (Monad m) => (a' -> Bool) -> a' -> Proxy a' () a' () m r
+ Control.Proxy.Prelude.Base: dropWhileU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> CoPipe p a' a' m r
- Control.Proxy.Prelude.Base: enumFromC :: (Enum a, Monad m) => a -> y' -> Proxy a x y' y m r
+ Control.Proxy.Prelude.Base: enumFromC :: (Enum a', Monad m, Proxy p) => a' -> () -> CoProducer p a' m r
- Control.Proxy.Prelude.Base: enumFromS :: (Enum a, Monad m) => a -> y' -> Proxy x' x y' a m r
+ Control.Proxy.Prelude.Base: enumFromS :: (Enum b, Monad m, Proxy p) => b -> () -> Producer p b m r
- Control.Proxy.Prelude.Base: enumFromToC :: (Enum a, Ord a, Monad m) => a -> a -> y' -> Proxy a x y' y m ()
+ Control.Proxy.Prelude.Base: enumFromToC :: (Enum a', Ord a', Monad m, Proxy p) => a' -> a' -> () -> CoProducer p a' m ()
- Control.Proxy.Prelude.Base: enumFromToS :: (Enum a, Ord a, Monad m) => a -> a -> y' -> Proxy x' x y' a m ()
+ Control.Proxy.Prelude.Base: enumFromToS :: (Enum b, Ord b, Monad m, Proxy p) => b -> b -> () -> Producer p b m ()
- Control.Proxy.Prelude.Base: execB :: (Monad m) => m () -> m () -> a' -> Proxy a' a a' a m r
+ Control.Proxy.Prelude.Base: execB :: (Monad m, Proxy p) => m r1 -> m r2 -> a' -> p a' a a' a m r
- Control.Proxy.Prelude.Base: execD :: (Monad m) => m () -> a' -> Proxy a' a a' a m r
+ Control.Proxy.Prelude.Base: execD :: (Monad m, Proxy p) => m r1 -> a' -> p a' a a' a m r
- Control.Proxy.Prelude.Base: execU :: (Monad m) => m () -> a' -> Proxy a' a a' a m r
+ Control.Proxy.Prelude.Base: execU :: (Monad m, Proxy p) => m r2 -> a' -> p a' a a' a m r
- Control.Proxy.Prelude.Base: filterD :: (Monad m) => (a -> Bool) -> () -> Proxy () a () a m r
+ Control.Proxy.Prelude.Base: filterD :: (Monad m, Proxy p) => (a -> Bool) -> () -> Pipe p a a m r
- Control.Proxy.Prelude.Base: filterU :: (Monad m) => (a' -> Bool) -> a' -> Proxy a' () a' () m r
+ Control.Proxy.Prelude.Base: filterU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> CoPipe p a' a' m r
- Control.Proxy.Prelude.Base: fromListC :: (Monad m) => [a] -> () -> Proxy a x () y m ()
+ Control.Proxy.Prelude.Base: fromListC :: (Monad m, Proxy p) => [a'] -> () -> CoProducer p a' m ()
- Control.Proxy.Prelude.Base: fromListS :: (Monad m) => [a] -> () -> Proxy x' x () a m ()
+ Control.Proxy.Prelude.Base: fromListS :: (Monad m, Proxy p) => [b] -> () -> Producer p b m ()
- Control.Proxy.Prelude.Base: mapB :: (Monad m) => (a -> b) -> (b' -> a') -> b' -> Proxy a' a b' b m r
+ Control.Proxy.Prelude.Base: mapB :: (Monad m, Proxy p) => (a -> b) -> (b' -> a') -> b' -> p a' a b' b m r
- Control.Proxy.Prelude.Base: mapD :: (Monad m) => (a -> b) -> x -> Proxy x a x b m r
+ Control.Proxy.Prelude.Base: mapD :: (Monad m, Proxy p) => (a -> b) -> x -> p x a x b m r
- Control.Proxy.Prelude.Base: mapMB :: (Monad m) => (a -> m b) -> (b' -> m a') -> b' -> Proxy a' a b' b m r
+ Control.Proxy.Prelude.Base: mapMB :: (Monad m, Proxy p) => (a -> m b) -> (b' -> m a') -> b' -> p a' a b' b m r
- Control.Proxy.Prelude.Base: mapMD :: (Monad m) => (a -> m b) -> x -> Proxy x a x b m r
+ Control.Proxy.Prelude.Base: mapMD :: (Monad m, Proxy p) => (a -> m b) -> x -> p x a x b m r
- Control.Proxy.Prelude.Base: mapMU :: (Monad m) => (b' -> m a') -> b' -> Proxy a' x b' x m r
+ Control.Proxy.Prelude.Base: mapMU :: (Monad m, Proxy p) => (b' -> m a') -> b' -> p a' x b' x m r
- Control.Proxy.Prelude.Base: mapU :: (Monad m) => (b' -> a') -> b' -> Proxy a' x b' x m r
+ Control.Proxy.Prelude.Base: mapU :: (Monad m, Proxy p) => (b' -> a') -> b' -> p a' x b' x m r
- Control.Proxy.Prelude.Base: takeB :: (Monad m) => Int -> a' -> Proxy a' a a' a m a'
+ Control.Proxy.Prelude.Base: takeB :: (Monad m, Proxy p) => Int -> a' -> p a' a a' a m a'
- Control.Proxy.Prelude.Base: takeB_ :: (Monad m) => Int -> a' -> Proxy a' a a' a m ()
+ Control.Proxy.Prelude.Base: takeB_ :: (Monad m, Proxy p) => Int -> a' -> p a' a a' a m ()
- Control.Proxy.Prelude.Base: takeWhileD :: (Monad m) => (a -> Bool) -> a' -> Proxy a' a a' a m ()
+ Control.Proxy.Prelude.Base: takeWhileD :: (Monad m, Proxy p) => (a -> Bool) -> a' -> p a' a a' a m ()
- Control.Proxy.Prelude.Base: takeWhileU :: (Monad m) => (a' -> Bool) -> a' -> Proxy a' a a' a m ()
+ Control.Proxy.Prelude.Base: takeWhileU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' a a' a m ()
- Control.Proxy.Prelude.IO: getLineC :: y' -> Proxy String x y' y IO r
+ Control.Proxy.Prelude.IO: getLineC :: (Proxy p) => () -> CoProducer p String IO r
- Control.Proxy.Prelude.IO: getLineS :: y' -> Proxy x' x y' String IO r
+ Control.Proxy.Prelude.IO: getLineS :: (Proxy p) => () -> Producer p String IO r
- Control.Proxy.Prelude.IO: hPrintB :: (Show a, Show a') => Handle -> a' -> Proxy a' a a' a IO r
+ Control.Proxy.Prelude.IO: hPrintB :: (Show a, Show a', Proxy p) => Handle -> a' -> p a' a a' a IO r
- Control.Proxy.Prelude.IO: hPrintD :: (Show a) => Handle -> x -> Proxy x a x a IO r
+ Control.Proxy.Prelude.IO: hPrintD :: (Show a, Proxy p) => Handle -> x -> p x a x a IO r
- Control.Proxy.Prelude.IO: hPrintU :: (Show a') => Handle -> a' -> Proxy a' x a' x IO r
+ Control.Proxy.Prelude.IO: hPrintU :: (Show a', Proxy p) => Handle -> a' -> p a' x a' x IO r
- Control.Proxy.Prelude.IO: hPutStrLnB :: Handle -> String -> Proxy String String String String IO r
+ Control.Proxy.Prelude.IO: hPutStrLnB :: (Proxy p) => Handle -> String -> p String String String String IO r
- Control.Proxy.Prelude.IO: hPutStrLnD :: Handle -> x -> Proxy x String x String IO r
+ Control.Proxy.Prelude.IO: hPutStrLnD :: (Proxy p) => Handle -> x -> p x String x String IO r
- Control.Proxy.Prelude.IO: hPutStrLnU :: Handle -> String -> Proxy String x String x IO r
+ Control.Proxy.Prelude.IO: hPutStrLnU :: (Proxy p) => Handle -> String -> p String x String x IO r
- Control.Proxy.Prelude.IO: printB :: (Show a, Show a') => a' -> Proxy a' a a' a IO r
+ Control.Proxy.Prelude.IO: printB :: (Show a', Show a, Proxy p) => a' -> p a' a a' a IO r
- Control.Proxy.Prelude.IO: printD :: (Show a) => x -> Proxy x a x a IO r
+ Control.Proxy.Prelude.IO: printD :: (Show a, Proxy p) => x -> p x a x a IO r
- Control.Proxy.Prelude.IO: printU :: (Show a') => a' -> Proxy a' x a' x IO r
+ Control.Proxy.Prelude.IO: printU :: (Show a', Proxy p) => a' -> p a' x a' x IO r
- Control.Proxy.Prelude.IO: promptC :: y' -> Proxy String String y' y IO r
+ Control.Proxy.Prelude.IO: promptC :: (Proxy p) => () -> Client p String String IO r
- Control.Proxy.Prelude.IO: promptS :: String -> Proxy x' x String String IO r
+ Control.Proxy.Prelude.IO: promptS :: (Proxy p) => String -> Server p String String IO r
- Control.Proxy.Prelude.IO: putStrLnB :: String -> Proxy String String String String IO r
+ Control.Proxy.Prelude.IO: putStrLnB :: (Proxy p) => String -> p String String String String IO r
- Control.Proxy.Prelude.IO: putStrLnD :: x -> Proxy x String x String IO r
+ Control.Proxy.Prelude.IO: putStrLnD :: (Proxy p) => x -> p x String x String IO r
- Control.Proxy.Prelude.IO: putStrLnU :: String -> Proxy String x String x IO r
+ Control.Proxy.Prelude.IO: putStrLnU :: (Proxy p) => String -> p String x String x IO r
- Control.Proxy.Prelude.IO: readLnC :: (Read a) => y' -> Proxy a x y' y IO r
+ Control.Proxy.Prelude.IO: readLnC :: (Read a', Proxy p) => () -> CoProducer p a' IO r
- Control.Proxy.Prelude.IO: readLnS :: (Read a) => y' -> Proxy x' x y' a IO r
+ Control.Proxy.Prelude.IO: readLnS :: (Read b, Proxy p) => () -> Producer p b IO r
- Control.Proxy.Trans: class ProxyTrans t where liftP f = mapP (\ () -> f) () mapP = (liftP .)
+ Control.Proxy.Trans: class ProxyTrans t
- Control.Proxy.Trans: liftP :: (ProxyTrans t, Monad (p b c d e m), Channel p) => p b c d e m r -> t p b c d e m r
+ Control.Proxy.Trans: liftP :: (ProxyTrans t, Monad m, Proxy p) => p a' a b' b m r -> t p a' a b' b m r
- Control.Proxy.Trans: mapP :: (ProxyTrans t, Monad (p b c d e m), Channel p) => (a -> p b c d e m r) -> (a -> t p b c d e m r)
+ Control.Proxy.Trans: mapP :: (Monad m, Proxy p, ProxyTrans t) => (q -> p a' a b' b m r) -> (q -> t p a' a b' b m r)
- Control.Proxy.Trans.Either: catch :: (Monad (p a' a b' b m)) => EitherP e p a' a b' b m r -> (e -> EitherP f p a' a b' b m r) -> EitherP f p a' a b' b m r
+ Control.Proxy.Trans.Either: catch :: (Monad m, Proxy p) => EitherP e p a' a b' b m r -> (e -> EitherP f p a' a b' b m r) -> EitherP f p a' a b' b m r
- Control.Proxy.Trans.Either: handle :: (Monad (p a' a b' b m)) => (e -> EitherP f p a' a b' b m r) -> EitherP e p a' a b' b m r -> EitherP f p a' a b' b m r
+ Control.Proxy.Trans.Either: handle :: (Monad m, Proxy p) => (e -> EitherP f p a' a b' b m r) -> EitherP e p a' a b' b m r -> EitherP f p a' a b' b m r
- Control.Proxy.Trans.Either: left :: (Monad (p a' a b' b m)) => e -> EitherP e p a' a b' b m r
+ Control.Proxy.Trans.Either: left :: (Monad m, Proxy p) => e -> EitherP e p a' a b' b m r
- Control.Proxy.Trans.Either: right :: (Monad (p a' a b' b m)) => r -> EitherP e p a' a b' b m r
+ Control.Proxy.Trans.Either: right :: (Monad m, Proxy p) => r -> EitherP e p a' a b' b m r
- Control.Proxy.Trans.Either: throw :: (Monad (p a' a b' b m)) => e -> EitherP e p a' a b' b m r
+ Control.Proxy.Trans.Either: throw :: (Monad m, Proxy p) => e -> EitherP e p a' a b' b m r
- Control.Proxy.Trans.Maybe: just :: (Monad (p a' a b' b m)) => r -> MaybeP p a' a b' b m r
+ Control.Proxy.Trans.Maybe: just :: (Monad m, Proxy p) => r -> MaybeP p a' a b' b m r
- Control.Proxy.Trans.Maybe: nothing :: (Monad (p a' a b' b m)) => MaybeP p a' a b' b m r
+ Control.Proxy.Trans.Maybe: nothing :: (Monad m, Proxy p) => MaybeP p a' a b' b m r
- Control.Proxy.Trans.Reader: ask :: (Monad (p a' a b' b m)) => ReaderP i p a' a b' b m i
+ Control.Proxy.Trans.Reader: ask :: (Proxy p, Monad m) => ReaderP i p a' a b' b m i
- Control.Proxy.Trans.Reader: asks :: (Monad (p a' a b' b m)) => (i -> r) -> ReaderP i p a' a b' b m r
+ Control.Proxy.Trans.Reader: asks :: (Proxy p, Monad m) => (i -> r) -> ReaderP i p a' a b' b m r
- Control.Proxy.Trans.Reader: local :: (Monad (p a' a b' b m)) => (i -> i) -> ReaderP i p a' a b' b m r -> ReaderP i p a' a b' b m r
+ Control.Proxy.Trans.Reader: local :: (i -> i) -> ReaderP i p a' a b' b m r -> ReaderP i p a' a b' b m r
- Control.Proxy.Trans.Reader: withReaderP :: (Monad (p a' a b' b m)) => (j -> i) -> ReaderP i p a' a b' b m r -> ReaderP j p a' a b' b m r
+ Control.Proxy.Trans.Reader: withReaderP :: (j -> i) -> ReaderP i p a' a b' b m r -> ReaderP j p a' a b' b m r
- Control.Proxy.Trans.State: evalStateK :: (Monad (p a' a b' b m)) => s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m r)
+ Control.Proxy.Trans.State: evalStateK :: (Proxy p, Monad m) => s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m r)
- Control.Proxy.Trans.State: evalStateP :: (Monad (p a' a b' b m)) => s -> StateP s p a' a b' b m r -> p a' a b' b m r
+ Control.Proxy.Trans.State: evalStateP :: (Proxy p, Monad m) => s -> StateP s p a' a b' b m r -> p a' a b' b m r
- Control.Proxy.Trans.State: execStateK :: (Monad (p a' a b' b m)) => s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m s)
+ Control.Proxy.Trans.State: execStateK :: (Proxy p, Monad m) => s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m s)
- Control.Proxy.Trans.State: execStateP :: (Monad (p a' a b' b m)) => s -> StateP s p a' a b' b m r -> p a' a b' b m s
+ Control.Proxy.Trans.State: execStateP :: (Proxy p, Monad m) => s -> StateP s p a' a b' b m r -> p a' a b' b m s
- Control.Proxy.Trans.State: get :: (Monad (p a' a b' b m)) => StateP s p a' a b' b m s
+ Control.Proxy.Trans.State: get :: (Proxy p, Monad m) => StateP s p a' a b' b m s
- Control.Proxy.Trans.State: gets :: (Monad (p a' a b' b m)) => (s -> r) -> StateP s p a' a b' b m r
+ Control.Proxy.Trans.State: gets :: (Proxy p, Monad m) => (s -> r) -> StateP s p a' a b' b m r
- Control.Proxy.Trans.State: modify :: (Monad (p a' a b' b m)) => (s -> s) -> StateP s p a' a b' b m ()
+ Control.Proxy.Trans.State: modify :: (Proxy p, Monad m) => (s -> s) -> StateP s p a' a b' b m ()
- Control.Proxy.Trans.State: put :: (Monad (p a' a b' b m)) => s -> StateP s p a' a b' b m ()
+ Control.Proxy.Trans.State: put :: (Proxy p, Monad m) => s -> StateP s p a' a b' b m ()
- Control.Proxy.Trans.Writer: censor :: (Monad (p a' a b' b m), Monoid w) => (w -> w) -> WriterP w p a' a b' b m r -> WriterP w p a' a b' b m r
+ Control.Proxy.Trans.Writer: censor :: (Proxy p, Monad m, Monoid w) => (w -> w) -> WriterP w p a' a b' b m r -> WriterP w p a' a b' b m r
- Control.Proxy.Trans.Writer: execWriterK :: (Monad (p a' a b' b m), Monoid w) => (q -> WriterP w p a' a b' b m r) -> (q -> p a' a b' b m w)
+ Control.Proxy.Trans.Writer: execWriterK :: (Proxy p, Monad m, Monoid w) => (q -> WriterP w p a' a b' b m r) -> (q -> p a' a b' b m w)
- Control.Proxy.Trans.Writer: execWriterP :: (Monad (p a' a b' b m), Monoid w) => WriterP w p a' a b' b m r -> p a' a b' b m w
+ Control.Proxy.Trans.Writer: execWriterP :: (Proxy p, Monad m, Monoid w) => WriterP w p a' a b' b m r -> p a' a b' b m w
- Control.Proxy.Trans.Writer: tell :: (Monad (p a' a b' b m), Monoid w) => w -> WriterP w p a' a b' b m ()
+ Control.Proxy.Trans.Writer: tell :: (Proxy p, Monad m, Monoid w) => w -> WriterP w p a' a b' b m ()
Files
- Control/Frame.hs +0/−473
- Control/Frame/Tutorial.hs +0/−487
- Control/IMonad/Trans/Free.hs +0/−56
- Control/MFunctor.hs +45/−3
- Control/PFunctor.hs +32/−0
- Control/Pipe.hs +195/−9
- Control/Pipe/Core.hs +0/−230
- Control/Pipe/Tutorial.hs +0/−539
- Control/Proxy.hs +21/−18
- Control/Proxy/Class.hs +394/−84
- Control/Proxy/Core.hs +31/−203
- Control/Proxy/Core/Correct.hs +186/−0
- Control/Proxy/Core/Fast.hs +238/−0
- Control/Proxy/Pipe.hs +165/−58
- Control/Proxy/Prelude.hs +2/−2
- Control/Proxy/Prelude/Base.hs +598/−198
- Control/Proxy/Prelude/IO.hs +81/−68
- Control/Proxy/Prelude/Kleisli.hs +51/−17
- Control/Proxy/Synonym.hs +66/−0
- Control/Proxy/Trans.hs +52/−19
- Control/Proxy/Trans/Either.hs +102/−49
- Control/Proxy/Trans/Identity.hs +105/−39
- Control/Proxy/Trans/Maybe.hs +95/−38
- Control/Proxy/Trans/Reader.hs +98/−50
- Control/Proxy/Trans/State.hs +99/−51
- Control/Proxy/Trans/Tutorial.hs +0/−415
- Control/Proxy/Trans/Writer.hs +89/−46
- Control/Proxy/Tutorial.hs +1889/−549
- Data/Closed.hs +0/−9
- pipes.cabal +22/−33
− Control/Frame.hs
@@ -1,473 +0,0 @@-{-|- 'Frame's extend 'Pipe's with:-- * The ability to fold input-- * Prompt and deterministic finalization-- 'Frame's differ from 'Pipe's because they form restricted monads rather than- forming ordinary monads. This means you must rebind @do@ notation to use- restricted monads from the @index-core@ package. See the \"Create Frames\"- section for details. For even more details, consult the @index-core@- package.--}--{-# LANGUAGE GADTs, TypeOperators #-}--module Control.Frame (- -- * Types- -- $types- C,- O,- M,- FrameF(..),- Frame,- Stack,- -- * Create Frames- -- $create-- -- ** Primitives- -- $primitives- yieldF,- awaitF,- close,- -- ** Pipe-like primitives- -- $pipeprims- yield,- await,- -- * Finalize Frames- -- $finalization- catchD,- catchF,- finallyD,- finallyF,- -- * Compose Frames- -- $compose- (<-<),- (>->),- idF,- FrameC(..),- -- * Run Frames- -- $run- runFrame- ) where--import Control.Category-import Control.IMonad-import Control.IMonad.Trans-import Control.IMonad.Trans.Free-import Control.Monad.Instances ()-import Data.Closed (C)-import Data.Maybe-import Prelude hiding ((.), id)---- For documentation-import Control.Pipe hiding (await, yield, Await, Yield)--{- $types- The first step to convert 'Pipe' code to 'Frame' code is to translate the- types. All types of the form \"@Pipe a b m r@\" become- \"@Frame b m (M a) C r@\". For example, given the following type signatures- from the tutorial:--> printer :: (Show a) => Pipe b C IO r-> take' :: Int -> Pipe b b IO ()-> fromList :: (Monad m) => [b] -> Pipe () b m ()-- ... you would replace them with:--> printer :: (Show a) => Frame C IO (M a) C r-> take' :: Int -> Frame a IO (M a) C ()-> fromList :: (Monad m) => [a] -> Frame a m (M ()) C ()-> -- To use the finalization example, change fromList's base monad to 'IO'-> fromList :: [a] -> Frame a IO (M ()) C ()--}---- | Index representing an open input end, receiving values of type @a@-data O a = O -- Not exported---- | Index representing an open input end, receiving values of type @Maybe a@-type M a = O (Maybe a)--{-|- Base functor for a pipe that can close its input end-- * @b@ - Output type-- * @x@ - Next step-- * @i@ - Current step's index--}-data FrameF b x i where- Yield :: b -> x i -> FrameF b x i- Await :: (a -> x (O a)) -> FrameF b x (O a)- Close :: x C -> FrameF b x (O a)--instance IFunctor (FrameF b) where- fmapI f p = case p of- Yield b y -> Yield b (f y)- Await a -> Await (f . a)- Close c -> Close (f c)--{-|- A 'Frame' is like a 'Pipe' with an indexed input end:-- * @b@ - The type of the 'Frame's output-- * @m@ - The base monad-- * @i@ - The initial index of the input end ('O'pen or 'C'losed)-- * @j@ - The final index of the input end ('O'pen or 'C'losed)-- * @r@ - The return value--}-type Frame b m i j r = IFreeT (FrameF (m (), b)) (U m) (r := j) i---- | A self-contained 'Frame' that is ready to be run-type Stack m r = Frame C m (M ()) C r---- $create--- The second step to convert 'Pipe' code to 'Frame' code is to change your--- module header to:------ > {-# LANGUAGE RebindableSyntax #-}--- >--- > import Control.IMonad.Do--- > import Control.Frame--- > import Prelude hiding (Monad(..))------ "Control.Frame" replaces all 'Pipe' 'await's and 'yield's with their--- corresponding 'Frame' counterparts. @Control.IMonad.Do@ rebinds @do@--- notation to work with restricted monads, which also requires using the--- @RebindableSyntax@ extension and hiding the 'Monad' class from the @Prelude@.------ You also must use the restricted monad utility functions, which have the--- same name as their ordinary monad counterparts except with an \'@R@\' suffix,--- such as 'foreverR' instead of 'forever'. Finally, you must use 'liftU'--- instead of 'lift' to invoke operations in the base monad.------ Finally, every terminating 'Frame' must be 'close'd exactly once before being--- passed to composition.------ > printer = foreverR $ do--- > a <- await--- > liftU $ print a--- >--- > take' n = do--- > replicateMR_ n $ do--- > a <- await--- > yield a--- > close--- > liftU $ putStrLn "You shall not pass!"--- >--- > fromList xs = do--- > close--- > mapMR_ yield xs--{- $primitives- 'yieldF' guards against downstream termination by yielding the most- up-to-date finalization alongside each value, so that downstream can call- that finalizer if it terminates before requesting another value.-- 'awaitF' intercepts upstream termination by returning a 'Nothing' if- upstream terminates before providing a value. Further attempts to request- input from upstream will terminate the current 'Frame' using the- return value provided from upstream.-- While 'awaitF' is useful for folds, 'yieldF' is less useful for end-users of- this library and the higher-order 'catchF' / 'finallyF' finalization- functions are much more user-friendly.-- Composing two 'Frame's requires that each 'Frame' invokes 'close' exactly- once. Anything else will not type-check. Leave out the 'close' statement- when writing library components and let the person assembling the components- for composition specify where the 'close' goes.-- The earlier you 'close' the upstream 'Frame', the earlier it is finalized.- However, once you 'close' it you may no longer 'await'.--}---- | 'Yield' the most current finalizer for this 'Frame' alongside the value-yieldF :: (Monad m) => m () -> b -> Frame b m i i ()-yieldF m x = liftF $ Yield (m, x) (V ())---- | 'Await' a value from upstream, returning 'Nothing' if upstream terminates-awaitF :: (Monad m) => Frame b m (M a) (M a) (Maybe a)-awaitF = liftF $ Await V---- | 'Close' the input end, calling the finalizers of every upstream 'Frame'-close :: (Monad m) => Frame b m (M a) C ()-close = liftF $ Close (V ())--{- $pipeprims- The following 'Pipe'-like primitives are built on top of the 'Frame'- primitives. They behave identically to their 'Pipe' counterparts and can- be used as drop-in replacements for them.--}---- | 'yield' a value upstream alongside an empty finalizer-yield :: (Monad m) => b -> Frame b m i i ()-yield = yieldF (return ())---- | 'await' a value from upstream and terminate if upstream terminates-await :: (Monad m) => Frame b m (M a) (M a) a-await = awaitF !>= maybe await returnR--{- $finalization- The third (and optional) step to convert 'Pipe' code to 'Frame' code is to- register finalizers for your 'Frame'. These finalizers may be arbitrarily- nested:--> printer = foreverR $ catchF (putStrLn "printer interrupted") $ do-> a <- await-> liftU $ print a->-> take' n = finallyF (putStrLn "You shall not pass!") $ do-> replicateMR_ n $ do-> a <- catchF (putStrLn "take' interrupted") await-> yield a-> close->-> fromList xs = catchF (putStrLn "fromList interrupted") $ do-> close-> mapMR_ yield xs-- These convenience functions register block-level finalizers to be called if- another 'Frame' terminates first. The naming conventions are:-- * \"catch\" functions (i.e. 'catchD' / 'catchF') call the finalizer only if- another 'Frame' terminates before the block completes, but will not call- the finalizer if the block terminates normally.-- * \"finally\" functions (i.e. 'finallyD' / 'finallyF') are like \"catch\"- functions except that they also call the finalizer if the block terminates- normally.-- * Functions that end in a \'@D@\' suffix (i.e. 'catchD' / 'finallyD') only- guard against downstream termination.-- * Functions that end in a \'@F@\' suffix (i.e. 'catchF' / 'finallyF') guard- against termination in both directions. You usually want these ones.-- Note that finalization blocks that /begin/ after the 'close' statement may- only use the \'@D@\'-suffixed version as upstream has been closed off. This- is a consequence of a deficiency in Haskell's type system that will take- time to work around. However an \'@F@\'-suffixed block that begins before a- 'close' statement may continue through it normally. So, for code blocks- after a 'close' statement, use 'catchD' \/ 'finallyD', otherwise use- 'catchF' \/ 'finallyF'. In future releases, the \'@D@\'-suffixed versions- will be removed and merged into the \'@F@\'-suffixed versions.--}--{-|- @catchD m p@ calls the finalizer @m@ if a downstream 'Frame' terminates- before @p@ finishes.--}-catchD :: (Monad m) => m () -> Frame b m i j r -> Frame b m i j r-catchD m p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Return r -> returnI r- Wrap (Close p') -> wrap $ Close (catchD m p')- Wrap (Yield (m', b) p') -> wrap $ Yield (m' >> m, b) (catchD m p')- Wrap (Await f ) -> wrap $ Await $ fmap (catchD m) f--{-|- @catchF m p@ calls the finalizer @m@ if any 'Frame' terminates before @p@- finishes.--}-catchF :: (Monad m) => m () -> Frame b m (M a) j r -> Frame b m (M a) j r-catchF m p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Return r -> returnI r- Wrap (Close p') -> wrap $ Close $ catchD m p'- Wrap (Yield (m', b) p') -> wrap $ Yield (m' >> m, b) (catchF m p')- Wrap (Await f ) -> wrap $ Await $ \e -> case e of- Nothing -> liftU m !> catchF m (f e)- Just _ -> catchF m (f e)--{-|- @finallyD m p@ calls the finalizer @m@ if a downstream 'Frame' terminates- before @p@ finishes or if @p@ completes normally.--}-finallyD :: (Monad m) => m () -> Frame b m i j r -> Frame b m i j r-finallyD m p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Return r -> liftU m !> returnI r- Wrap (Close p') -> wrap $ Close (finallyD m p')- Wrap (Yield (m', b) p') -> wrap $ Yield (m' >> m, b) (finallyD m p')- Wrap (Await f ) -> wrap $ Await $ fmap (finallyD m) f--{-|- @finallyF m p@ calls the finalizer @m@ if any 'Frame' terminates before @p@- finishes or if @p@ completes normally.--}-finallyF :: (Monad m) => m () -> Frame b m (M a) j r -> Frame b m (M a) j r-finallyF m p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Return r -> liftU m !> returnI r- Wrap (Close p') -> wrap $ Close $ finallyD m p'- Wrap (Yield (m', b) p') -> wrap $ Yield (m' >> m, b) (finallyF m p')- Wrap (Await f ) -> wrap $ Await $ \e -> case e of- Nothing -> liftU m !> finallyF m (f e)- Just _ -> finallyF m (f e)--(<~<) :: (Monad m)- => IFreeT (FrameF c) (U m) (r := C) (O b)- -> IFreeT (FrameF b) (U m) (r := C) (O a)- -> IFreeT (FrameF c) (U m) (r := C) (O a)-p1 <~< p2 = IFreeT $ U $ do- x1 <- unU $ runIFreeT p1- unU $ runIFreeT $ case x1 of- Wrap (Close p1') -> wrap $ Close p1'- Wrap (Yield c p1') -> wrap $ Yield c (p1' <~< p2)- Wrap (Await f1 ) -> IFreeT $ U $ do- x2 <- unU $ runIFreeT p2- let p1' = IFreeT $ returnI x1- unU $ runIFreeT $ case x2 of- Wrap (Close p2') -> wrap $ Close $ p1' <~| p2'- Wrap (Yield b p2') -> f1 b <~< p2'- Wrap (Await f2) -> wrap $ Await $ fmap (\p2'-> p1' <~< p2') f2--(<~|) :: (Monad m)- => IFreeT (FrameF c) (U m) (r := C) (O b)- -> IFreeT (FrameF b) (U m) (r := C) C- -> IFreeT (FrameF c) (U m) (r := C) C-p1 <~| p2 = IFreeT $ U $ do- x1 <- unU $ runIFreeT p1- unU $ runIFreeT $ case x1 of- Wrap (Close p1') -> p1'- Wrap (Yield c p1') -> wrap $ Yield c (p1' <~| p2)- Wrap (Await f1 ) -> IFreeT $ U $ do- x2 <- unU $ runIFreeT p2- unU $ runIFreeT $ case x2 of- Return r -> returnI r- Wrap (Yield b p2') -> f1 b <~| p2' --heap :: (Monad m)- => m ()- -> IFreeT (FrameF (m (), c)) (U m) (r := C) (M b )- -> IFreeT (FrameF (m (), c)) (U m) (r := C) (M (m (), b))-heap m p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Wrap (Close p') -> wrap $ Close $ liftU m !> p'- Wrap (Yield (m', c) p') -> wrap $ Yield (m >> m', c) (heap m p')- Wrap (Await f ) -> wrap $ Await $ \e -> case e of- Nothing -> heap (return ()) (f Nothing)- Just (m', b) -> heap m' (f $ Just b)--stack :: (Monad m)- => Bool- -> IFreeT (FrameF b ) (U m) (r := C) (M a)- -> IFreeT (FrameF (Maybe b)) (U m) (r := C) (M a)-stack t p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Wrap (Close p') -> wrap $ Close $ warn p'- Wrap (Yield b p') -> wrap $ Yield (Just b) (stack t p')- Wrap (Await f ) ->- let p' = wrap $ Await $ \e -> stack (isNothing e) (f e)- in case t of- False -> p'- True -> wrap $ Yield Nothing p'--warn :: (Monad m)- => IFreeT (FrameF b ) (U m) (r := C) C- -> IFreeT (FrameF (Maybe b)) (U m) (r := C) C-warn p = IFreeT $ U $ do- x <- unU $ runIFreeT p- unU $ runIFreeT $ case x of- Return r -> wrap $ Yield Nothing (returnI r)- Wrap (Yield b p') -> wrap $ Yield (Just b) (warn p')--{- $compose- The fourth step to convert 'Pipe' code to 'Frame' code is to replace ('<+<')- with ('<-<'):--> printer <-< take' 3 <-< fromList [1..]-- Like 'Pipe's, Frames form a 'Category' where composition pipes the output- from the upstream 'Frame' to the input of the downstream 'Frame'.- Additionally, composition guarantees the following behaviors:-- * 'Frame's receive exactly one 'Nothing' if an upstream 'Frame' terminates.-- * Registered finalizers get called exactly once if a downstream 'Frame'- terminates.-- * Finalizers are always ordered from upstream to downstream.-- The 'Category' laws cannot be broken, so you don't have to be careful when- using 'Frame's.-- Note that you may only compose 'Frame's that begin open and end closed.--}---- | Corresponds to ('<<<')/('.') from @Control.Category@-(<-<) :: Monad m- => Frame c m (M b) C r -> Frame b m (M a) C r -> Frame c m (M a) C r-p1 <-< p2 = heap (return ()) p1 <~< stack False p2---- | Corresponds to ('>>>') from @Control.Category@-(>->) :: Monad m- => Frame b m (M a) C r -> Frame c m (M b) C r -> Frame c m (M a) C r-(>->) = flip (<-<)--infixr 9 <-<-infixr 9 >->---- | Corresponds to 'id' from @Control.Category@-idF :: (Monad m) => Frame a m (M a) C r-idF = foreverR $ await !>= yield---- | 'Frame's form a 'Category' instance when you rearrange the type variables-newtype FrameC m r a b = FrameC { unFrameC :: Frame b m (M a) C r }--instance (Monad m) => Category (FrameC m r) where- id = FrameC idF- (FrameC p1) . (FrameC p2) = FrameC (p1 <-< p2)--{- $run- The fifth step to convert 'Pipe' code to 'Frame' code is to use 'runFrame'- instead of 'runPipe':-->>> runFrame $ printer <-< take' 3 <-< fromList [1..]-1-2-3-fromList interrupted-You shall not pass!-printer interrupted->>> runFrame $ printer <-< take' 3 <-< fromList [1]-1-You shall not pass!-take' interrupted-printer interrupted---}--{-|- Run the 'Frame' monad transformer, converting it back to the base monad.-- 'runFrame' is the 'Frame' equivalent to 'runPipe' and requires a- self-contained 'Stack'.--}-runFrame :: (Monad m) => Stack m r -> m r-runFrame p = do- x <- unU $ runIFreeT p- case x of- Wrap (Close p') -> runFrame' p'- Wrap (Yield _ p') -> runFrame p'- Wrap (Await f ) -> runFrame (f $ Just ())--runFrame' :: (Monad m) => Frame C m C C r -> m r-runFrame' p = do- x <- unU $ runIFreeT p- case x of- Return (V r) -> return r- Wrap (Yield _ p') -> runFrame' p'
− Control/Frame/Tutorial.hs
@@ -1,487 +0,0 @@-{-|- This module provides the tutorial for "Control.Frame".--}--module Control.Frame.Tutorial (- -- * Restricted Monads- -- $restrict1-- -- $extension-- -- $restrict2-- -- * Type Signatures- -- $types-- -- * Prompt Finalization- -- $prompt-- -- * Composition- -- $compose-- -- * Finalization- -- $ensure-- -- * Folds- -- $fold-- -- * Strictness- -- $strict-- -- * Robustness- -- $robust- ) where---- For documentation-import Control.Category-import Control.Frame-import Control.IMonad-import Control.IMonad.Trans-import Control.Monad.Trans.Class-import Control.Pipe hiding (await, yield, Await, Yield)--{- $restrict1- 'Frame's extend 'Pipe's with two new features:-- * Folding input and intercepting upstream termination-- * Guaranteeing prompt and deterministic finalization-- However, these extra features comes with some added complexity: restricted- monads, also known as indexed monads. Restricted monads sound scarier than- they are, so I'll demonstrate that if you are comfortable using monads, then- you'll be comfortable using restricted monads.-- Let's translate the @take'@ function from the 'Pipe's tutorial into a- 'Frame' to see what changes when we use restricted monads:---}--- $extension--- > {-# LANGUAGE RebindableSyntax #-}--- >--- > import Control.Frame--- > import Control.IMonad.Do--- > import Control.IMonad.Trans--- > import Prelude hiding (Monad(..))--- >--- > take' :: Int -> Frame a IO (M a) C ()--- > take' n = do--- > replicateMR_ n $ do--- > x <- await--- > yield x--- > close--- > liftU $ putStrLn "You shall not pass!"-{- $restrict2- This time I included all imports and highlighted the new @RebindableSyntax@- extension. The new imports belong to the @Control.IMonad@ hierarchy from- the @index-core@ package, which provides the core restricted monad- functionality.-- Yet, you almost wouldn't even know you were using an restricted monad just- by looking at the code. This is because @index-core@ can rebind @do@- notation to use restricted monads instead of ordinary extensions. Three- things make this possible:-- * The @RebindableSyntax@ extension, which allows libraries to override- @do@ syntax (among other things)-- * The @Control.IMonad.Do@ module which exports the new bindings for @do@- notation-- * Hiding 'Monad' from the Prelude so that it does not conflict with the- bindings from @index-core@-- However, you are not obligated to rebind @do@ notation to use 'Frame's. You- can choose to keep ordinary @do@ notation and desugar the restricted monad- by hand. Just import @Control.IMonad@ instead, drop the @RebindableSyntax@- extension, and don't hide 'Monad'. Then you can desugar @take'@ manually- using the restricted monad operators:--> import Control.Frame-> import Control.IMonad-> import Control.IMonad.Trans->-> take' :: Int -> Frame a IO (M a) C ()-> take' n =-> (replicateMR_ n $-> await !>= \x -> -> yield x) !>= \_ ->-> close !>= \_ ->-> liftU $ putStrLn "You shall not pass!"-- However, for this tutorial I will use the @do@ notation, since it's prettier- and easier to use.-- You'll also notice functions that resemble the ones in @Control.Monad@,- except with an \'@R@\' suffix on the end of them, like 'replicateMR_'.- Most functions in @Control.Monad@ have a restricted counterpart provided by- @Control.IMonad.Restrict@ (which is in turn re-exported by- @Control.IMonad@), such as 'whenR', 'foreverR', and 'mapMR'.-- Also, every time you lift an operation from the base monad, you must use- 'liftU' instead of 'lift'. 'Frame's are \"restricted monad transformers\",- and they would normally lift a base restricted monad using 'liftI', but- they can also lift ordinary monads, too, using 'liftU' (mnemonic: \"lift\"- an ordinary monad and \'U\'pgrade it to a restricted monad).--}--{- $types- The 'Frame' type constructor also looks a bit different, too:--> Frame a IO (M a) C ()-- Let's dissect that to understand how 'Frame's work:--> | Output | Base monad | Initial Input | Final Input | Return Value-> Frame a IO (M a) C ()-- 'Frame's differ from 'Pipe's in that their input end indexes the beginning- and end of the operation. Our @take'@ function starts off with an open- input end (@M a@), and ends with a closed input end (@C@).-- @take'@ finishes with a closed input end because it called the 'close'- function, which seals off and finalizes upstream. You can see that the- 'close' primitive changes the index just by looking at its type:--> close :: Monad m => Frame b m (M a) C ()-- The 'close' instruction begins with an open input end (@M a@) and finishes- with a closed input end (@C@). If you tried to call 'close' twice, you'd- get a type error:--> -- wrong!-> do close-> close-- This prevents you from accidentally finalizing upstream twice.-- 'close' is the only primitive that changes the index, and there is no way to- reopen the input once you have closed it. 'close' also forbids you from- 'await'ing input from upstream after you have already closed it. If you- try, you will get a type error--> -- wrong!-> do close-> await-- This prevents you from requesting input from a finalized pipe. In fact,- once you 'close' your input end, every upstream 'Frame' disappears- completely. You couldn't get input from upstream anyway, even if you- somehow allowed 'await' statements after 'close'.-- You can check out 'await''s type signature to see why it won't type-check- after 'close':--> await :: Monad m => Frame b m (M a) (M a) a-- 'await' must begin with the input end open (@M a@) and it leaves the input- end open when done (@M a@). However, you can still use a 'yield' anywhere:--> yield :: Monad m => b -> Frame b m i i ()-- 'yield' will work whether or not the input end is open, and it leaves the- input end in the same state once 'yield' is done.--}--{- $prompt- Every 'Frame' must close its input end /exactly/ one time before you can- compose it with other 'Frame's. The only exception is if a 'Frame' never- terminates:--> -- This type-checks because foreverR is polymorphic in the final index-> printer :: (Show b) => Frame C IO (M b) C r-> printer = foreverR $ do-> a <- await-> liftU $ print a-- However, when a 'Frame' no longer needs input then you should 'close' it as- early as possible. The earlier you 'close' upstream, the more promptly- upstream gets finalized.-- If you write a stand-alone producer from start to finish, you can be sure it- will never need upstream, so you can close it immediately:--> -- I'm keeping fromList's input end polymorphic for a later example-> fromList :: (M.Monad m) => [b] -> Frame b m (M a) C ()-> fromList xs = do-> close-> mapMR_ yield xs-- However, if @fromList@ were a library function, you would remove the 'close'- statement as you cannot guarantee that your user won't want to 'await' after- @fromList@. Or, the user might want to call @fromList@ twice within the- same 'Frame', and having two close statements would lead to a type error.- Therefore, a good rule of thumb when writing library code for 'Frame's is to- always let the user decide when to 'close' the 'Frame' unless you are- writing a stand-alone 'Frame'.-- So for right now, I will leave the 'close' in @fromList@ for simplicity and- treat it as a stand-alone 'Frame'. Also, it will come in handy for a later- example.--}--{- $compose- Composition works just like 'Pipe's, except you use the ('<-<') composition- operator instead of ('<+<'):--> stack :: Stack IO ()-> stack = printer <-< take' 3 <-< fromList [1..]-- The 'Frame' equivalent to 'Pipeline' is a 'Stack' (mnemonic: call stack;- also the name 'Frame' refers to a call stack frame):--> type Stack m r = Frame C m (M ()) C r-- Similarly, you use 'runFrame' instead of 'runPipe' to convert the 'Frame'- back to the base monad:-->>> runFrame stack-1-2-3-You shall not pass!-- However, let's carefully inspect the type of composition:--> (<-<) :: Monad m-> => Frame c m (M b) C r-> -> Frame b m (M a) C r-> -> Frame c m (M a) C r-- Each argument 'Frame' must begin in an open state and end in a closed state.- This means that each 'Frame' in a 'Stack' must call 'close' exactly once- before it may be used. 'runFrame' has the exact same restriction:--> runFrame :: Monad m => Stack m r -> m r-> runFrame ~ Monad m => Frame C m (M ()) C r -> m r-- Composition specifically requires the user to define when to finalize- upstream and does not assume this occurs at the end of the 'Frame'. This- doesn't pose a problem for stand-alone 'Frame's, since they will know when- they no longer need input, but smaller library components designed to be- assembled into larger 'Frame's should let the user decide at the very last- moment where to 'close' the 'Pipe'. There is no way to know ahead of time- where the 'close' should be until the complete 'Frame' has been assembled.--}--{- $ensure- With 'Frame's in hand, we can now write a safe @read'@ function:--> readFile' :: Handle -> Frame Text IO C C ()-> readFile' h = do-> eof <- liftU $ hIsEOF h-> whenR (not eof) $ do-> s <- liftU $ hGetLine h-> yield s-> readFile' h-> -> read' :: FilePath -> Frame Text IO C C ()-> read' file = do-> liftU $ putStrLn "Opening file..."-> h <- liftU $ openFile file ReadMode-> -- The following requires "import qualified Control.Monad as M"-> finallyD (putStrLn "Closing file ..." M.>> hClose h) $ readFile' h-- The 'finallyD' function registers a block-level finalizer that executes if a- downstream 'Pipe' terminates or if the block completes normally. The more- general 'finallyF' function will call the finalizer if /any/ 'Frame'- terminates.-- Usually you would always want to use 'finallyF', but because of some type- limitations you can only use 'finallyD' after a 'Frame' is closed. A future- release of this library will fix this and merge 'finallyD' into 'finallyF'.- So that means that for everything beginning before a 'close' statement, use- 'finallyF', otherwise use 'finallyD'.-- Similarly, you can use the 'catchF' / 'catchD' counterparts to the- \"finally\" functions. The \"catch\" functions run the finalizer only if- another 'Frame' terminates before the block is done, but not if the block- terminates normally.-- We don't 'close' the @read'@ function because it's not a stand-alone- 'Frame'. We want to be able to concatenate multiple @read'@s together- within the same 'Frame', like so:--> files = do-> close-> read' "file1.txt"-> read' "file2.txt"-- So let's assume those two files have the following contents:-- \"@file1.txt@\"--> Line 1-> Line 2-> Line 3-- \"@file2.txt@\"--> A-> B-> C-- We can now check to see if our @files@ producer works:-->>> runFrame $ printer <-< files-Opening file...-"Line1"-"Line2"-"Line3"-Closing file ...-Opening file...-"A"-"B"-"C"-Closing file ...-- More importantly, files are never opened if they aren't demanded and they- are always properly finalized if the consumer terminates early:-->>> runFrame $ printer <-< take' 2 <-< files-Opening file...-"Line1"-"Line2"-Closing file ...-You shall not pass!-- So we get lazy, deterministic, and prompt resource management. Nice!---}--{- $fold- 'Frame's can actually do more than just manage finalization! Using- 'Frame's, we can now correctly implement folds like @toList@ in a way that- is truly compositional:--> toList :: (M.Monad m) => Frame b m (M a) (M a) [a]-> toList = do-> a' <- awaitF-> case a' of-> Nothing -> return []-> Just a -> do-> as <- toList-> return (a:as)-- We used one new function this time: 'awaitF'. This is like 'await' except- that it returns a 'Nothing' if upstream terminates before 'yield'ing back a- value. This allows you to intercept upstream termination and do some- cleanup, and in our case we use it to end the fold.-- You only receive a 'Nothing' once when you use 'awaitF'. Any attempt to- request more input after you receive the first 'Nothing' will terminate the- current 'Frame' using the upstream return value. In fact, 'await' is built- on top of 'awaitF':--> await = do-> a' <- awaitF-> case a' of-> Nothing -> await-> Just a -> return a-- If it gets a 'Nothing', it just ignores it and 'await's again, choosing to- not do any cleanup.-- Now let's make sure our @toList@ function works. I didn't make @toList@ a- stand-alone 'Frame', so we will have to include a 'close' statement to- complete it before composing it:--> p1 = do-> xs <- toList-> close-> return (Just xs)->-> p2 xs = do-> fromList xs-> return Nothing -- Remember: they need the same return type-->>> runFrame $ p1 <-< p2 [1..10]-Just [1,2,3,4,5,6,7,8,9,10]--}--{- $strict- Lazy resource management has one important disadvantage: we can't free the- resource until downstream no longer needs input. Many libraries duplicate- their code to provide Lazy and Strict versions, allowing the user to decide- if they want:-- * Lazy input, which conserves memory, but holds onto the resource until- downstream is done processing it-- * Strict input, which loads everything into memory, but can then immediately- dispose of the resource before the input is processed-- What if there were a way to seamlessly switch between those semantics or- even choose something in between? Well, it turns out we can!-- First, we can combine @fromList@ and @toList@ into something even cooler:--> strict :: (M.Monad m) => Frame a m (M a) C ()-> strict = do-> xs <- toList-> fromList xs-- As the name suggests, the @strict@ function is strict in its input.- @strict@ loads the entire input into memory, finalizes upstream, then- proceeds to hand the input off to downstream. We can prove this just by- using it:-->>> runFrame $ printer <-< strict <-< files-> Opening file...-> Closing file ...-> Opening file...-> Closing file ...-> "Line1"-> "Line2"-> "Line3"-> "A"-> "B"-> "C"-- Both files were disposed of immediately, at the expense of using more- memory.-- But what if we want something in between strictness and laziness? Maybe - something like this:-->>> runFrame $ printer <-< strict <-< take' 2 <-< files-Opening file...-Closing file ...-You shall not pass!-"Line1"-"Line2"-- Now we have the best of both worlds. We can pick and choose how much of- our source to strictly load into memory. In the above example, we specified- that we wanted to be strict only in the first two lines of our input, and as- a result the third line of \"@file1.txt@\" is never read and \"@file2.txt@\"- is never even opened!-- Now we have a way to seamlessly slide anywhere on the spectrum between- laziness and strictness, and it's all implemented entirely within Haskell- in a way that is elegant and intuitive without the use of artificial and- clumsy 'seq' annotations.--}--{- $robust- The 'Frame' implementation exposes all internals, yet this does not- compromise safety or invariants in any way. The library's implementation is- \"correct-by-construction\", meaning that you can extend it with your own- features if you so choose, and you never have to worry about accidentally- breaking any laws, such as the associativity of composition.-- This has the following important practical benefits for finalization and- folds:-- * Finalizers never get duplicated or dropped-- * Folds can be performed anywhere within the 'Stack', not just at the most- downstream 'Frame', as the @strict@ example illustrates.-- * You can reason about each 'Frame's finalization behavior completely- independently of other 'Frame's.-- Composition elegantly handles every single corner case. This directly- follows from strictly enforcing the 'Category' laws, because categories have- no corners!--}
− Control/IMonad/Trans/Free.hs
@@ -1,56 +0,0 @@--- | This module is the indexed version of "Control.Monad.Trans.Free"--{-# LANGUAGE KindSignatures, TypeOperators #-}--module Control.IMonad.Trans.Free (- -- * Free monad transformers- -- $freet- IFreeF(..),- IFreeT(..),- wrap,- liftF- ) where--import Control.Category.Index-import Control.IMonad-import Control.IMonad.Trans--{- $freet- Indexed free monad transformers lift the constructor signatures to- the category of indexed Haskell functions: (':->').--> Return :: r :-> IFreeF f r x-> Wrap :: f x :-> IFreeF f r x->-> IFreeT :: m (IFreeF f r (IFreeT f m r)) :-> IFreeT f m r--}---- | Indexed equivalent to @FreeF@-data IFreeF f r (x :: * -> *) i = Return (r i) | Wrap (f x i)---- | Indexed equivalent to @FreeT@-newtype IFreeT f m r i = IFreeT { runIFreeT :: m (IFreeF f r (IFreeT f m r)) i }--instance (IFunctor f, IMonad m) => IFunctor (IFreeT f m) where- fmapI f x = x ?>= returnI . f--instance (IFunctor f, IMonad m) => IMonad (IFreeT f m) where- returnI = IFreeT . returnI . Return- bindI f m = IFreeT $- runIFreeT m ?>= \x ->- runIFreeT $ case x of- Return r -> f r- Wrap w -> wrap $ fmapI (bindI f) w--instance (IFunctor f) => IMonadTrans (IFreeT f) where- liftI = IFreeT . fmapI Return---- | Indexed equivalent to @wrap@-wrap :: (IMonad m) => f (IFreeT f m r) :-> IFreeT f m r-wrap = IFreeT . returnI . Wrap---- | Indexed equivalent to @liftF@-liftF :: (IFunctor f, IMonad m) => f r :-> IFreeT f m r-liftF x = wrap $ fmapI returnI x---- FIXME: Add IIdentity so that IFree can be defined in terms of IFreeT
Control/MFunctor.hs view
@@ -3,12 +3,54 @@ {-# LANGUAGE Rank2Types #-} module Control.MFunctor (- -- * Monads over functors- MFunctor(..)+ -- * Functors over Monads+ MFunctor(..),+ raise ) where +import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Monad.Trans.Identity (IdentityT, mapIdentityT)+import Control.Monad.Trans.Maybe (MaybeT, mapMaybeT)+import Control.Monad.Trans.Reader (ReaderT, mapReaderT)+import Control.Monad.Trans.RWS (RWST, mapRWST)+import qualified Control.Monad.Trans.State.Strict as StateStrict+import qualified Control.Monad.Trans.State.Lazy as StateLazy +import qualified Control.Monad.Trans.Writer.Strict as WriterStrict+import qualified Control.Monad.Trans.Writer.Lazy as WriterLazy+ -- | A functor in the category of monads class MFunctor t where {-| Lift a monad morphism from @m@ to @n@ into a monad morphism from @(t m)@ to @(t n)@ -}- mapT :: (Monad m, Monad n) => (forall a . m a -> n a) -> t m b -> t n b+ hoist :: (Monad m) => (forall a . m a -> n a) -> t m b -> t n b++instance MFunctor IdentityT where+ hoist nat = mapIdentityT nat++instance MFunctor MaybeT where+ hoist nat = mapMaybeT nat++instance MFunctor (ReaderT r) where+ hoist nat = mapReaderT nat++instance MFunctor (RWST r w s) where+ hoist nat = mapRWST nat++instance MFunctor (StateStrict.StateT s) where+ hoist nat = StateStrict.mapStateT nat++instance MFunctor (StateLazy.StateT s) where+ hoist nat = StateLazy.mapStateT nat++instance MFunctor (WriterStrict.WriterT w) where+ hoist nat = WriterStrict.mapWriterT nat++instance MFunctor (WriterLazy.WriterT w) where+ hoist nat = WriterLazy.mapWriterT nat++{-| Lift the base monad++> raise = hoist lift+-}+raise :: (Monad m, MFunctor t1, MonadTrans t2) => t1 m r -> t1 (t2 m) r+raise = hoist lift
+ Control/PFunctor.hs view
@@ -0,0 +1,32 @@+-- | This module defines functors in the category of proxies++{-# LANGUAGE KindSignatures, Rank2Types #-}++module Control.PFunctor (+ -- * Functors over Proxies+ PFunctor(..),+ raiseP+ ) where++import Control.Proxy.Class (Proxy)+import Control.Proxy.Trans (ProxyTrans(liftP))++-- | A functor in the category of monads+class PFunctor (+ t :: (* -> * -> * -> * -> (* -> *) -> * -> *)+ -> * -> * -> * -> * -> (* -> *) -> * -> * ) where+ {-| Lift a proxy morphism from @p@ to @q@ into a proxy morphism from+ @(t p)@ to @(t q)@ -}+ hoistP+ :: (Monad m, Proxy p)+ => (forall a' a b' b r1 . p a' a b' b m r1 -> q a' a b' b m r1)+ -> (t p a' a b' b m r2 -> t q a' a b' b m r2)++{-| Lift the base proxy++> raiseP = hoistP liftP+-}+raiseP+ :: (Monad m, Proxy p, PFunctor t1, ProxyTrans t2)+ => t1 p a' a b' b m r -> t1 (t2 p) a' a b' b m r+raiseP = hoistP liftP
Control/Pipe.hs view
@@ -1,14 +1,200 @@--- | Top-level import for the "Control.Pipe" hierarchy+{-| This module remains as a wistful reminder of this library's humble origins.+ This library now builds upon the more general 'Proxy' type, but still keeps+ the @pipes@ name. Read "Control.Proxy.Tutorial" to learn about this new+ implementation. + The 'Pipe' type is a monad transformer that enriches the base monad with the+ ability to 'await' or 'yield' data to and from other 'Pipe's. -}+ module Control.Pipe (- -- * Modules- -- $modules- module Control.Pipe.Core+ -- * Types+ -- $types+ Pipe(..),+ Producer,+ Consumer,+ Pipeline,+ -- * Create Pipes+ -- $create+ await,+ yield,+ pipe,+ -- * Compose Pipes+ -- $category+ (<+<),+ (>+>),+ idP,+ PipeC(..),+ -- * Run Pipes+ runPipe ) where -import Control.Pipe.Core+import Control.Applicative (Applicative(pure, (<*>)))+import Control.Category (Category((.), id), (<<<), (>>>))+import Control.Monad (forever)+import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Proxy.Synonym (C)+import Prelude hiding ((.), id) -{- $modules- "Control.Pipe.Core" provides the core type and primitives.- - "Control.Pipe.Tutorial" provides an extended tutorial. -}+{- $types+ The 'Pipe' type is strongly inspired by Mario Blazevic's @Coroutine@ type in+ his concurrency article from Issue 19 of The Monad Reader.+-}++{-|+ The base type for pipes++ * @a@ - The type of input received from upstream pipes++ * @b@ - The type of output delivered to downstream pipes++ * @m@ - The base monad++ * @r@ - The type of the return value+-}+data Pipe a b m r+ = Await (a -> Pipe a b m r)+ | Yield b (Pipe a b m r)+ | M (m (Pipe a b m r))+ | Pure r+{-+Technically, the correct implementation that satisfies the monad transformer+laws is:++type PipeF a b x = Await (a -> x) | Yield b x deriving (Functor)++type Pipe a b = FreeT (PipeF a b)+-}++instance (Monad m) => Functor (Pipe a b m) where+ fmap f pr = go pr where+ go p = case p of+ Await k -> Await (\a -> go (k a))+ Yield b p' -> Yield b (go p')+ M m -> M (m >>= \p' -> return (go p'))+ Pure r -> Pure (f r)++instance (Monad m) => Applicative (Pipe a b m) where+ pure = Pure+ pf <*> px = go pf where+ go p = case p of+ Await k -> Await (\a -> go (k a))+ Yield b p' -> Yield b (go p')+ M m -> M (m >>= \p' -> return (go p'))+ Pure f -> fmap f px++instance (Monad m) => Monad (Pipe a b m) where+ return = Pure+ pm >>= f = go pm where+ go p = case p of+ Await k -> Await (\a -> go (k a))+ Yield b p' -> Yield b (go p')+ M m -> M (m >>= \p' -> return (go p'))+ Pure r -> f r++instance MonadTrans (Pipe a b) where+ lift m = M (m >>= \r -> return (Pure r))++-- | A pipe that produces values+type Producer b m r = Pipe () b m r++-- | A pipe that consumes values+type Consumer a m r = Pipe a C m r++-- | A self-contained pipeline that is ready to be run+type Pipeline m r = Pipe () C m r++{- $create+ 'yield' and 'await' are the only two primitives you need to create pipes.+ Since @Pipe a b m@ is a monad, you can assemble 'yield' and 'await'+ statements using ordinary @do@ notation. Since @Pipe a b@ is also a monad+ transformer, you can use 'lift' to invoke the base monad. For example, you+ could write a pipe stage that requests permission before forwarding any+ output:++> check :: (Show a) => Pipe a a IO r+> check = forever $ do+> x <- await+> lift $ putStrLn $ "Can '" ++ (show x) ++ "' pass?"+> ok <- read <$> lift getLine+> when ok (yield x)+-}++{-|+ Wait for input from upstream.++ 'await' blocks until input is available from upstream.+-}+await :: Pipe a b m a+await = Await Pure++{-|+ Deliver output downstream.++ 'yield' restores control back upstream and binds its value to 'await'.+-}+yield :: b -> Pipe a b m ()+yield b = Yield b (Pure ())++{-|+ Convert a pure function into a pipe++> pipe f = forever $ do+> x <- await+> yield (f x)+-}+pipe :: (Monad m) => (a -> b) -> Pipe a b m r+pipe f = go where+ go = Await (\a -> Yield (f a) go)++{- $category+ 'Pipe's form a 'Category', meaning that you can compose 'Pipe's using+ ('>+>') and also define an identity 'Pipe': 'idP'. These satisfy the+ category laws:++> idP >+> p = p+>+> p >+> idP = p+>+> (p1 >+> p2) >+> p3 = p1 >+> (p2 >+> p3)++ @(p1 >+> p2)@ satisfies all 'await's in @p2@ with 'yield's in @p1@. If any+ 'Pipe' terminates the entire 'Pipeline' terminates.+-}++-- | 'Pipe's form a 'Category' instance when you rearrange the type variables+newtype PipeC m r a b = PipeC { unPipeC :: Pipe a b m r}++instance (Monad m) => Category (PipeC m r) where+ id = PipeC idP+ PipeC p1 . PipeC p2 = PipeC $ p1 <+< p2++-- | Corresponds to ('<<<')/('.') from @Control.Category@+(<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r+(Yield b p1) <+< p2 = Yield b (p1 <+< p2)+(M m ) <+< p2 = M (m >>= \p1 -> return (p1 <+< p2))+(Pure r ) <+< _ = Pure r+(Await k ) <+< (Yield b p2) = k b <+< p2+p1 <+< (Await k) = Await (\a -> p1 <+< k a)+p1 <+< (M m) = M (m >>= \p2 -> return (p1 <+< p2))+_ <+< (Pure r) = Pure r++-- | Corresponds to ('>>>') from @Control.Category@+(>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r+p2 >+> p1 = p1 <+< p2++infixr 8 <+<+infixl 8 >+>++-- | Corresponds to 'id' from @Control.Category@+idP :: (Monad m) => Pipe a a m r+idP = go where+ go = Await (\a -> Yield a go)++-- | Run the 'Pipe' monad transformer, converting it back into the base monad+runPipe :: (Monad m) => Pipe () b m r -> m r+runPipe pl = go pl where+ go p = case p of+ Yield _ p' -> go p' + Await k -> go (k ())+ M m -> m >>= go+ Pure r -> return r
− Control/Pipe/Core.hs
@@ -1,230 +0,0 @@-{-| The 'Pipe' type is a monad transformer that enriches the base monad with the- ability to 'await' or 'yield' data to and from other 'Pipe's. -}--module Control.Pipe.Core (- -- * Types- -- $types- Pipe(..),- C,- Producer,- Consumer,- Pipeline,- -- * Create Pipes- -- $create- await,- yield,- pipe,- -- * Compose Pipes- -- $category- (<+<),- (>+>),- idP,- PipeC(..),- -- * Run Pipes- -- $runpipe- runPipe- ) where--import Control.Applicative (Applicative(pure, (<*>)))-import Control.Category (Category((.), id), (<<<), (>>>))-import Control.Monad (forever)-import Control.Monad.Trans.Class (MonadTrans(lift))-import Data.Closed (C)-import Prelude hiding ((.), id)--{- $types- The 'Pipe' type is strongly inspired by Mario Blazevic's @Coroutine@ type in- his concurrency article from Issue 19 of The Monad Reader.--}--{-|- The base type for pipes-- * @a@ - The type of input received from upstream pipes-- * @b@ - The type of output delivered to downstream pipes-- * @m@ - The base monad-- * @r@ - The type of the return value--}-data Pipe a b m r- = Await (a -> Pipe a b m r)- | Yield b (Pipe a b m r)- | M (m (Pipe a b m r))- | Pure r-{--type PipeF a b x = Await (a -> x) | Yield b x deriving (Functor)--type Pipe a b = FreeT (PipeF a b)--}--instance (Monad m) => Functor (Pipe a b m) where- fmap f pr = go pr where- go p = case p of- Await k -> Await (\a -> go (k a))- Yield b p' -> Yield b (go p')- M m -> M (m >>= \p' -> return (go p'))- Pure r -> Pure (f r)--instance (Monad m) => Applicative (Pipe a b m) where- pure = Pure- pf <*> px = go pf where- go p = case p of- Await k -> Await (\a -> go (k a))- Yield b p' -> Yield b (go p')- M m -> M (m >>= \p' -> return (go p'))- Pure f -> fmap f px--instance (Monad m) => Monad (Pipe a b m) where- return = Pure- pm >>= f = go pm where- go p = case p of- Await k -> Await (\a -> go (k a))- Yield b p' -> Yield b (go p')- M m -> M (m >>= \p' -> return (go p'))- Pure r -> f r--instance MonadTrans (Pipe a b) where- lift m = M (m >>= \r -> return (Pure r))---- | A pipe that produces values-type Producer b = Pipe () b---- | A pipe that consumes values-type Consumer b = Pipe b C---- | A self-contained pipeline that is ready to be run-type Pipeline = Pipe () C--{- $create- 'yield' and 'await' are the only two primitives you need to create pipes.- Since @Pipe a b m@ is a monad, you can assemble 'yield' and 'await'- statements using ordinary @do@ notation. Since @Pipe a b@ is also a monad- transformer, you can use 'lift' to invoke the base monad. For example, you- could write a pipe stage that requests permission before forwarding any- output:--> check :: (Show a) => Pipe a a IO r-> check = forever $ do-> x <- await-> lift $ putStrLn $ "Can '" ++ (show x) ++ "' pass?"-> ok <- read <$> lift getLine-> when ok (yield x)--}--{-|- Wait for input from upstream.-- 'await' blocks until input is available from upstream.--}-await :: Pipe a b m a-await = Await Pure--{-|- Deliver output downstream.-- 'yield' restores control back upstream and binds the result to 'await'.--}-yield :: b -> Pipe a b m ()-yield b = Yield b (Pure ())--{-|- Convert a pure function into a pipe--> pipe f = forever $ do-> x <- await-> yield (f x)--}-pipe :: (Monad m) => (a -> b) -> Pipe a b m r-pipe f = go where- go = Await (\a -> Yield (f a) go)--{- $category- 'Pipe's form a 'Category', meaning that you can compose 'Pipe's using- ('<+<') and also define an identity 'Pipe': 'idP'. These satisfy the- category laws:--> idP <+< p = p->-> p <+< idP = p->-> (p1 <+< p2) <+< p3 = p1 <+< (p2 <+< p3)-- 'Pipe' composition binds the output of the upstream 'Pipe' to the input of- the downstream 'Pipe'. Like Haskell functions, 'Pipe's are lazy, meaning- that upstream 'Pipe's are only evaluated as far as necessary to generate- enough input for downstream 'Pipe's. If any 'Pipe' terminates, it also- terminates every 'Pipe' composed with it.--}---- | 'Pipe's form a 'Category' instance when you rearrange the type variables-newtype PipeC m r a b = PipeC { unPipeC :: Pipe a b m r}--instance (Monad m) => Category (PipeC m r) where- id = PipeC idP- PipeC p1 . PipeC p2 = PipeC $ p1 <+< p2---- | Corresponds to ('<<<')/('.') from @Control.Category@-(<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r-(Yield b p1) <+< p2 = Yield b (p1 <+< p2)-(M m ) <+< p2 = M (m >>= \p1 -> return (p1 <+< p2))-(Pure r ) <+< _ = Pure r-(Await k ) <+< (Yield b p2) = k b <+< p2-p1 <+< (Await k) = Await (\a -> p1 <+< k a)-p1 <+< (M m) = M (m >>= \p2 -> return (p1 <+< p2))-_ <+< (Pure r) = Pure r---- | Corresponds to ('>>>') from @Control.Category@-(>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r-p2 >+> p1 = p1 <+< p2--{- These associativities might help performance since pipe evaluation is- downstream-biased. I set them to the same priority as (.). -}-infixr 9 <+<-infixl 9 >+>---- | Corresponds to 'id' from @Control.Category@-idP :: (Monad m) => Pipe a a m r-idP = go where- go = Await (\a -> Yield a go)--{- $runpipe- Note that you can also unwrap a 'Pipe' a single step at a time using- 'runFreeT' (since 'Pipe' is just a type synonym for a free monad- transformer). This will take you to the next /external/ 'await' or 'yield'- statement. This means that a closed 'Pipeline' will unwrap to a single- step, in which case you would have been better served by 'runPipe'.--}-{-|- Run the 'Pipe' monad transformer, converting it back into the base monad.-- 'runPipe' imposes two conditions:-- * The pipe's input, if any, is trivially satisfiable (i.e. @()@)-- * The pipe does not 'yield' any output-- The latter restriction makes 'runPipe' less polymorphic than it could be,- and I settled on the restriction for three reasons:-- * It prevents against accidental data loss.-- * It protects against silent failures-- * It prevents wastefully draining a scarce resource by gratuitously- driving it to completion-- If you believe that discarding output is the appropriate behavior, you can- specify this by explicitly feeding your output to a pipe that gratuitously- discards it:--> runPipe $ forever await <+< p--}-runPipe :: (Monad m) => Pipeline m r -> m r-runPipe pl = go pl where- go p = case p of- Yield _ p' -> go p' - Await k -> go (k ())- M m -> m >>= go- Pure r -> return r
− Control/Pipe/Tutorial.hs
@@ -1,539 +0,0 @@-{-|- This module provides the tutorial for "Control.Pipe".--}--module Control.Pipe.Tutorial (- -- * Types- -- $type-- -- * Composition- -- $compose-- -- * Modularity- -- $modular-- -- * Vertical Concatenation- -- $vertical-- -- * Return Values- -- $return-- -- * Termination- -- $terminate-- -- * Folds- -- $folds-- -- * Resource Management- -- $resource-- -- * Bidirectional Pipes- -- $bidirectional- ) where---- For documentation-import Control.Category-import Control.Frame hiding (await, yield)-import Control.Monad.Trans.Class-import Control.Pipe--{- $type- This library represents unidirectional streaming computations using the- 'Pipe' type.-- 'Pipe' is a monad transformer that extends the base monad with the ability- to 'await' input from or 'yield' output to other 'Pipe's. 'Pipe's resemble- enumeratees in other libraries because they receive an input stream and- transform it into a new output stream.-- I'll introduce our first 'Pipe', which is a verbose version of the Prelude's- 'take' function:--> take' :: Int -> Pipe a a IO ()-> take' n = do-> replicateM_ n $ do-> x <- await-> yield x-> lift $ putStrLn "You shall not pass!"-- This 'Pipe' forwards the first @n@ values it receives undisturbed, then it- outputs a cute message.-- Let's dissect the above 'Pipe''s type to learn a bit about how 'Pipe's work:--> | Input Type | Output Type | Base monad | Return value-> Pipe a a IO ()-- So @take'@ 'await's input values of type \'@a@\' from upstream 'Pipe's and- 'yield's output values of type \'@a@\' to downstream 'Pipe's. @take'@ uses- 'IO' as its base monad because it invokes the 'putStrLn' function. If we- were to remove the call to 'putStrLn', the compiler would infer the- following type instead, which is polymorphic in the base monad:--> take' :: (Monad m) => Int -> Pipe a a m ()-- Now let's create a function that converts a list into a 'Pipe' by 'yield'ing- each element of the list:--> fromList :: (Monad m) => [b] -> Pipe a b m ()-> fromList = mapM_ yield-- Note that @fromList xs@ is polymorphic in its input. This is because it- does not 'await' any input. If we wanted, we could type-restrict it to:--> fromList :: (Monad m) => [b] -> Pipe () b m ()-- There is no type that forbids a 'Pipe' from 'await'ing, but you can- guarantee that if it does 'await', the request is trivially satisfiable by- supplying it with @()@.-- A 'Pipe' that doesn't 'await' (any useful input) can serve as the first- stage in a 'Pipeline'. I provide a type synonym for this common case:--> type Producer b m r = Pipe () b m r-- 'Producer's resemble enumerators in other libraries because they function as- data sources.-- You can then use the 'Producer' type synonym to rewrite the type signature- for @fromList@ as:--> fromList :: (Monad m) => [b] -> Producer b m ()-- Now let's create a 'Pipe' that prints every value delivered to it:--> printer :: (Show b) => Pipe b c IO r-> printer = forever $ do-> x <- await-> lift $ print x-- Here, @printer@ is polymorphic in its output. We could type-restrict it to- guarantee it will never 'yield' by setting the output to 'C', an unhabited- type that \'@C@\'loses the output end:--> printer :: (Show b) => Pipe b C IO r-- A 'Pipe' that never 'yield's can be the final stage in a 'Pipeline'. Again,- I provide a type synonym for this common case:--> type Consumer b m r = Pipe b C m r-- So we could instead write @printer@'s type as:--> printer :: (Show b) => Consumer b IO r-- 'Consumer's resemble iteratees in other libraries because they function as- data sinks.--}--{- $compose- What distinguishes 'Pipe's from every other iteratee implementation is that- they form a true 'Category'. Because of this, you can literally compose- 'Pipe's into 'Pipeline's using ordinary composition:--> newtype PipeC m r a b = PipeC { unPipeC :: Pipe a b m r }-> instance Category (PipeC m r) where ...-- For example, you can compose the above 'Pipe's with:--> pipeline :: Pipe () C IO ()-> pipeline = unPipeC $ PipeC printer . PipeC (take' 3) . PipeC (fromList [1..])-- The compiler deduces that the final 'Pipe' must be blocked at both ends,- meaning it will never 'await' useful input and it will never 'yield' any- output. This represents a self-contained 'Pipeline' and I provide a type- synonym for this common case:--> type Pipeline m r = Pipe () C m r-- Also, I provide '<+<' as a convenience operator for composing 'Pipe's- without the burden of wrapping and unwrapping newtypes:--> p1 <+< p2 == unPipeC $ PipeC p1 . PipeC p2-- So you can rewrite @pipeline@ as:--> pipeline :: Pipeline IO ()-> pipeline = printer <+< take' 3 <+< fromList [1..]-- Like many other monad transformers, you convert the 'Pipe' monad back to the- base monad using some sort of \"@run...@\" function. In this case, it's the- 'runPipe' function:--> runPipe :: (Monad m) => Pipeline m r -> m r-- 'runPipe' only works on self-contained 'Pipeline's, but you don't need to- worry about explicitly type-restricting any of your 'Pipe's. Self-contained- 'Pipeline's will automatically have polymorphic input and output ends and- they will type-check when you provide them to 'runPipe'.-- Let's try using 'runPipe':-->>> runPipe pipeline-1-2-3-You shall not pass!-- Fascinating! Our 'Pipe' terminates even though @printer@ never terminates- and @fromList@ never terminates when given an infinite list. To illustrate- why our 'Pipe' terminates, let's outline the 'Pipe' flow control rules for- composition:-- * 'Pipe's are lazy, so execution begins at the most downstream 'Pipe'- (@printer@ in our example).-- * When a 'Pipe' 'await's, it blocks until it receives input from the next- 'Pipe' upstream-- * When a 'Pipe' 'yield's, it blocks until it receives a new 'await' request- from downstream.-- * If a 'Pipe' terminates, it terminates every other 'Pipe' composed with it.-- All of these flow control rules uniquely follow from the 'Category' laws.-- It might surprise you that termination brings down the entire 'Pipeline'- until you realize that:-- * Downstream 'Pipe's depending on the result from the terminated 'Pipe'- cannot proceed-- * Upstream 'Pipe's won't be further evaluated because the terminated 'Pipe'- will not request any further input from them-- So in our previous example, the 'Pipeline' terminated because \"@take' 3@\"- terminated and brought down the entire 'Pipeline' with it.-- Actually, these flow control rules will mislead you into thinking that- composed 'Pipe's behave as a collection of sub-'Pipe's with some sort of- message passing architecture between them, but nothing could be further from- the truth! When you compose 'Pipe's, they automatically fuse into a single- 'Pipe' that corresponds to how you would have written the control flow by- hand.-- For example, if you compose @printer@ and @fromList@:--> printer <+< fromList [1..]-- The result is indistinguishable from:--> lift (mapM_ print [1..])-- ... which is what we would have written by hand if we had not used 'Pipe's- at all! All 'runPipe' does is just remove the 'lift'!--}--{- $modular- Given a loop like:--> loop :: IO r-> loop = forever $ do-> x <- dataSource-> y <- processData x-> dataSink y-- We could decompose it into three separate parts:--> stage1 :: Producer a IO r-> stage1 = forever $ do-> x <- dataSource-> yield x->-> stage2 :: Pipe a b IO r-> stage2 = forever $ do-> x <- await-> y <- processData x-> yield y->->-> stage3 :: Consumer b IO r-> stage3 = forever $ do-> y <- await-> dataSink y->-> stage3 <+< stage2 <+< stage1 = lift loop-- In other words, 'Pipe's let you decompose loops into modular components,- which promotes loose coupling and allows you to freely mix and match those- components.-- To demonstrate this, let's define a new data source that indefinitely- prompts the user for integers:--> prompt :: Producer Int IO a-> prompt = forever $ do-> lift $ putStrLn "Enter a number: "-> n <- read <$> lift getLine-> yield n-- Now we can use it as a drop-in replacement for @fromList@:-->>> runPipe $ printer <+< take' 3 <+< prompt-Enter a number:-1<Enter>-1-Enter a number:-2<Enter>-2-Enter a number:-3<Enter>-3-You shall not pass!---}--{- $vertical- You can easily \"vertically\" concatenate 'Pipe's, 'Producer's, and- 'Consumer's, all using simple monad sequencing: ('>>'). For example, here- is how you concatenate 'Producer's:-->>> runPipe $ printer <+< (fromList [1..3] >> fromList [10..12])-1-2-3-10-11-12-- Here's how you would concatenate 'Consumer's:-->>> let print' n = printer <+< take' n :: (Show a) => Int -> Consumer a IO ()->>> runPipe $ (print' 3 >> print' 4) <+< fromList [1..]-1-2-3-You shall not pass!-4-5-6-7-You shall not pass!-- ... but the above example is gratuitous because we could have just- concatenated the intermediate @take'@ 'Pipe':-->>> runPipe $ printer <+< (take' 3 >> take' 4) <+< fromList [1..]-1-2-3-You shall not pass!-4-5-6-7-You shall not pass!---}--{- $return- 'Pipe' composition imposes an important requirement: You can only compose- 'Pipe's that have the same return type. For example, I could write the- following function:--> deliver :: (Monad m) => Int -> Consumer a m [a]-> deliver n = replicateM n await-- ... and I might try to compose it with @fromList@:-->>> runPipe $ deliver 3 <+< fromList [1..10] -- wrong!-- ... but this wouldn't type-check, because @fromList@ has a return type of- @()@ and @deliver@ has a return type of @[Int]@. Composition requires that- every 'Pipe' has a return value ready in case it terminates first.-- Fortunately, we don't have to rewrite the @fromList@ function because we can- just add a return value using vertical concatenation:-->>> runPipe $ deliver 3 <+< (fromList [1..10] >> return [])-[1,2,3]-- ... although a more idiomatic Haskell version would be:-->>> runPipe $ (Just <$> deliver 3) <+< (fromList [1..10] *> pure Nothing)-Just [1,2,3]-- This forces you to cover all code paths by thinking about what return value- you would provide if something were to go wrong. For example, let's say I- were to make a mistake and request more input than @fromList@ can deliver:-->>> runPipe $ (Just <$> deliver 99) <+< (fromList [1..10] *> pure Nothing)-Nothing-- The type system saved me by forcing me to cover all corner cases and handle- every way my program could terminate.--}--{- $terminate-- Now what if you wanted to write a 'Pipe' that only reads from its input end- (i.e. a 'Consumer') and returns a list of every value delivered to it when- its input 'Pipe' terminates?--> toList :: (Monad m) => Consumer a m [a]-> toList = ???-- You can't write such a 'Pipe' because if its input terminates then it brings- down @toList@ with it! This is correct because @toList@ as defined is not- compositional (yet!).-- To see why, let's say you somehow got @toList@ to work and the following- imaginary code sample worked:-->>> runPipe $ toList <+< (fromList [1..5] >> return [])-[1,2,3,4,5]-- @toList@ is defined to return its value when the 'Pipe' immediately upstream- (@fromList@ in this case) terminates. This behavior immediately leads to a- problem. What if I were to insert an \"identity\" 'Pipe' between @toList@- and @fromList@:--> identity = forever $ await >>= yield-> -- This is how id is actually implemented!-- This 'Pipe' forwards every valued untouched, so we would expect it to not- have any affect if we were to insert it in the middle:-->>> runPipe $ toList <+< identity <+< (fromList [1..5] >> return [])-??? -- Oops! Something other than [1,2,3,4,5], perhaps even non-termination-- The answer couldn't be @[1,2,3,4,5]@ because @toList@ would monitor - @identity@ instead of @fromList@ and since @identity@ never terminates- @toList@ never terminates. This is what I mean when I say that @toList@'s- specified behavior is non-compositional. It only works if it is coupled- directly to the desired 'Pipe' and breaks when you introduce intermediate- stages.-- Note that a terminated 'Pipe' only brings down 'Pipe's composed with it. To- illustrate this, let's use the following example:--> p = do a <+< b-> c-- @a@, @b@, and @c@ are 'Pipe's, and @c@ shares the same input and output as- the composite 'Pipe' @a <+< b@, otherwise we cannot combine them within the- same monad. In the above example, either @a@ or @b@ could terminate and- bring down the other one since they are composed, but @c@ is guaranteed to- continue after @a <+< b@ terminates because it is not composed with them.- Conceptually, we can think of this as @c@ automatically taking over the- 'Pipe''s channeling responsibilities when @a <+< b@ can no longer continue.- There is no need to \"restart\" the input or output manually as in some- other iteratee libraries.-- The @pipes@ library, unlike other iteratee libraries, grounds its vertical- and horizontal concatenation in category theory by deriving horizontal- concatenation ('.') from its 'Category' instance and vertical concatenation- ('>>') from its 'Monad' instance. This makes it easier to reason about- 'Pipe's because you can leverage your intuition about 'Category's and- 'Monad's to understand their behavior. The only 'Pipe'-specific primitives- are 'await' and 'yield'.--}--{- $folds- While we cannot intercept termination, we can still fold our input. We can- embed 'WriterT' in our base monad, since 'Pipe' is a monad transformer, and- store the result in the monoid:--> toList :: Consumer a (WriterT [a] m) r-> toList = forever $ do-> a <- await-> lift $ tell [a]-->>> execWriterT $ runPipe $ toList <+< fromList [1..4]-[1,2,3,4]-- But what if other pipes have a base monad that is not compatible, such as:--> prompt3 :: Producer Int IO a-> prompt3 = take' 3 <+< prompt-- That's okay, because we can transparently 'lift' any Pipe's base monad,- using 'hoistFreeT' from @Control.Monad.Trans.Free@ in the @free@ package:-->>> execWriterT $ runPipe $ toList <+< hoistFreeT lift prompt3-3<Enter>-4<Enter>-6<Enter>-[3,4,6]---}--{- $resource- Pipes handle streaming computations well, but do not handle resource- management well. To see why, let's say we have the file \"@test.txt@\"- with the following contents:--> Line 1-> Line 2-> Line 3-- .. and we wish to lazily read one line at a time from it:--> readFile' :: Handle -> Producer Text IO ()-> readFile' h = do-> eof <- lift $ hIsEOF h-> when (not eof) $ do-> s <- lift $ hGetLine h-> yield s-> readFile' h-- We could then try to be slick and write a lazy version that only reads as- many lines as we request:--> read' :: FilePath -> Producer Text IO ()-> read' file = do-> lift $ putStrLn "Opening file ..."-> h <- lift $ openFile file ReadMode-> readFile' h-> lift $ putStrLn "Closing file ..."-> lift $ hClose h-- Now compose!-->>> runPipe $ printer <+< read' "test.xt"-Opening file ...-"Line 1"-"Line 2"-"Line 3"-Closing file ...-- So far, so good. Equally important, the file is never opened if we replace- @printer@ with a 'Pipe' that never demands input:-->>> runPipe $ (lift $ putStrLn "I don't need input") <+< read' "test.txt"-I don't need input-- There is still one problem, though. What if we wrote:-->>> runPipe $ printer <+< take' 2 <+< read' "test.txt"-Opening file ...-"Line 1"-"Line 2"-You shall not pass!-- Oh no! While it was lazy and only read two lines from the file, it was also- too lazy to properly close our file! \"@take' 2@\" terminated before- @read'@, preventing @read'@ from properly closing \"test.txt\". This is why- 'Pipe' composition fails to guarantee deterministic finalization.-- The "Control.Frame" module of this library provides a temporary solution to- this problem, but in the longer run there will be a more elegant solution- built on top of "Control.Proxy".--}--{- $bidirectional- The 'Pipe' type suffers from one restriction: it only handles a- unidirectional flow of information. If you want a bidirectional 'Pipe'- type, then use the 'Proxy' type from "Control.Proxy", which generalizes the- 'Pipe' type to bidirectional flow.-- More importantly, the 'Proxy' type is a strict superset of the 'Pipe' type,- so all 'Pipe' utilities and extensions are actually written as 'Proxy'- utilities and extensions, in order to avoid code duplication.-- So if you want to use these extensions, import "Control.Proxy" instead,- which exports a backwards compatible 'Pipe' implementation along with all- utilities and extensions. The 'Pipe' implementation in "Control.Pipe.Core"- exists purely as a reference implementation for people who wish to study the- simpler 'Pipe' type when building their own iteratee libraries.--}
Control/Proxy.hs view
@@ -1,32 +1,35 @@--- | Default imports for the "Control.Proxy" hierarchy+{-| Recommended entry import for this library + Read "Control.Proxy.Tutorial" for an extended proxy tutorial. -}+ module Control.Proxy ( -- * Modules- -- $modules- module Control.Proxy.Class,+ -- $default module Control.Proxy.Core,- module Control.Proxy.Pipe,- module Control.Proxy.Trans,- module Control.Proxy.Prelude+ module Control.Proxy.Core.Fast ) where -import Control.Proxy.Class import Control.Proxy.Core-import Control.Proxy.Pipe-import Control.Proxy.Trans-import Control.Proxy.Prelude+import Control.Proxy.Core.Fast hiding (Request, Respond, M, Pure) -{- $modules- "Control.Proxy.Core" provides the core 'Proxy' type.+{- $default+ "Control.Proxy.Core" exports everything except 'runProxy'. - "Control.Proxy.Class" provides the abstract interface to 'Proxy' operations.+ This library provides two base proxy implementations, each of which export+ their own 'runProxy' function: - "Control.Proxy.Trans" provides proxy transformers.+ * "Control.Proxy.Core.Fast": This runs faster for code that is not+ 'IO'-bound, but it only obeys the monad transformer laws modulo safe+ observation functions. - "Control.Proxy.Pipe" provides a backwards-compatible re-implementation of- 'Pipe's.+ * "Control.Proxy.Core.Correct": This trades speed on pure code segments, but+ strictly preserves the monad transformer laws. - "Control.Proxy.Prelude" provides a standard library of proxies.+ This module selects the currently recommended implementation (Fast). - Consult "Control.Proxy.Tutorial" for an extended tutorial.+ You can switch to the correct implementation by importing+ "Control.Proxy.Core" and "Control.Proxy.Core.Correct".++ You can lock in the fast implementation (in case I change the recommended+ default) by importing "Control.Proxy.Core" and "Control.Proxy.Core.Fast". -}
Control/Proxy/Class.hs view
@@ -1,72 +1,308 @@-{-| This module provides an abstract interface to 'Proxy'-like behavior, so that- multiple proxy implementations can share the same library of utility- proxies. -}+{-# LANGUAGE Rank2Types #-} +{-| The 'Proxy' class defines the library's core API. Everything else in this+ library builds exclusively on top of the 'Proxy' type class so that all+ proxy implementations and extensions can share the same standard library.++ Several of these type classes duplicate methods from familiar type-classes+ (such as ('?>=') duplicating ('>>=')). You do NOT need to use these+ duplicate methods. Instead, read the \"Polymorphic proxies\" section below+ which explains their purpose and how they help clean up type signatures. -}+ module Control.Proxy.Class (- -- * Proxy composition- Channel(..),- -- * Proxy request and respond+ -- * Core proxy class+ Proxy(..),+ idT,+ coidT,+ (<-<),+ (<~<),++ -- * request/respond substitution Interact(..),+ (/</),+ (\<\),++ -- * Laws+ -- $laws++ -- * Polymorphic proxies+ -- $poly+ MonadPlusP(..),+ MonadIOP(..) ) where -{- * I use educated guesses about which associativy is optimal for each operator- * Keep precedence lower than function composition, which is 9 at the time of- of this comment -}+import Control.Monad.IO.Class (MonadIO)++-- Documentation imports+import Control.Monad.Trans.Class (lift)+import Control.MFunctor(hoist)++{- * I make educated guesses about which associativy is most efficient for each+ operator.+ * Keep proxy composition lower in precedence than function composition, which+ is 9 at the time of of this comment, so that users can write things like:++> lift . k >-> p+>+> hoist f . k >-> p+-} infixr 7 <-< infixl 7 >-> infixr 8 /</ infixl 8 \>\ infixl 8 \<\ infixr 8 />/+infixl 1 ?>= -- This should match the fixity of >>= -{-| The 'Channel' class defines an interface to a bidirectional flow of- information.+{-| The core API for the @pipes@ library - Laws:+ You should only use 'request', 'respond', and ('>->') + I only provide ('>~>') for theoretical symmetry, and the remaining methods+ just implement internal type class plumbing.+-}+class Proxy p where+ {-| 'request' input from upstream, passing an argument with the request++ @request a'@ passes @a'@ as a parameter to upstream that upstream may+ use to decide what response to return. 'request' binds the upstream's+ response of type @a@ to its own return value. -}+ request :: (Monad m) => a' -> p a' a b' b m a++ {-| 'respond' with an output for downstream and bind downstream's next+ 'request'+ + @respond b@ satisfies a downstream 'request' by supplying the value @b@.+ 'respond' blocks until downstream 'request's a new value and binds the+ argument of type @b'@ from the next 'request' as its return value. -}+ respond :: (Monad m) => b -> p a' a b' b m b'++ {-| Compose two proxies blocked on a 'respond', generating a new proxy+ blocked on a 'respond'++ Begins from the downstream end and satisfies every 'request' with a+ 'respond' -}+ (>->)+ :: (Monad m)+ => (b' -> p a' a b' b m r)+ -> (c' -> p b' b c' c m r)+ -> (c' -> p a' a c' c m r)++ {-| Compose two proxies blocked on a 'request', generating a new proxy+ blocked on a 'request'++ Begins from the upstream end and satisfies every 'respond' with a+ 'request' -}+ (>~>)+ :: (Monad m)+ => (a -> p a' a b' b m r)+ -> (b -> p b' b c' c m r)+ -> (a -> p a' a c' c m r)++ {-| 'return_P' is identical to 'return', except with a more polymorphic+ constraint. -}+ return_P :: (Monad m) => r -> p a' a b' b m r++ {-| ('?>=') is identical to ('>>='), except with a more polymorphic+ constraint. -}+ (?>=)+ :: (Monad m)+ => p a' a b' b m r -> (r -> p a' a b' b m r') -> p a' a b' b m r'++ {-| 'lift_P' is identical to 'lift', except with a more polymorphic+ constraint. -}+ lift_P :: (Monad m) => m r -> p a' a b' b m r++ {-| 'hoist_P' is identical to 'hoist', except with a more polymorphic+ constraint. -}+ hoist_P+ :: (Monad m)+ => (forall r . m r -> n r) -> (p a' a b' b m r' -> p a' a b' b n r')++{-| 'idT' forwards requests followed by responses++> idT = request >=> respond >=> idT+-}+idT :: (Monad m, Proxy p) => a' -> p a' a a' a m r+idT = go where+ go a' =+ request a' ?>= \a ->+ respond a ?>= \a'2 ->+ go a'2+-- idT = foreverK $ request >=> respond++{-| 'coidT' forwards responses followed by requests++> coidT = respond >=> request >=> coidT+-}+coidT :: (Monad m, Proxy p) => a -> p a' a a' a m r+coidT = go where+ go a =+ respond a ?>= \a' ->+ request a' ?>= \a2 ->+ go a2+-- coidT = foreverK $ respond >=> request++{-| Compose two proxies blocked on a 'respond', generating a new proxy blocked+ on a 'respond'++ Begins from the downstream end and satisfies every 'request' with a+ 'respond' -}+(<-<)+ :: (Monad m, Proxy p)+ => (c' -> p b' b c' c m r)+ -> (b' -> p a' a b' b m r)+ -> (c' -> p a' a c' c m r)+p1 <-< p2 = p2 >-> p1++{-| Compose two proxies blocked on a 'request', generating a new proxy blocked+ on a 'request'++ Begins from the upstream end and satisfies every 'respond' with a 'request'++ You don't need to use this. I include it only for symmetry. -}+(<~<)+ :: (Monad m, Proxy p)+ => (b -> p b' b c' c m r)+ -> (a -> p a' a b' b m r)+ -> (a -> p a' a c' c m r)+p1 <~< p2 = p2 >~> p1++-- | Two extra Proxy categories of theoretical interest+class Interact p where+ -- | @f \\>\\ g@ replaces all 'request's in 'g' with 'f'.+ (\>\) :: (Monad m)+ => (b' -> p a' a x' x m b)+ -> (c' -> p b' b x' x m c)+ -> (c' -> p a' a x' x m c)++ -- | @f \/>\/ g@ replaces all 'respond's in 'f' with 'g'.+ (/>/) :: (Monad m)+ => (a -> p x' x b' b m a')+ -> (b -> p x' x c' c m b')+ -> (a -> p x' x c' c m a')++-- | @f \/<\/ g@ replaces all 'request's in 'f' with 'g'.+(/</) :: (Monad m, Interact p)+ => (c' -> p b' b x' x m c)+ -> (b' -> p a' a x' x m b)+ -> (c' -> p a' a x' x m c)+p1 /</ p2 = p2 \>\ p1++-- | @f \\<\\ g@ replaces all 'respond's in 'g' with 'f'.+(\<\) :: (Monad m, Interact p)+ => (b -> p x' x c' c m b')+ -> (a -> p x' x b' b m a')+ -> (a -> p x' x c' c m a')+p1 \<\ p2 = p2 />/ p1++{- $laws+ The 'Proxy' class defines an interface to all core proxy capabilities that+ all proxy-like types must implement.++ First, all proxies must support a bidirectional flow of information, defined+ by:++ * ('>->')++ * ('>~>')++ * 'request'++ * 'respond'++ Intuitively, both @p1 >-> p2@ and @p1 >~> p2@ pair each 'request' in @p2@+ with a 'respond' in @p1@. ('>->') accepts proxies blocked on 'respond' and+ begins from the downstream end, whereas ('>~>') accepts proxies blocked on+ 'request' and begins from the upstream end.++ Second, all proxies are monads, defined by:++ * 'return_P'++ * ('?>=')++ These must satify the monad laws using @(>>=) = (?>=)@ and+ @return = return_P@.++ Third, all proxies are monad transformers, defined by:++ * 'lift_P'++ This must satisfy the monad transformer laws, using @lift = lift_P@.++ Fourth, all proxies are functors in the category of monads, defined by:++ * 'hoist_P'++ This must satisfy the functor laws, using @hoist = hoist_P@.++ All 'Proxy' instances must satisfy these additional laws:+ * ('>->') and 'idT' form a category: -> idT >-> f = f+> Define: idT = request >=> respond >=> idT >-> f >-> idT = f+> idT >-> p = p >-> (f >-> g) >-> h = f >-> (g >-> h)+> p >-> idT = p+>+> (p1 >-> p2) >-> p3 = p1 >-> (p2 >-> p3) - Minimal complete definition:+ * ('>~>') and 'coidT' form a category: - * 'idT'+> Define: coidT = respond >=> request >=> coidT+>+> coidT >~> p = p+>+> p >~> coidT = p+>+> (p1 >~> p2) >~> p3 = p1 >~> (p2 >~> p3) - * ('>->') or ('<-<').--}-class Channel p where- {-| 'idT' acts like a \'T\'ransparent proxy, passing all requests further- upstream, and passing all responses further downstream. -}- idT :: (Monad m) => a' -> p a' a a' a m r+ * @(hoistK f)@ defines a functor between proxy categories: - {-| Compose two proxies, satisfying all requests from downstream with- responses from upstream. -}- (>->) :: (Monad m)- => (b' -> p a' a b' b m r)- -> (c' -> p b' b c' c m r)- -> (c' -> p a' a c' c m r)- p1 >-> p2 = p2 <-< p1+> Define: hoistK f = (hoist f .)+>+> hoistK f (p1 >-> p2) = hoistK f p1 >-> hoistK p2+>+> hoistK f idT = idT+>+> hoistK f (p1 >~> p2) = hoistK f p1 >~> hoistK p2+>+> hoistK f coidT = coidT - {-| Compose two proxies, satisfying all requests from downstream with- responses from upstream. -}- (<-<) :: (Monad m)- => (c' -> p b' b c' c m r)- -> (b' -> p a' a b' b m r)- -> (c' -> p a' a c' c m r)- p1 <-< p2 = p2 >-> p1+ Also, all proxies must satisfy the following 'Proxy' laws: -{-| The 'Interact' class defines the ability to:+> -- Define: liftK = (lift .)+>+> p1 >-> liftK f = liftK f+>+> p1 >-> (liftK f >=> respond >=> p2) = liftK f >=> respond >=> (p1 >-> p2)+>+> (liftK g >=> respond >=> p1) >-> (liftK f >=> request >=> liftK h >=> p2)+> = liftK (f >=> g >=> h) >=> (p1 >-> p2)+>+> (liftK g >=> request >=> p1) >-> (liftK f >=> request >=> p2)+> = liftK (f >=> g) >=> request >=> (p1 >~> p2)+>+> liftK f >~> p2 = liftK f+>+> (liftK f >=> request >=> p1) >~> p2 = liftK f >=> request >=> (p1 >~> p2)+>+> (liftK f >=> respond >=> liftK h >=> p1) >~> (liftK g >=> request >=> p2)+> = liftK (f >=> g >=> h) >=> (p1 >~> p2)+>+> (liftK f >=> respond >=> p1) >~> (liftK g >=> respond >=> p2)+> = liftK (f >=> g) >=> (p1 >-> p2) - * Request input using the 'request' command+ The 'Interact' class exists primarily for theoretical interest and to+ justify some of the functor laws for the 'ProxyTrans' type class. You will+ probably never use it. + The 'Interact' class defines the ability to:+ * Replace existing 'request' commands using ('\>\') - * Respond with output using the 'respond' command- * Replace existing 'respond' commands using ('/>/') Laws:@@ -87,56 +323,130 @@ > > (f />/ g) />/ h = f />/ (g />/ h) - Minimal complete definition:+ Additionally, ('\>\') and ('/>/') distribute in one direction over Kleisli+ composition: - * 'request',+> a \>\ (b >=> c) = (a \>\ b) >=> (a \>\ c)+>+> a \>\ return = return - * ('\>\') or ('/</'),+> (b >=> c) />/ a = (b />/ a) >=> (c />/ a)+>+> return />/ a = return+-} - * 'respond', and+{- $poly+ Many of these type classes contain methods which copy methods from more+ familiar type classes. These duplicate methods serve two purposes. - * ('/>/') or ('\<\').--}-class Interact p where- {-| 'request' input from upstream, passing an argument with the request+ First, this library requires type class instances that would otherwise be+ impossible to define without providing higher-kinded constraints. Rather+ than use the following illegal polymorphic constraint: - @request a'@ passes @a'@ as a parameter to upstream that upstream may- use to decide what response to return. 'request' binds the upstream's- response to its own return value. -}- request :: (Monad m) => a' -> p a' a x' x m a+> instance (forall a' a b' b . MonadTrans (p a' a b' b)) => ... - -- | @f \\>\\ g@ replaces all 'request's in 'g' with 'f'.- (\>\) :: (Monad m)- => (b' -> p a' a x' x m b)- -> (c' -> p b' b x' x m c)- -> (c' -> p a' a x' x m c)- p1 \>\ p2 = p2 /</ p1+ ... the instance can instead use the following Haskell98 constraint: - -- | @f \/<\/ g@ replaces all 'request's in 'f' with 'g'.- (/</) :: (Monad m)- => (c' -> p b' b x' x m c)- -> (b' -> p a' a x' x m b)- -> (c' -> p a' a x' x m c)- p1 /</ p2 = p2 \>\ p1+> instance (MonadTransP p) => ... - {-| 'respond' with an output for downstream and bind downstream's next- 'request'- - @respond b@ satisfies a downstream 'request' by supplying the value @b@- 'respond' blocks until downstream 'request's a new value and binds the- argument from the next 'request' as its return value. -}- respond :: (Monad m) => a -> p x' x a' a m a'+ Second, these type classes don't require the @FlexibleContexts@ extension+ to use and substantially clean up constraints in type signatures. They+ convert messy constraints like this: - -- | @f \/>\/ g@ replaces all 'respond's in 'f' with 'g'.- (/>/) :: (Monad m)- => (a -> p x' x b' b m a')- -> (b -> p x' x c' c m b')- -> (a -> p x' x c' c m a')- p1 />/ p2 = p2 \<\ p1+> p :: (MonadP (p a' a b' b m), MonadTrans (p a' a b' b)) => ... - -- | @f \\<\\ g@ replaces all 'respond's in 'g' with 'f'.- (\<\) :: (Monad m)- => (b -> p x' x c' c m b')- -> (a -> p x' x b' b m a')- -> (a -> p x' x c' c m a')- p1 \<\ p2 = p2 />/ p1+ .. into cleaner and more general constraints like this:++> P :: (Proxy p) => ...++ These type classes exist solely for internal plumbing and you should never+ directly use the duplicate methods from them. Instead, you can use all the+ original type classes as long as you embed your proxy code within at least+ one proxy transformer (or 'IdentityP' if don't use any transformers). The+ type-class machinery will then automatically convert the messier and less+ polymorphic constraints to the simpler and more general constraints.++ For example, consider the following almost-correct definition for @mapMD@+ (from "Control.Proxy.Prelude.Base"):++> import Control.Monad.Trans.Class+> import Control.Proxy+>+> mapMD f = foreverK $ \a' -> do+> a <- request a'+> b <- lift (f a)+> respond b++ The compiler infers the following messy constraint:++> mapMD+> :: (Monad m, Monad (p x a x b m), MonadTrans (p x a x b), Proxy p)+> => (a -> m b) -> x -> p x a x b m r++ Instead, you can embed the code in the @IdentityP@ proxy transformer by+ wrapping it in 'runIdentityK':++> -- |difference| +> mapMD f = runIdentityK $ foreverK $ \a' -> do+> a <- request a'+> b <- lift (f a)+> respond b++ ... and now the compiler collapses all the constraints into the 'Proxy'+ constraint:++> mapMD :: (Monad m, Proxy p) => (a -> m b) -> x -> p x a x b m r++ You do not incur any performance penalty for writing polymorphic code or+ embedding it in 'IdentityP'. This library employs several rewrite @RULES@+ which transform your polymorphic code into the equivalent type-specialized+ hand-tuned code. These rewrite rules fire very robustly and they do not+ require any assistance on your part from compiler pragmas like @INLINE@,+ @NOINLINE@ or @SPECIALIZE@.++ If you nest proxies within proxies:++> example () = do+> request ()+> lift $ request ()+> lift $ lift $ request ()++ ... then you can still keep the nice constraints using:++> example () = runIdentityP . hoist (runIdentityP . hoist runIdentityP) $ do+> request ()+> lift $ request ()+> lift $ lift $ request ()++ You don't need to use 'runIdentityP' \/ 'runIdentityK' if you use any other+ proxy transformers (In fact you can't, it's a type error). The following+ code example illustrates this, where the 'throw' command (from the 'EitherP'+ proxy transformer) suffices to guide the compiler to the cleaner type+ signature:++> import Control.Monad+> import Control.Proxy+> import qualified Control.Proxy.Trans.Either as E+>+> example :: (Monad m, Proxy p) => () -> Producer (EitherP String p) Char m ()+> example () = do+> c <- request ()+> when (c == ' ') $ E.throw "Error: received space"+> respond c+-}++{-| The @(MonadPlusP p)@ constraint is equivalent to the following constraint:++> (forall a' a b' b m . (Monad m) => MonadPlus (p a' a b' b m)) => ...+-}+class (Proxy p) => MonadPlusP p where+ mzero_P :: (Monad m) => p a' a b' b m r+ mplus_P+ :: (Monad m) => p a' a b' b m r -> p a' a b' b m r -> p a' a b' b m r++{-| The @(MonadIOP p)@ constraint is equivalent to the following constraint:++> (forall a' a b' b m . (MonadIO m) => MonadIO (p a' a b' b m)) => ...+-}+class (Proxy p) => MonadIOP p where+ liftIO_P :: (MonadIO m) => IO r -> p a' a b' b m r
Control/Proxy/Core.hs view
@@ -1,217 +1,45 @@-{-| A 'Proxy' 'request's input from upstream and 'respond's with output to- downstream.-- For an extended tutorial, consult "Control.Proxy.Tutorial". -}+-- | Default imports for the "Control.Proxy" hierarchy module Control.Proxy.Core (- -- * Types- Proxy(..),- C,- Server,- Client,- Session,- -- * Run Sessions - -- $run- runProxy,- runProxyK,- runSession,- runSessionK,- -- * Utility Proxies- -- $utility- discard,- ignore+ -- * Modules+ -- $modules+ module Control.Proxy.Class,+ module Control.Proxy.Synonym,+ module Control.Proxy.Prelude,+ module Control.Proxy.Trans,+ module Control.Proxy.Trans.Identity,+ module Control.Monad,+ module Control.Monad.Trans.Class,+ module Control.MFunctor ) where -import Control.Applicative (Applicative(pure, (<*>)))-import Control.Monad (ap, forever, liftM, (>=>))-import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.MFunctor (MFunctor(hoist))+import Control.Monad (forever, (>=>), (<=<)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (- Channel(idT, (<-<)), Interact(request, (/</), respond, (\<\)) )-import Data.Closed (C)--{-| A 'Proxy' communicates with an upstream interface and a downstream- interface.-- The type variables of @Proxy req_a resp_a req_b resp_b m r@ signify:-- * @req_a @ - The request supplied to the upstream interface-- * @resp_a@ - The response provided by the upstream interface-- * @req_b @ - The request supplied by the downstream interface-- * @resp_b@ - The response provided to the downstream interface-- * @m @ - The base monad-- * @r @ - The final return value -}-data Proxy a' a b' b m r- = Request a' (a -> Proxy a' a b' b m r )- | Respond b (b' -> Proxy a' a b' b m r )- | M (m (Proxy a' a b' b m r))- | Pure r--instance (Monad m) => Functor (Proxy a' a b' b m) where- fmap f p0 = go p0 where- go p = case p of- Request a' fa -> Request a' (\a -> go (fa a ))- Respond b fb' -> Respond b (\b' -> go (fb' b'))- M m -> M (m >>= \p' -> return (go p'))- Pure r -> Pure (f r)--instance (Monad m) => Applicative (Proxy a' a b' b m) where- pure = Pure- pf <*> px = go pf where- go p = case p of- Request a' fa -> Request a' (\a -> go (fa a ))- Respond b fb' -> Respond b (\b' -> go (fb' b'))- M m -> M (m >>= \p' -> return (go p'))- Pure f -> fmap f px--instance (Monad m) => Monad (Proxy a' a b' b m) where- return = Pure- p0 >>= f = go p0 where- go p = case p of- Request a' fa -> Request a' (\a -> go (fa a))- Respond b fb' -> Respond b (\b' -> go (fb' b'))- M m -> M (m >>= \p' -> return (go p'))- Pure r -> f r--instance MonadTrans (Proxy a' a b' b) where- lift = M . liftM Pure--instance (MonadIO m) => MonadIO (Proxy a' a b' b m) where- liftIO = M . liftIO . liftM Pure--instance Channel Proxy where- idT = \a' -> Request a' $ \a -> Respond a idT- k1 <-< k2_0 = \c' -> k1 c' |-< k2_0 where- p1 |-< k2 = case p1 of- Request b' fb -> fb <-| k2 b'- Respond c fc' -> Respond c (\c' -> fc' c' |-< k2)- M m -> M (m >>= \p1' -> return (p1' |-< k2))- Pure r -> Pure r- fb <-| p2 = case p2 of- Request a' fa -> Request a' (\a -> fb <-| fa a) - Respond b fb' -> fb b |-< fb'- M m -> M (m >>= \p2' -> return (fb <-| p2'))- Pure r -> Pure r--instance Interact Proxy where- request a' = Request a' Pure- k1 /</ k2 = \a' -> go (k1 a') where- go p = case p of- Request b' fb -> k2 b' >>= \b -> go (fb b)- Respond x fx' -> Respond x (\x' -> go (fx' x'))- M m -> M (m >>= \p' -> return (go p'))- Pure a -> Pure a- respond a = Respond a Pure- k1 \<\ k2 = \a' -> go (k2 a') where- go p = case p of- Request x' fx -> Request x' (\x -> go (fx x))- Respond b fb' -> k1 b >>= \b' -> go (fb' b')- M m -> M (m >>= \p' -> return (go p'))- Pure a -> Pure a--instance MFunctor (Proxy a' a b' b) where- mapT nat p0 = go p0 where- go p = case p of- Request a' fa -> Request a' (\a -> go (fa a ))- Respond b fb' -> Respond b (\b' -> go (fb' b'))- M m -> M (nat (m >>= \p' -> return (go p')))- Pure r -> Pure r--{-| @Server req resp@ receives requests of type @req@ and sends responses of- type @resp@.-- 'Server's only 'respond' and never 'request' anything. -}-type Server req resp = Proxy C () req resp--{-| @Client req resp@ sends requests of type @req@ and receives responses of- type @resp@.-- 'Client's only 'request' and never 'respond' to anything. -}-type Client req resp = Proxy req resp () C--{-| A self-contained 'Session', ready to be run by 'runSession'-- 'Session's never 'request' anything or 'respond' to anything. -}-type Session = Proxy C () () C--{- $run- I provide two ways to run proxies:-- * 'runProxy', which discards unhandled output from either end-- * 'runSession', which type restricts its argument to ensure no loose ends-- Both functions require that the input to each end is trivially satisfiable,- (i.e. @()@).-- I recommend 'runProxy' for most use cases since it is more convenient.-- 'runSession' only accepts sessions that do not send unhandled data flying- off each end, which provides the following benefits:-- * It prevents against accidental data loss.-- * It protects against silent failures-- * It prevents wastefully draining a scarce resource by gratuitously- driving it to completion-- However, this restriction means that you must either duplicate every utility- function to specialize them to the end-point positions (which I do not do),- or explicitly close loose ends using the 'discard' and 'ignore' proxies:+import Control.Proxy.Class+import Control.Proxy.Synonym+import Control.Proxy.Trans+import Control.Proxy.Trans.Identity+import Control.Proxy.Prelude -> runSession $ discard <-< p <-< ignore+{- $modules+ "Control.Proxy.Class" defines the 'Proxy' type class that lets you program+ generically over proxy implementations and their transformers. - Use the \'@K@\' versions of each command if you are running sessions nested- within sessions. They provide a Kleisli arrow as their result suitable to- be passed to another 'runProxy' / 'runSession' command.--}+ "Control.Proxy.Synonym" defines type synonyms for proxies that don't use all+ of their inputs or outputs, such as 'Pipe's, 'Producer's, and 'Server's. -{-| Run a self-sufficient 'Proxy' Kleisli arrow, converting it back to the base- monad -}-runProxy :: (Monad m) => (() -> Proxy a' () () b m r) -> m r-runProxy k = go (k ()) where- go p = case p of- Request _ fa -> go (fa ())- Respond _ fb' -> go (fb' ())- M m -> m >>= go- Pure r -> return r+ "Control.Proxy.Prelude" provides a standard library of proxies. -{-| Run a self-sufficient 'Proxy' Kleisli arrow, converting it back to a Kleisli- arrow in the base monad -}-runProxyK :: (Monad m) => (() -> Proxy a () () b m r) -> (() -> m r)-runProxyK p = \() -> runProxy p+ "Control.Proxy.Trans" defines the 'ProxyTrans' type class that lets you+ write your own proxy extensions. -{-| Run a self-contained 'Session' Kleisli arrow, converting it back to the base- monad -}-runSession :: (Monad m) => (() -> Session m r) -> m r-runSession = runProxy+ "Control.Proxy.Trans.Identity" exports 'runIdentityP', which substantially+ eases writing completely polymorphic proxies. -{-| Run a self-contained 'Session' Kleisli arrow, converting it back to a- Kleisli arrow in the base monad -}-runSessionK :: (Monad m) => (() -> Session m r) -> (() -> m r)-runSessionK = runProxyK+ "Control.Monad" exports 'forever', ('>=>'), and ('<=<'). -{- $utility- 'discard' provides a fallback client that gratuitously 'request's input- from a server, but discards all responses.+ "Control.Monad.Trans.Class" exports 'lift'. - 'ignore' provides a fallback server that trivially 'respond's with output- to a client, but ignores all request parameters.+ "Control.MFunctor" exports 'hoist'. -}---- | Discard all responses-discard :: (Monad m) => () -> Proxy () a () C m r-discard _ = go where- go = Request () (\_ -> go)---- | Ignore all requests-ignore :: (Monad m) => a -> Proxy C () a () m r-ignore _ = go where- go = Respond () (\_ -> go)
+ Control/Proxy/Core/Correct.hs view
@@ -0,0 +1,186 @@+{-| This module provides the correct proxy implementation which strictly+ enforces the monad transformer laws. You can safely import this module+ without violating any laws or invariants.++ However, I advise that you stick to the 'Proxy' type class API rather than+ import this module so that your code works with both 'Proxy' implementations+ and also works with all proxy transformers. -}++module Control.Proxy.Core.Correct (+ -- * Types+ ProxyCorrect(..),+ ProxyF(..),++ -- * Run Sessions + -- $run+ runProxy,+ runProxyK,+ runPipe+ ) where++import Control.Applicative (Applicative(pure, (<*>)))+import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.MFunctor (MFunctor(hoist))+import Control.Proxy.Class+import Control.Proxy.Synonym (C)++{-| A 'ProxyCorrect' communicates with an upstream interface and a downstream+ interface.++ The type variables of @ProxyCorrect req_a' resp_a req_b' resp_b m r@+ signify:++ * @req_a'@ - The request supplied to the upstream interface++ * @resp_a@ - The response provided by the upstream interface++ * @req_b'@ - The request supplied by the downstream interface++ * @resp_b@ - The response provided to the downstream interface++ * @m @ - The base monad++ * @r @ - The final return value -}+data ProxyCorrect a' a b' b m r =+ Proxy { unProxy :: m (ProxyF a' a b' b r (ProxyCorrect a' a b' b m r)) }++-- | The base functor for the 'ProxyCorrect' type+data ProxyF a' a b' b r x+ = Request a' (a -> x)+ | Respond b (b' -> x)+ | Pure r++instance (Monad m) => Functor (ProxyCorrect a' a b' b m) where+ fmap f p0 = go p0 where+ go p = Proxy (do+ x <- unProxy p+ return (case x of+ Request a' fa -> Request a' (\a -> go (fa a ))+ Respond b fb' -> Respond b (\b' -> go (fb' b'))+ Pure r -> Pure (f r) ) )++instance (Monad m) => Applicative (ProxyCorrect a' a b' b m) where+ pure r = Proxy (return (Pure r))+ pf <*> px = go pf where+ go p = Proxy (do+ x <- unProxy p+ case x of+ Request a' fa -> return (Request a' (\a -> go (fa a )))+ Respond b fb' -> return (Respond b (\b' -> go (fb' b')))+ Pure f -> unProxy (fmap f px) )++instance (Monad m) => Monad (ProxyCorrect a' a b' b m) where+ return = \r -> Proxy (return (Pure r))+ p0 >>= f = go p0 where+ go p = Proxy (do+ x <- unProxy p+ case x of+ Request a' fa -> return (Request a' (\a -> go (fa a )))+ Respond b fb' -> return (Respond b (\b' -> go (fb' b')))+ Pure r -> unProxy (f r) )++instance MonadTrans (ProxyCorrect a' a b' b) where+ lift = lift_P++instance (MonadIO m) => MonadIO (ProxyCorrect a' a b' b m) where+ liftIO m = Proxy (liftIO (m >>= \r -> return (Pure r)))+ -- liftIO = Proxy . liftIO . liftM Pure++instance MonadIOP ProxyCorrect where+ liftIO_P = liftIO++instance Proxy ProxyCorrect where+ fb'_0 >-> fc' = \c' -> fb'_0 >-| fc' c' where+ fb' >-| p1 = Proxy (do+ x <- unProxy p1+ case x of+ Request b' fb -> unProxy (fb' b' |-> fb)+ Respond c fc' -> return (Respond c (\c' -> fb' >-| fc' c'))+ Pure r -> return (Pure r) )+ p2 |-> fb = Proxy (do+ x <- unProxy p2+ case x of+ Request a' fa -> return (Request a' (\a -> fa a |-> fb))+ Respond b fb' -> unProxy (fb' >-| fb b)+ Pure r -> return (Pure r) )++ fa_0 >~> fb_0 = \a -> fa_0 a |-> fb_0 where+ fb' >-| p1 = Proxy (do+ x <- unProxy p1+ case x of+ Request b' fb -> unProxy (fb' b' |-> fb)+ Respond c fc' -> return (Respond c (\c' -> fb' >-| fc' c'))+ Pure r -> return (Pure r) )+ p2 |-> fb = Proxy (do+ x <- unProxy p2+ case x of+ Request a' fa -> return (Request a' (\a -> fa a |-> fb))+ Respond b fb' -> unProxy (fb' >-| fb b)+ Pure r -> return (Pure r) )++ request a' = Proxy (return (Request a' (\a -> Proxy (return (Pure a )))))+ respond b = Proxy (return (Respond b (\b' -> Proxy (return (Pure b')))))++ return_P = return+ (?>=) = (>>=)++ lift_P m = Proxy (m >>= \r -> return (Pure r))++ hoist_P = hoist++instance Interact ProxyCorrect where+ k2 \>\ k1 = \a' -> go (k1 a') where+ go p = Proxy (do+ x <- unProxy p+ case x of+ Request b' fb -> unProxy (k2 b' >>= \b -> go (fb b))+ Respond x fx' -> return (Respond x (\x' -> go (fx' x')))+ Pure a -> return (Pure a) )+ k2 />/ k1 = \a' -> go (k2 a') where+ go p = Proxy (do+ x <- unProxy p+ case x of+ Request x' fx -> return (Request x' (\x -> go (fx x)))+ Respond b fb' -> unProxy (k1 b >>= \b' -> go (fb' b'))+ Pure a -> return (Pure a) )++instance MFunctor (ProxyCorrect a' a b' b) where+ hoist nat p0 = go p0 where+ go p = Proxy (nat (do+ x <- unProxy p+ return (case x of+ Request a' fa -> Request a' (\a -> go (fa a ))+ Respond b fb' -> Respond b (\b' -> go (fb' b'))+ Pure r -> Pure r )))++{- $run+ The following commands run self-sufficient proxies, converting them back to+ the base monad.++ These are the only functions specific to the 'ProxyCorrect' type.+ Everything else programs generically over the 'Proxy' type class.++ Use 'runProxyK' if you are running proxies nested within proxies. It+ provides a Kleisli arrow as its result that you can pass to another+ 'runProxy' / 'runProxyK' command. -}++{-| Run a self-sufficient 'ProxyCorrect' Kleisli arrow, converting it back to+ the base monad -}+runProxy :: (Monad m) => (() -> ProxyCorrect a' () () b m r) -> m r+runProxy k = go (k ()) where+ go p = do+ x <- unProxy p+ case x of+ Request _ fa -> go (fa ())+ Respond _ fb' -> go (fb' ())+ Pure r -> return r++{-| Run a self-sufficient 'ProxyCorrect' Kleisli arrow, converting it back to a+ Kleisli arrow in the base monad -}+runProxyK :: (Monad m) => (() -> ProxyCorrect a' () () b m r) -> (() -> m r)+runProxyK p = \() -> runProxy p++-- | Run the 'Pipe' monad transformer, converting it back to the base monad+runPipe :: (Monad m) => ProxyCorrect a' () () b m r -> m r+runPipe p = runProxy (\_ -> p)
+ Control/Proxy/Core/Fast.hs view
@@ -0,0 +1,238 @@+{-| This is an internal module, meaning that it is unsafe to import unless you+ understand the risks.++ This module provides the fast proxy implementation, which achieves its speed+ by weakening the monad transformer laws. These laws do not hold if you can+ pattern match on the constructors, as the following counter-example+ illustrates:++> lift . return = M . return . Pure+>+> return = Pure+>+> lift . return /= return++ These laws only hold when viewed through certain safe observation functions,+ like 'runProxy' and 'observe'.++ Also, you really should not use the constructors anyway, let alone the+ concrete type and instead you should stick to the 'Proxy' type class API.+ This not only ensures that your code does not violate the monad transformer+ laws, but also guarantees that it works with the other proxy implementations+ and with any proxy transformers. -}++module Control.Proxy.Core.Fast (+ -- * Types+ ProxyFast(..),++ -- * Run Sessions + -- $run+ runProxy,+ runProxyK,+ runPipe,++ -- * Safety+ observe+ ) where++import Control.Applicative (Applicative(pure, (<*>)))+-- import Control.Monad (ap, forever, liftM, (>=>))+import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.MFunctor (MFunctor(hoist))+import Control.Proxy.Class+import Control.Proxy.Synonym (C)++{-| A 'ProxyFast' communicates with an upstream interface and a downstream+ interface.++ The type variables of @ProxyFast req_a' resp_a req_b' resp_b m r@ signify:++ * @req_a'@ - The request supplied to the upstream interface++ * @resp_a@ - The response provided by the upstream interface++ * @req_b'@ - The request supplied by the downstream interface++ * @resp_b@ - The response provided to the downstream interface++ * @m @ - The base monad++ * @r @ - The final return value -}+data ProxyFast a' a b' b m r+ = Request a' (a -> ProxyFast a' a b' b m r )+ | Respond b (b' -> ProxyFast a' a b' b m r )+ | M (m (ProxyFast a' a b' b m r))+ | Pure r++instance (Monad m) => Functor (ProxyFast a' a b' b m) where+ fmap f p0 = go p0 where+ go p = case p of+ Request a' fa -> Request a' (\a -> go (fa a ))+ Respond b fb' -> Respond b (\b' -> go (fb' b'))+ M m -> M (m >>= \p' -> return (go p'))+ Pure r -> Pure (f r)++instance (Monad m) => Applicative (ProxyFast a' a b' b m) where+ pure = Pure+ pf <*> px = go pf where+ go p = case p of+ Request a' fa -> Request a' (\a -> go (fa a ))+ Respond b fb' -> Respond b (\b' -> go (fb' b'))+ M m -> M (m >>= \p' -> return (go p'))+ Pure f -> fmap f px++instance (Monad m) => Monad (ProxyFast a' a b' b m) where+ return = Pure+ (>>=) = _bind++_bind+ :: (Monad m)+ => ProxyFast a' a b' b m r+ -> (r -> ProxyFast a' a b' b m r')+ -> ProxyFast a' a b' b m r'+p0 `_bind` f = go p0 where+ go p = case p of+ Request a' fa -> Request a' (\a -> go (fa a))+ Respond b fb' -> Respond b (\b' -> go (fb' b'))+ M m -> M (m >>= \p' -> return (go p'))+ Pure r -> f r++-- | Only satisfies laws modulo 'observe'+instance MonadTrans (ProxyFast a' a b' b) where+ lift = _lift++_lift :: (Monad m) => m r -> ProxyFast a' a b' b m r+_lift m = M (m >>= \r -> return (Pure r))+-- _lift = M . liftM Pure++{- These never fire, for some reason, but keep them until I figure out how to+ get them to work. -}+{-# RULES+ "_lift m ?>= f" forall m f .+ _bind (_lift m) f = M (m >>= \r -> return (f r))+ #-}++instance (MonadIO m) => MonadIO (ProxyFast a' a b' b m) where+ liftIO m = M (liftIO (m >>= \r -> return (Pure r)))+ -- liftIO = M . liftIO . liftM Pure++instance MonadIOP ProxyFast where+ liftIO_P = liftIO++instance Proxy ProxyFast where+ fb'_0 >-> fc'_0 = \c' -> fb'_0 >-| fc'_0 c' where+ p1 |-> fb = case p1 of+ Request a' fa -> Request a' (\a -> fa a |-> fb)+ Respond b fb' -> fb' >-| fb b+ M m -> M (m >>= \p1' -> return (p1' |-> fb))+ Pure r -> Pure r+ fb' >-| p2 = case p2 of+ Request b' fb -> fb' b' |-> fb+ Respond c fc' -> Respond c (\c' -> fb' >-| fc' c')+ M m -> M (m >>= \p2' -> return (fb' >-| p2'))+ Pure r -> Pure r++ fa_0 >~> fb_0 = \a -> fa_0 a |-> fb_0 where+ p1 |-> fb = case p1 of+ Request a' fa -> Request a' (\a -> fa a |-> fb)+ Respond b fb' -> fb' >-| fb b+ M m -> M (m >>= \p1' -> return (p1' |-> fb))+ Pure r -> Pure r+ fb' >-| p2 = case p2 of+ Request b' fb -> fb' b' |-> fb+ Respond c fc' -> Respond c (\c' -> fb' >-| fc' c')+ M m -> M (m >>= \p2' -> return (fb' >-| p2'))+ Pure r -> Pure r++ request a' = Request a' Pure+ respond b = Respond b Pure++ return_P = return+ (?>=) = _bind++ lift_P = _lift++ hoist_P = hoist++{-# RULES+ "_bind (Request a' Pure) f" forall a' f .+ _bind (Request a' Pure) f = Request a' f;+ "_bind (Respond b Pure) f" forall b f .+ _bind (Respond b Pure) f = Respond b f+ #-}++instance Interact ProxyFast where+ k2 \>\ k1 = \a' -> go (k1 a') where+ go p = case p of+ Request b' fb -> k2 b' >>= \b -> go (fb b)+ Respond x fx' -> Respond x (\x' -> go (fx' x'))+ M m -> M (m >>= \p' -> return (go p'))+ Pure a -> Pure a+ k2 />/ k1 = \a' -> go (k2 a') where+ go p = case p of+ Request x' fx -> Request x' (\x -> go (fx x))+ Respond b fb' -> k1 b >>= \b' -> go (fb' b')+ M m -> M (m >>= \p' -> return (go p'))+ Pure a -> Pure a++instance MFunctor (ProxyFast a' a b' b) where+ hoist nat p0 = go (observe p0) where+ go p = case p of+ Request a' fa -> Request a' (\a -> go (fa a ))+ Respond b fb' -> Respond b (\b' -> go (fb' b'))+ M m -> M (nat (m >>= \p' -> return (go p')))+ Pure r -> Pure r++{- $run+ The following commands run self-sufficient proxies, converting them back to+ the base monad.++ These are the only functions specific to the 'ProxyFast' type. Everything+ else programs generically over the 'Proxy' type class.++ Use 'runProxyK' if you are running proxies nested within proxies. It+ provides a Kleisli arrow as its result that you can pass to another+ 'runProxy' / 'runProxyK' command. -}++{-| Run a self-sufficient 'ProxyFast' Kleisli arrow, converting it back to the+ base monad -}+runProxy :: (Monad m) => (() -> ProxyFast a' () () b m r) -> m r+runProxy k = go (k ()) where+ go p = case p of+ Request _ fa -> go (fa ())+ Respond _ fb' -> go (fb' ())+ M m -> m >>= go+ Pure r -> return r++{-| Run a self-sufficient 'ProxyFast' Kleisli arrow, converting it back to a+ Kleisli arrow in the base monad -}+runProxyK :: (Monad m) => (() -> ProxyFast a' () () b m r) -> (() -> m r)+runProxyK p = \() -> runProxy p++-- | Run the 'Pipe' monad transformer, converting it back to the base monad+runPipe :: (Monad m) => ProxyFast a' () () b m r -> m r+runPipe p = runProxy (\_ -> p)++{-| The monad transformer laws are correct when viewed through the 'observe'+ function:++> observe (lift (return r)) = observe (return r)+>+> observe (lift (m >>= f)) = observe (lift m >>= lift . f)++ This correctness comes at a moderate cost to performance, so use this+ function sparingly or else you would be better off using+ "Control.Proxy.Core.Correct".++ You do not need to use this function if you use the safe API exported from+ "Control.Proxy", which does not export any functions or constructors that+ can violate the monad transformer laws.+-}+observe :: (Monad m) => ProxyFast a' a b' b m r -> ProxyFast a' a b' b m r+observe p = M (go p) where+ go p = case p of+ M m' -> m' >>= go+ Pure r -> return (Pure r)+ Request a' fa -> return (Request a' (\a -> observe (fa a )))+ Respond b fb' -> return (Respond b (\b' -> observe (fb' b')))
Control/Proxy/Pipe.hs view
@@ -1,90 +1,197 @@-{-| This module provides an API compatible with "Control.Pipe"+{-# LANGUAGE KindSignatures #-} - Consult "Control.Pipe.Core" for more extensive documentation and- "Control.Pipe.Tutorial" for an extended tutorial. -}+{-| This module provides an API similar to "Control.Pipe" for those who prefer+ the classic 'Pipe' API. + This module differs slightly from "Control.Pipe" in order to promote+ seamless interoperability with both pipes and proxies. See the \"Upgrade+ Pipes to Proxies\" section below for details. -} module Control.Proxy.Pipe (- -- * Types- Pipe,- Producer,- Consumer,- Pipeline, -- * Create Pipes await, yield, pipe,+ -- * Compose Pipes (<+<), (>+>), idP,- -- * Run Pipes- runPipe- ) where -import Control.Proxy.Core-import Control.Proxy.Class-import Data.Closed (C)--{-| The type variables of @Pipe a b m r@ signify:-- * @a@ - The type of input received from upstream pipes-- * @b@ - The type of output delivered to downstream pipes-- * @m@ - The base monad-- * @r@ - The type of the return value -}-type Pipe a b = Proxy () a () b+ -- * Synonyms+ Pipeline, --- | A pipe that produces values-type Producer b = Pipe () b+ -- * Run Pipes+ -- $run --- | A pipe that consumes values-type Consumer a = Pipe a C+ -- * Upgrade Pipes to Proxies+ -- $upgrade+ ) where --- | A self-contained pipeline that is ready to be run-type Pipeline = Pipe () C+import Control.Monad (forever)+import Control.Proxy.Class (Proxy(request, respond, (>->), (?>=)))+import Control.Proxy.Synonym (Pipe, Consumer, Producer, C)+import Control.Proxy.Trans.Identity (runIdentityP) {-| Wait for input from upstream - 'await' blocks until input is available -}-await :: (Monad m) => Pipe a b m a+ 'await' blocks until input is available from upstream. -}+await :: (Monad m, Proxy p) => Pipe p a b m a await = request ()-{-# INLINE await #-} --- | Convert a pure function into a pipe-pipe :: (Monad m) => (a -> b) -> Pipe a b m r-pipe f = go where- go = Request () (\a -> Respond (f a) (\() -> go))- {-| Deliver output downstream - 'yield' restores control back downstream and binds the result to 'await'. -}-yield :: (Monad m) => b -> Pipe a b m ()-yield = respond-{-# INLINE yield #-}+ 'yield' restores control back downstream and binds its value to 'await'. -}+yield :: (Monad m, Proxy p) => b -> p a' a b' b m ()+yield b = runIdentityP $ do+ respond b+ return () +-- | Convert a pure function into a pipe+pipe :: (Monad m, Proxy p) => (a -> b) -> Pipe p a b m r+pipe f = runIdentityP $ forever $ do+ a <- request ()+ respond (f a)+ infixr 9 <+< infixl 9 >+> -- | Corresponds to ('<<<')/('.') from @Control.Category@-(<+<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r-p1 <+< p2 = ((\() -> p1) <-< (\() -> p2)) ()+(<+<)+ :: (Monad m, Proxy p) => Pipe p b c m r -> Pipe p a b m r -> Pipe p a c m r+p1 <+< p2 = p2 >+> p1 -- | Corresponds to ('>>>') from @Control.Category@-(>+>) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r-(>+>) = flip (<+<)+(>+>)+ :: (Monad m, Proxy p) => Pipe p a b m r -> Pipe p b c m r -> Pipe p a c m r+p1 >+> p2 = ((\() -> p1) >-> (\() -> p2)) () -- | Corresponds to 'id' from @Control.Category@-idP :: (Monad m) => Pipe a a m r-idP = go where- go = Request () (\a -> Respond a (\() -> go))+idP :: (Monad m, Proxy p) => Pipe p a a m r+idP = runIdentityP $ forever $ do+ a <- request ()+ respond a --- | Run the 'Pipe' monad transformer, converting it back to the base monad-runPipe :: (Monad m) => Pipeline m r -> m r-runPipe p' = go p' where- go p = case p of- Request _ fa -> go (fa ())- Respond _ fb' -> go (fb' ())- M m -> m >>= go- Pure r -> return r+{-| A self-contained 'Pipeline' that is ready to be run++ 'Pipeline's never 'request' nor 'respond'. -}+type Pipeline (p :: * -> * -> * -> * -> (* -> *) -> * -> *) = p C () () C++{- $run+ The "Control.Proxy.Core.Fast" and "Control.Proxy.Core.Correct" modules+ provide their corresponding 'runPipe' functions, specialized to their own+ 'Proxy' implementations.++ Each implementation must supply its own 'runPipe' function since it is+ the only non-polymorphic 'Pipe' function and the compiler uses it to+ select which underlying proxy implementation to use. -}++{- $upgrade+ You can upgrade classic 'Pipe' code to work with the proxy ecosystem in+ steps. Each change enables greater interoperability with proxy utilities+ and transformers and if time permits you should implement the entire upgrade+ for your libraries if you want to take advantage of proxy standard+ libraries.++ First, import "Control.Proxy" and "Control.Proxy.Pipe" instead of+ "Control.Pipe". Then, add 'ProxyFast' after every 'Pipe', 'Producer', or+ 'Consumer' in any type signature. For example, you would convert this:++> import Control.Pipe+>+> fromList :: (Monad m) => [b] -> Producer b m ()+> fromList xs = mapM_ yield xs++ ... to this:++> import Control.Proxy+> import Control.Proxy.Pipe -- transition import+>+> fromList :: (Monad m) => [b] -> Producer ProxyFast b m ()+> fromList xs = mapM_ yield xs++ The change ensures that all your code now works in the 'ProxyFast' monad,+ which is the faster of the two proxy implementations.++ Second, modify all your 'Pipe's to take an empty '()' as their final+ argument, and translate the following functions:++ * ('<+<') to ('<-<')++ * 'runPipe' to 'runProxy'++ For example, you would convert this:++> import Control.Proxy+> import Control.Proxy.Pipe+>+> fromList :: (Monad m) => [b] -> Producer ProxyFast b m ()+> fromList xs = mapM_ yield xs++ ... to this:++> import Control.Proxy+> import Control.Proxy.Pipe+>+> fromList :: (Monad m) => [b] -> () -> Producer ProxyFast b m ()+> fromList xs () = mapM_ yield xs++ Now when you call these within a @do@ block you must supplying an+ additional @()@ argument:++> examplePipe () = do+> a <- request ()+> fromList [1..a] ()++ This change lets you switch from pipe composition, ('<+<'), to proxy+ composition, ('<-<'), so that you can mix proxy utilities with pipes.++ Third, wrap your pipe's implementation in 'runIdentityP' (which+ "Control.Proxy" exports):++> import Control.Proxy+> import Control.Proxy.Pipe+>+> fromList xs () = runIdentityP $ mapM_ yield xs++ Then replace the 'ProxyFast' in the type signature with a type variable @p@+ constrained by the 'Proxy' type class:++> fromList :: (Monad m, Proxy p) => [b] -> () -> Producer p b m ()++ This change upgrades your 'Pipe' to work natively within proxies and proxy+ transformers, without any manual conversion or lifting. You can now compose+ or sequence your 'Pipe' within any feature set transparently.++ Finally, replace each 'await' with @request ()@ and each 'yield' with+ 'respond'. Also, replace every 'Pipeline' with 'Session'. This lets you+ drop the "Control.Proxy.Pipe" import:++> import Control.Proxy+>+> fromList :: (Monad m, Proxy p) => [b] -> () -> Producer p b m ()+> fromList xs () = runIdentityP $ mapM_ respond xs++ Also, I encourage you to continue using the 'Pipe', 'Consumer' and+ 'Producer' type synonyms to simplify type signatures. The following+ examples show how they cleanly mix with proxies and their extensions:++> import Control.Proxy+> import Control.Proxy.Trans.Either as E+> import Control.Proxy.Trans.State+>+> -- A Producer enriched with pipe-local state+> example1 :: (Monad m, Proxy p) => () -> Producer (StateP Int p) Int m r+> example1 () = forever $ do+> n <- get+> respond n+> put (n + 1)+>+> -- A Consumer enriched with error-handling+> example2 :: (Proxy p) => () -> Consumer (EitherP String p) Int IO ()+> example2 () = do+> n <- request ()+> if (n == 0)+> then E.throw "Error: received 0"+> else lift $ print n++-}
Control/Proxy/Prelude.hs view
@@ -1,6 +1,7 @@ -- | Entry point for the Control.Proxy.Prelude hierarchy module Control.Proxy.Prelude (+ -- * Modules -- $modules module Control.Proxy.Prelude.Base, module Control.Proxy.Prelude.IO,@@ -17,5 +18,4 @@ "Control.Proxy.Prelude.IO" provides proxies for simple 'IO'. "Control.Proxy.Prelude.Kleisli" provides convenience functions for working- with Kleisli arrows.--}+ with Kleisli arrows. -}
Control/Proxy/Prelude/Base.hs view
@@ -2,15 +2,19 @@ module Control.Proxy.Prelude.Base ( -- * Maps- mapB, mapD, mapU,- mapMB,+ mapB, mapMD, mapMU,- execB,+ mapMB,+ useD,+ useU,+ useB, execD, execU,+ execB,+ -- * Filters takeB, takeB_,@@ -22,45 +26,94 @@ dropWhileU, filterD, filterU,+ -- * Lists fromListS, fromListC,+ -- * Enumerations enumFromS, enumFromC, enumFromToS,- enumFromToC- ) where+ enumFromToC, -import Control.Monad (replicateM_, void, when, (>=>))-import Control.Monad.Trans.Class (lift)-import Control.Proxy.Class (request, respond, idT)-import Control.Proxy.Core (Proxy(..), Server, Client)-import Control.Proxy.Prelude.Kleisli (foreverK, replicateK)+ -- * Folds+ foldD,+ foldU,+ allD,+ allU,+ allD_,+ allU_,+ anyD,+ anyU,+ anyD_,+ anyU_,+ sumD,+ sumU,+ productD,+ productU,+ lengthD,+ lengthU,+ headD,+ headD_,+ headU,+ headU_,+ lastD,+ lastU,+ toListD,+ toListU,+ foldrD,+ foldrU,+ foldlD',+ foldlU', -{-| @(mapB f g)@ applies @f@ to all values going downstream and @g@ to all- values going upstream.+ -- * Zips and Merges+ zipD,+ mergeD, - Mnemonic: map \'@B@\'idirectional+ -- * Closed Adapters+ -- $open+ unitD,+ unitU, -> mapB f1 g1 >-> mapB f2 g2 = mapB (f2 . f1) (g1 . g2)->-> mapB id id = idT--}-mapB :: (Monad m) => (a -> b) -> (b' -> a') -> b' -> Proxy a' a b' b m r-mapB f g = go where- go b' = Request (g b') (\a -> Respond (f a) go)--- mapB f g = foreverK $ request . g >=> respond . f+ -- * Modules+ -- $modules+ module Control.Monad.Trans.State.Strict,+ module Control.Monad.Trans.Writer.Strict,+ module Data.Monoid+ ) where +import Control.MFunctor (hoist)+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Writer.Strict (+ WriterT(runWriterT), execWriterT, runWriter, tell )+import Control.Monad.Trans.State.Strict (+ StateT(runStateT), execStateT, runState, execState, get, put )+import Control.Proxy.Class+import Control.Proxy.Synonym+import Control.Proxy.Trans.Identity (runIdentityP, runIdentityK)+import Data.Monoid (+ Monoid,+ Endo(Endo, appEndo),+ All(All, getAll),+ Any(Any, getAny),+ Sum(Sum, getSum),+ Product(Product, getProduct),+ First(First, getFirst),+ Last(Last, getLast) )+ {-| @(mapD f)@ applies @f@ to all values going \'@D@\'ownstream. > mapD f1 >-> mapD f2 = mapD (f2 . f1) > > mapD id = idT -}-mapD :: (Monad m) => (a -> b) -> x -> Proxy x a x b m r-mapD f = go where- go x = Request x (\a -> Respond (f a) go)+mapD :: (Monad m, Proxy p) => (a -> b) -> x -> p x a x b m r+mapD f = runIdentityK go where+ go x = do+ a <- request x+ x2 <- respond (f a)+ go x2 -- mapD f = foreverK $ request >=> respond . f {-| @(mapU g)@ applies @g@ to all values going \'@U@\'pstream.@@ -69,26 +122,30 @@ > > mapU id = idT -}-mapU :: (Monad m) => (b' -> a') -> b' -> Proxy a' x b' x m r-mapU g = go where- go b' = Request (g b') (\x -> Respond x go)+mapU :: (Monad m, Proxy p) => (b' -> a') -> b' -> p a' x b' x m r+mapU g = runIdentityK go where+ go b' = do+ x <- request (g b')+ b'2 <- respond x+ go b'2 -- mapU g = foreverK $ (request . g) >=> respond -{-| @(mapMB f g)@ applies the monadic function @f@ to all values going- downstream and the monadic function @g@ to all values going upstream.+{-| @(mapB f g)@ applies @f@ to all values going downstream and @g@ to all+ values going upstream. -> mapMB f1 g1 >-> mapMB f2 g2 = mapMB (f1 >=> f2) (g2 >=> g1)+ Mnemonic: map \'@B@\'idirectional++> mapB f1 g1 >-> mapB f2 g2 = mapB (f2 . f1) (g1 . g2) >-> mapMB return return = idT+> mapB id id = idT -}-mapMB :: (Monad m) => (a -> m b) -> (b' -> m a') -> b' -> Proxy a' a b' b m r-mapMB f g = go where- go b' =- M (g b' >>= \a' -> return (- Request a' (\a ->- M (f a >>= \b -> return (- Respond b go )))))--- mapMB f g = foreverK $ lift . g >=> request >=> lift . f >=> respond+mapB :: (Monad m, Proxy p) => (a -> b) -> (b' -> a') -> b' -> p a' a b' b m r+mapB f g = runIdentityK go where+ go b' = do+ a <- request (g b')+ b'2 <- respond (f a )+ go b'2+-- mapB f g = foreverK $ request . g >=> respond . f {-| @(mapMD f)@ applies the monadic function @f@ to all values going downstream @@ -96,13 +153,14 @@ > > mapMD return = idT -}-mapMD :: (Monad m) => (a -> m b) -> x -> Proxy x a x b m r-mapMD f = go where- go x =- Request x (\a ->- M (f a >>= \b -> return (- Respond b go )))--- mapMDf = foreverK $ request >=> lift . f >=> respond+mapMD :: (Monad m, Proxy p) => (a -> m b) -> x -> p x a x b m r+mapMD f = runIdentityK go where+ go x = do+ a <- request x+ b <- lift (f a)+ x2 <- respond b+ go x2+-- mapMD f = foreverK $ request >=> lift . f >=> respond {-| @(mapMU g)@ applies the monadic function @g@ to all values going upstream @@ -110,92 +168,166 @@ > > mapMU return = idT -}-mapMU :: (Monad m) => (b' -> m a') -> b' -> Proxy a' x b' x m r-mapMU g = go where- go b' =- M (g b' >>= \a' -> return (- Request a' (\x ->- Respond x go )))+mapMU :: (Monad m, Proxy p) => (b' -> m a') -> b' -> p a' x b' x m r+mapMU g = runIdentityK go where+ go b' = do+ a' <- lift (g b')+ x <- request a'+ b'2 <- respond x+ go b'2 -- mapMU g = foreverK $ lift . g >=> request >=> respond -{-| @(execB md mu)@ executes @mu@ every time values flow upstream through it,- and executes @md@ every time values flow downstream through it.+{-| @(mapMB f g)@ applies the monadic function @f@ to all values going+ downstream and the monadic function @g@ to all values going upstream. -> execB md1 mu1 >-> execB md2 mu2 = execB (md1 >> md2) (mu2 >> mu1)+> mapMB f1 g1 >-> mapMB f2 g2 = mapMB (f1 >=> f2) (g2 >=> g1) >-> execB (return ()) = idT+> mapMB return return = idT -}-execB :: (Monad m) => m () -> m () -> a' -> Proxy a' a a' a m r-execB md mu = go where- go a' =- M (mu >>= \_ -> return (- Request a' (\a ->- M (md >>= \_ -> return (- Respond a go )))))-{- execB md mu = foreverK $ \a' -> do- lift mu- a <- request a'- lift md- respond a -}+mapMB+ :: (Monad m, Proxy p) => (a -> m b) -> (b' -> m a') -> b' -> p a' a b' b m r+mapMB f g = runIdentityK go where+ go b' = do+ a' <- lift (g b')+ a <- request a'+ b <- lift (f a )+ b'2 <- respond b+ go b'2+-- mapMB f g = foreverK $ lift . g >=> request >=> lift . f >=> respond -{-| @execD md)@ executes @md@ every time values flow downstream through it.+{-| @(useD f)@ executes the monadic function @f@ on all values flowing+ \'@D@\'ownstream +> useD f1 >-> useD f2 = useD (\a -> f1 a >> f2 a)+>+> useD (\_ -> return ()) = idT+-}+useD :: (Monad m, Proxy p) => (a -> m r1) -> x -> p x a x a m r+useD f = runIdentityK go where+ go x = do+ a <- request x+ lift $ f a+ x2 <- respond a+ go x2++{-| @(useU g)@ executes the monadic function @g@ on all values flowing+ \'@U@\'pstream++> useU g1 >-> useU g2 = useU (\a' -> g2 a' >> g1 a')+>+> useU (\_ -> return ()) = idT+-}+useU :: (Monad m, Proxy p) => (a' -> m r2) -> a' -> p a' x a' x m r+useU g = runIdentityK go where+ go a' = do+ lift $ g a'+ x <- request a'+ a'2 <- respond x+ go a'2++{-| @(useB f g)@ executes the monadic function @f@ on all values flowing+ downstream and the monadic function @g@ on all values flowing upstream++> useB f1 g1 >-> useB f2 g2 = useB (\a -> f1 a >> f2 a) (\a' -> g2 a' >> g1 a')+>+> useB (\_ -> return ()) (\_ -> return ()) = idT+-}+useB+ :: (Monad m, Proxy p) => (a -> m r1) -> (a' -> m r2) -> a' -> p a' a a' a m r+useB f g = runIdentityK go where+ go a' = do+ lift $ g a'+ a <- request a'+ lift $ f a+ a'2 <- respond a+ go a'2++{-| @(execD md)@ executes @md@ every time values flow downstream through it.+ > execD md1 >-> execD md2 = execD (md1 >> md2) > > execD (return ()) = idT -}-execD :: (Monad m) => m () -> a' -> Proxy a' a a' a m r-execD md = go where- go a' =- Request a' (\a ->- M (md >>= \_ -> return (- Respond a go )))+execD :: (Monad m, Proxy p) => m r1 -> a' -> p a' a a' a m r+execD md = runIdentityK go where+ go a' = do+ a <- request a'+ lift md+ a'2 <- respond a+ go a'2 {- execD md = foreverK $ \a' -> do a <- request a' lift md respond a -} -{-| @execU mu)@ executes @mu@ every time values flow upstream through it.+{-| @(execU mu)@ executes @mu@ every time values flow upstream through it. > execU mu1 >-> execU mu2 = execU (mu2 >> mu1) > > execU (return ()) = idT -}-execU :: (Monad m) => m () -> a' -> Proxy a' a a' a m r-execU mu = go where- go a' =- M (mu >>= \_ -> return (- Request a' (\a ->- Respond a go )))+execU :: (Monad m, Proxy p) => m r2 -> a' -> p a' a a' a m r+execU mu = runIdentityK go where+ go a' = do+ lift mu+ a <- request a'+ a'2 <- respond a+ go a'2 {- execU mu = foreverK $ \a' -> do lift mu a <- request a' respond a -} +{-| @(execB md mu)@ executes @mu@ every time values flow upstream through it,+ and executes @md@ every time values flow downstream through it.++> execB md1 mu1 >-> execB md2 mu2 = execB (md1 >> md2) (mu2 >> mu1)+>+> execB (return ()) = idT+-}+execB :: (Monad m, Proxy p) => m r1 -> m r2 -> a' -> p a' a a' a m r+execB md mu = runIdentityK go where+ go a' = do+ lift mu+ a <- request a'+ lift md+ a'2 <- respond a+ go a'2+{- execB md mu = foreverK $ \a' -> do+ lift mu+ a <- request a'+ lift md+ respond a -}+ {-| @(takeB n)@ allows @n@ upstream/downstream roundtrips to pass through > takeB n1 >=> takeB n2 = takeB (n1 + n2) -- n1 >= 0 && n2 >= 0 > > takeB 0 = return -}-takeB :: (Monad m) => Int -> a' -> Proxy a' a a' a m a'-takeB n0 = go n0 where+takeB :: (Monad m, Proxy p) => Int -> a' -> p a' a a' a m a'+takeB n0 = runIdentityK (go n0) where go n- | n <= 0 = Pure- | otherwise = \a' -> Request a' (\a -> Respond a (go (n - 1)))+ | n <= 0 = return+ | otherwise = \a' -> do+ a <- request a'+ a'2 <- respond a+ go (n - 1) a'2 -- takeB n = replicateK n $ request >=> respond -- | 'takeB_' is 'takeB' with a @()@ return value, convenient for composing-takeB_ :: (Monad m) => Int -> a' -> Proxy a' a a' a m ()-takeB_ n0 = go n0 where+takeB_ :: (Monad m, Proxy p) => Int -> a' -> p a' a a' a m ()+takeB_ n0 = runIdentityK (go n0) where go n- | n <= 0 = \_ -> Pure ()- | otherwise = \a' -> Request a' (\a -> Respond a (go (n - 1)))- + | n <= 0 = \_ -> return ()+ | otherwise = \a' -> do+ a <- request a'+ a'2 <- respond a+ go (n - 1) a'2 -- takeB_ n = fmap void (takeB n) -{-| @takeWhileD p@ allows values to pass downstream so long as they satisfy the- predicate @p@.+{-| @(takeWhileD p)@ allows values to pass downstream so long as they satisfy+ the predicate @p@. > -- Using the "All" monoid over functions: > mempty = \_ -> True@@ -205,41 +337,32 @@ > > takeWhileD mempty = idT -}-takeWhileD :: (Monad m) => (a -> Bool) -> a' -> Proxy a' a a' a m ()-takeWhileD p = go where- go a' =- Request a' (\a ->- if (p a)- then Respond a go- else Pure () )-{- go a' = do+takeWhileD :: (Monad m, Proxy p) => (a -> Bool) -> a' -> p a' a a' a m ()+takeWhileD p = runIdentityK go where+ go a' = do a <- request a' if (p a)- then do- a'2 <- respond a- go a'2- else return () -}+ then do+ a'2 <- respond a+ go a'2+ else return () -{-| @takeWhileU p@ allows values to pass upstream so long as they satisfy the+{-| @(takeWhileU p)@ allows values to pass upstream so long as they satisfy the predicate @p@. > takeWhileU p1 >-> takeWhileU p2 = takeWhileU (p1 <> p2) > > takeWhileD mempty = idT -}-takeWhileU :: (Monad m) => (a' -> Bool) -> a' -> Proxy a' a a' a m ()-takeWhileU p = go where+takeWhileU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' a a' a m ()+takeWhileU p = runIdentityK go where go a' = if (p a')- then Request a' (\a -> Respond a go)- else Pure ()-{- go a' =- if (p a')- then do- a <- request a'- a'2 <- respond a- go a'2- else return () -}+ then do+ a <- request a'+ a'2 <- respond a+ go a'2+ else return_P () {-| @(dropD n)@ discards @n@ values going downstream @@ -247,11 +370,13 @@ > > dropD 0 = idT -}-dropD :: (Monad m) => Int -> () -> Proxy () a () a m r-dropD n0 = \() -> go n0 where+dropD :: (Monad m, Proxy p) => Int -> () -> Pipe p a a m r+dropD n0 = \() -> runIdentityP (go n0) where go n | n <= 0 = idT ()- | otherwise = Request () (\_ -> go (n - 1))+ | otherwise = do+ request ()+ go (n - 1) {- dropD n () = do replicateM_ n $ request () idT () -}@@ -262,21 +387,15 @@ > > dropU 0 = idT -}-dropU :: (Monad m) => Int -> a' -> Proxy a' () a' () m r-dropU n0- | n0 <= 0 = idT- | otherwise = go (n0 - 1) where- go n- | n <= 0 = \_ -> Respond () idT- | otherwise = \_ -> Respond () (go (n - 1))-{- dropU n a'- | n <= 0 = idT a'- | otherwise = do- replicateM_ (n - 1) $ respond ()- a'2 <- respond ()- idT a'2 -}+dropU :: (Monad m, Proxy p) => Int -> a' -> CoPipe p a' a' m r+dropU n0 = runIdentityK (go n0) where+ go n+ | n <= 0 = idT+ | otherwise = \_ -> do+ a' <- respond ()+ go (n - 1) a' -{-| @(dropWhileD p)@ discards values going upstream until one violates the+{-| @(dropWhileD p)@ discards values going downstream until one violates the predicate @p@. > -- Using the "Any" monoid over functions:@@ -287,33 +406,31 @@ > > dropWhileD mempty = idT -}-dropWhileD :: (Monad m) => (a -> Bool) -> () -> Proxy () a () a m r-dropWhileD p () = go where- go = Request () (\a -> if (p a) then go else Respond a idT)-{- go = do+dropWhileD :: (Monad m, Proxy p) => (a -> Bool) -> () -> Pipe p a a m r+dropWhileD p () = runIdentityP go where+ go = do a <- request () if (p a)- then go- else do- respond a- idT () -}+ then go+ else do+ x <- respond a+ idT x -{-| @(dropWhileU p)@ discards values going downstream until one violates the+{-| @(dropWhileU p)@ discards values going upstream until one violates the predicate @p@. > dropWhileU p1 >-> dropWhileU p2 = dropWhileU (p1 <> p2) > > dropWhileU mempty = idT -}-dropWhileU :: (Monad m) => (a' -> Bool) -> a' -> Proxy a' () a' () m r-dropWhileU p = go where- go a' = if (p a') then Respond () go else idT a'-{- go a' =+dropWhileU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> CoPipe p a' a' m r+dropWhileU p = runIdentityK go where+ go a' = if (p a')- then do- a'2 <- respond ()- go a'2- else idT a' -}+ then do+ a2 <- respond ()+ go a2+ else idT a' {-| @(filterD p)@ discards values going downstream if they fail the predicate @p@@@ -326,13 +443,15 @@ > > filterD mempty = idT -}-filterD :: (Monad m) => (a -> Bool) -> () -> Proxy () a () a m r-filterD p = \() -> go where- go = Request () (\a -> if (p a) then Respond a (\_ -> go) else go)-{- go = do+filterD :: (Monad m, Proxy p) => (a -> Bool) -> () -> Pipe p a a m r+filterD p = \() -> runIdentityP go where+ go = do a <- request ()- when (p a) $ respond a- go -}+ if (p a)+ then do+ respond a+ go+ else go {-| @(filterU p)@ discards values going upstream if they fail the predicate @p@ @@ -340,65 +459,346 @@ > > filterU mempty = idT -}-filterU :: (Monad m) => (a' -> Bool) -> a' -> Proxy a' () a' () m r-filterU p a'0 = go a'0 where+filterU :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> CoPipe p a' a' m r+filterU p = runIdentityK go where go a' = if (p a')- then Request a' (\_ -> Respond () go)- else Respond () go-{- go a' = do- when (p a') $ request a'- a'2 <- respond ()- go a'2 -}+ then do+ request a'+ a'2 <- respond ()+ go a'2+ else do+ a'2 <- respond ()+ go a'2 -{-| Convert a list into a 'Server'+{-| Convert a list into a 'Producer' > fromListS xs >=> fromListS ys = fromListS (xs ++ ys) > > fromListS [] = return -}-fromListS :: (Monad m) => [a] -> () -> Proxy x' x () a m ()-fromListS xs = \_ -> foldr (\e a -> Respond e (\_ -> a)) (Pure ()) xs-{-# INLINE fromListS #-}+fromListS :: (Monad m, Proxy p) => [b] -> () -> Producer p b m ()+fromListS xs = \_ -> foldr (\e a -> respond e ?>= \_ -> a) (return_P ()) xs -- fromListS xs _ = mapM_ respond xs -{-| Convert a list into a 'Client'+{-| Convert a list into a 'CoProducer' > fromListC xs >=> fromListC ys = fromListC (xs ++ ys) > > fromListC [] = return -}-fromListC :: (Monad m) => [a] -> () -> Proxy a x () y m ()-fromListC xs = \_ -> foldr (\e a -> Request e (\_ -> a)) (Pure ()) xs-{-# INLINE fromListC #-}+fromListC :: (Monad m, Proxy p) => [a'] -> () -> CoProducer p a' m ()+fromListC xs = \_ -> foldr (\e a -> request e ?>= \_ -> a) (return_P ()) xs -- fromListC xs _ = mapM_ request xs --- | 'Server' version of 'enumFrom'-enumFromS :: (Enum a, Monad m) => a -> y' -> Proxy x' x y' a m r-enumFromS a0 = \_ -> go a0 where- go a = Respond a (\_ -> go (succ a))-{- go a = do- _ <- respond a- go (succ a) -}+-- | 'Producer' version of 'enumFrom'+enumFromS :: (Enum b, Monad m, Proxy p) => b -> () -> Producer p b m r+enumFromS b0 = \_ -> runIdentityP (go b0) where+ go b = do+ respond b+ go (succ b) --- | 'Client' version of 'enumFrom'-enumFromC :: (Enum a, Monad m) => a -> y' -> Proxy a x y' y m r-enumFromC a0 = \_ -> go a0 where- go a = Request a (\_ -> go (succ a))-{- go a = do- _ <- request a- go (succ a) -}+-- | 'CoProducer' version of 'enumFrom'+enumFromC :: (Enum a', Monad m, Proxy p) => a' -> () -> CoProducer p a' m r+enumFromC a'0 = \_ -> runIdentityP (go a'0) where+ go a' = do+ request a'+ go (succ a') --- | 'Server' version of 'enumFromTo'-enumFromToS :: (Enum a, Ord a, Monad m) => a -> a -> y' -> Proxy x' x y' a m ()-enumFromToS a1 a2 _ = go a1 where- go n- | n > a2 = Pure ()- | otherwise = Respond n (\_ -> go (succ n))+-- | 'Producer' version of 'enumFromTo'+enumFromToS+ :: (Enum b, Ord b, Monad m, Proxy p) => b -> b -> () -> Producer p b m ()+enumFromToS b1 b2 _ = runIdentityP (go b1) where+ go b+ | b > b2 = return ()+ | otherwise = do+ respond b+ go (succ b) --- | 'Client' version of 'enumFromTo'-enumFromToC :: (Enum a, Ord a, Monad m) => a -> a -> y' -> Proxy a x y' y m ()-enumFromToC a1 a2 _ = go a1 where+-- | 'CoProducer' version of 'enumFromTo'+enumFromToC+ :: (Enum a', Ord a', Monad m, Proxy p)+ => a' -> a' -> () -> CoProducer p a' m ()+enumFromToC a1 a2 _ = runIdentityP (go a1) where go n- | n > a2 = Pure ()- | otherwise = Request n (\_ -> go (succ n))+ | n > a2 = return ()+ | otherwise = do+ request n+ go (succ n)++{-| Fold values flowing \'@D@\'ownstream++> foldD f >-> foldD g = foldD (f <> g)+>+> foldD mempty = idT+-}+foldD+ :: (Monad m, Proxy p, Monoid w) => (a -> w) -> x -> p x a x a (WriterT w m) r+foldD f = runIdentityK go where+ go x = do+ a <- request x+ lift $ tell $ f a+ x2 <- respond a+ go x2++{-| Fold values flowing \'@U@\'pstream++> foldU f >-> foldU g = foldU (g <> f)+>+> foldU mempty = idT+-}+foldU+ :: (Monad m, Proxy p, Monoid w)+ => (a' -> w) -> a' -> p a' x a' x (WriterT w m) r+foldU f = runIdentityK go where+ go a' = do+ lift $ tell $ f a'+ x <- request a'+ a'2 <- respond x+ go a'2++{-| Fold that returns whether 'All' values flowing \'@D@\'ownstream satisfy the+ predicate -}+allD :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT All m) r+allD pred = foldD (All . pred)++{-| Fold that returns whether 'All' values flowing \'@U@\'pstream satisfy the+ predicate -}+allU+ :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT All m) r+allU pred = foldU (All . pred)++{-| Fold that returns whether 'All' values flowing \'@D@\'ownstream satisfy the+ predicate++ 'allD_' terminates on the first value that fails the predicate -}+allD_ :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT All m) ()+allD_ pred = runIdentityK go where+ go x = do+ a <- request x+ if (pred a)+ then do+ x2 <- respond a+ go x2+ else lift $ tell $ All False++{-| Fold that returns whether 'All' values flowing \'@U@\'pstream satisfy the+ predicate++ 'allU_' terminates on the first value that fails the predicate -}+allU_+ :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT All m) ()+allU_ pred = runIdentityK go where+ go a' =+ if (pred a')+ then do+ x <- request a'+ a'2 <- respond x+ go a'2+ else lift $ tell $ All False++{-| Fold that returns whether 'Any' value flowing \'@D@\'ownstream satisfies+ the predicate -}+anyD :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT Any m) r+anyD pred = foldD (Any . pred)++{-| Fold that returns whether 'Any' value flowing \'@U@\'pstream satisfies+ the predicate -}+anyU+ :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT Any m) r+anyU pred = foldU (Any . pred)++{-| Fold that returns whether 'Any' value flowing \'@D@\'ownstream satisfies the+ predicate++ 'anyD_' terminates on the first value that satisfies the predicate -}+anyD_ :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT Any m) ()+anyD_ pred = runIdentityK go where+ go x = do+ a <- request x+ if (pred a)+ then lift $ tell $ Any True+ else do+ x2 <- respond a+ go x2++{-| Fold that returns whether 'Any' value flowing \'@U@\'pstream satisfies the+ predicate++ 'anyU_' terminates on the first value that satisfies the predicate -}+anyU_+ :: (Monad m, Proxy p) => (a' -> Bool) -> a' -> p a' x a' x (WriterT Any m) ()+anyU_ pred = runIdentityK go where+ go a' =+ if (pred a')+ then lift $ tell $ Any True+ else do+ x <- request a'+ a'2 <- respond x+ go a'2++-- | Compute the 'Sum' of all values that flow \'@D@\'ownstream+sumD :: (Monad m, Proxy p, Num a) => x -> p x a x a (WriterT (Sum a) m) r+sumD = foldD Sum++-- | Compute the 'Sum' of all values that flow \'@U@\'pstream+sumU :: (Monad m, Proxy p, Num a') => a' -> p a' x a' x (WriterT (Sum a') m) r+sumU = foldU Sum++-- | Compute the 'Product' of all values that flow \'@D@\'ownstream+productD+ :: (Monad m, Proxy p, Num a) => x -> p x a x a (WriterT (Product a) m) r+productD = foldD Product++-- | Compute the 'Product' of all values that flow \'@U@\'pstream+productU+ :: (Monad m, Proxy p, Num a') => a' -> p a' x a' x (WriterT (Product a') m) r+productU = foldU Product++-- | Count how many values flow \'@D@\'ownstream+lengthD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (Sum Int) m) r+lengthD = foldD (\_ -> Sum 1)++-- | Count how many values flow \'@U@\'pstream+lengthU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (Sum Int) m) r+lengthU = foldU (\_ -> Sum 1)++-- | Retrieve the first value going \'@D@\'ownstream+headD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (First a) m) r+headD = foldD (First . Just)++{-| Retrieve the first value going \'@D@\'ownstream++ 'headD_' terminates on the first value it receives -}+headD_ :: (Monad m, Proxy p) => x -> p x a x a (WriterT (First a) m) ()+headD_ x = runIdentityP $ do+ a <- request x+ lift $ tell $ First (Just a)++-- | Retrieve the first value going \'@U@\'pstream+headU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (First a') m) r+headU = foldU (First . Just)++{-| Retrieve the first value going \'@U@\'pstream++ 'headU_' terminates on the first value it receives -}+headU_ :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (First a') m) ()+headU_ a' = runIdentityP $ lift $ tell $ First (Just a')++-- | Retrieve the last value going \'@D@\'ownstream+lastD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (Last a) m) r+lastD = foldD (Last . Just)++-- | Retrieve the last value going \'@U@\'pstream+lastU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT (Last a') m) r+lastU = foldU (Last . Just)++-- | Fold the values flowing \'@D@\'ownstream into a list+toListD :: (Monad m, Proxy p) => x -> p x a x a (WriterT [a] m) r+toListD = foldD (\x -> [x])++-- | Fold the values flowing \'@U@\'pstream into a list+toListU :: (Monad m, Proxy p) => a' -> p a' x a' x (WriterT [a'] m) r+toListU = foldU (\x -> [x])++{-| Fold equivalent to 'foldr'++ To see why, consider this isomorphic type for 'foldr':++> foldr :: (a -> b -> b) -> [a] -> Endo b+-}+foldrD+ :: (Monad m, Proxy p) => (a -> b -> b) -> x -> p x a x a (WriterT (Endo b) m) r+foldrD step = foldD (Endo . step)++-- | Fold equivalent to 'foldr'+foldrU+ :: (Monad m, Proxy p)+ => (a' -> b -> b) -> a' -> p a' x a' x (WriterT (Endo b) m) r+foldrU step = foldU (Endo . step)++-- | Left strict fold over \'@D@\'ownstream values+foldlD'+ :: (Monad m, Proxy p) => (b -> a -> b) -> x -> p x a x a (StateT b m) r+foldlD' f = runIdentityK go where+ go x = do+ a <- request x+ lift $ do+ b <- get+ put $! f b a+ x2 <- respond a+ go x2++-- | Left strict fold over \'@U@\'pstream values+foldlU'+ :: (Monad m, Proxy p) => (b -> a' -> b) -> a' -> p a' x a' x (StateT b m) r+foldlU' f = runIdentityK go where+ go a' = do+ lift $ do+ b <- get+ put $! f b a'+ x <- request a'+ a'2 <- respond x+ go a'2++-- | Zip values flowing downstream+zipD+ :: (Monad m, Proxy p1, Proxy p2, Proxy p3)+ => () -> Consumer p1 a (Consumer p2 b (Producer p3 (a, b) m)) r+zipD () = runIdentityP $ hoist (runIdentityP . hoist runIdentityP) go where+ go = do+ a <- request ()+ lift $ do+ b <- request ()+ lift $ respond (a, b)+ go++-- | Interleave values flowing downstream using simple alternation+mergeD+ :: (Monad m, Proxy p1, Proxy p2, Proxy p3)+ => () -> Consumer p1 a (Consumer p2 a (Producer p3 a m)) r+mergeD () = runIdentityP $ hoist (runIdentityP . hoist runIdentityP) go where+ go = do+ a1 <- request ()+ lift $ do+ lift $ respond a1+ a2 <- request ()+ lift $ respond a2+ go++{- $open+ Use the @unit@ functions when you need to embed a proxy with a closed end+ within an open proxy. For example, the following code will not type-check+ because @fromListS [1..]@ is a 'Producer' and has a closed upstream end,+ which conflicts with the 'request' statement preceding it:++> p () = do+> request ()+> fromList [1..] ()++ You fix this by composing 'unitD' upstream of it, which replaces its closed+ upstream end with an open polymorphic end:++> p () = do+> request ()+> (fromList [1..] <-< unitD) ()++-}++-- | Compose 'unitD' with a closed upstream end to create a polymorphic end+unitD :: (Monad m, Proxy p) => y' -> p x' x y' () m r+unitD _ = runIdentityP go where+ go = do+ respond ()+ go++-- | Compose 'unitU' with a closed downstream end to create a polymorphic end+unitU :: (Monad m, Proxy p) => y' -> p () x y' y m r+unitU _ = runIdentityP go where+ go = do+ request ()+ go++{- $modules+ These modules help you build, run, and extract folds+-}
Control/Proxy/Prelude/IO.hs view
@@ -3,8 +3,7 @@ Note that 'String's are very inefficient, and I will release future separate packages with 'ByteString' and 'Text' operations. I only provide these to allow users to test simple I/O without requiring additional library- dependencies.--}+ dependencies. -} module Control.Proxy.Prelude.IO ( -- * Standard I/O@@ -25,45 +24,46 @@ promptC, -- * Handle I/O -- ** Input- hGetLineD,- hGetLineU,+ hGetLineS,+ hGetLineC, -- ** Output hPrintB, hPrintD, hPrintU, hPutStrLnB, hPutStrLnD,- hPutStrLnU+ hPutStrLnU, ) where import Control.Monad (forever) import Control.Monad.Trans.Class (lift) import Control.Proxy.Prelude.Kleisli (foreverK)-import Control.Proxy.Core (Proxy, Client, Server)-import Control.Proxy.Class (request, respond)-import System.IO (Handle, hGetLine, hPutStr, hPutStrLn, hPrint, stdin, stdout)+import Control.Proxy.Class (Proxy(request, respond))+import Control.Proxy.Trans.Identity (runIdentityP, runIdentityK)+import Control.Proxy.Synonym (Client, Server, Producer, CoProducer)+import qualified System.IO as IO --- | Get input from 'stdin' one line at a time and send \'@D@\'ownstream-getLineS :: y' -> Proxy x' x y' String IO r-getLineS _ = forever $ do+-- | A 'Producer' that sends lines from 'stdin' downstream+getLineS :: (Proxy p) => () -> Producer p String IO r+getLineS () = runIdentityP $ forever $ do str <- lift getLine respond str --- | Get input from 'stdin' one line at a time and send \'@U@\'pstream-getLineC :: y' -> Proxy String x y' y IO r-getLineC _ = forever $ do+-- | A 'CoProducer' that sends lines from 'stdin' upstream+getLineC :: (Proxy p) => () -> CoProducer p String IO r+getLineC () = runIdentityP $ forever $ do str <- lift getLine request str -- | 'read' input from 'stdin' one line at a time and send \'@D@\'ownstream-readLnS :: (Read a) => y' -> Proxy x' x y' a IO r-readLnS _ = forever $ do+readLnS :: (Read b, Proxy p) => () -> Producer p b IO r+readLnS () = runIdentityP $ forever $ do a <- lift readLn respond a -- | 'read' input from 'stdin' one line at a time and send \'@U@\'pstream-readLnC :: (Read a) => y' -> Proxy a x y' y IO r-readLnC _ = forever $ do+readLnC :: (Read a', Proxy p) => () -> CoProducer p a' IO r+readLnC () = runIdentityP $ forever $ do a <- lift readLn request a @@ -71,8 +71,8 @@ Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\" -}-printB :: (Show a, Show a') => a' -> Proxy a' a a' a IO r-printB = foreverK $ \a' -> do+printB :: (Show a', Show a, Proxy p) => a' -> p a' a a' a IO r+printB = runIdentityK $ foreverK $ \a' -> do lift $ do putStr "U: " print a'@@ -83,15 +83,15 @@ respond a -- | 'print's all values flowing \'@D@\'ownstream to 'stdout'-printD :: (Show a) => x -> Proxy x a x a IO r-printD = foreverK $ \x -> do+printD :: (Show a, Proxy p) => x -> p x a x a IO r+printD = runIdentityK $ foreverK $ \x -> do a <- request x lift $ print a respond a -- | 'print's all values flowing \'@U@\'pstream to 'stdout'-printU :: (Show a') => a' -> Proxy a' x a' x IO r-printU = foreverK $ \a' -> do+printU :: (Show a', Proxy p) => a' -> p a' x a' x IO r+printU = runIdentityK $ foreverK $ \a' -> do lift $ print a' x <- request a' respond x@@ -100,8 +100,8 @@ Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\" -}-putStrLnB :: String -> Proxy String String String String IO r-putStrLnB = foreverK $ \a' -> do+putStrLnB :: (Proxy p) => String -> p String String String String IO r+putStrLnB = runIdentityK $ foreverK $ \a' -> do lift $ do putStr "U: " putStrLn a'@@ -112,72 +112,84 @@ respond a -- | 'putStrLn's all values flowing \'@D@\'ownstream to 'stdout'-putStrLnD :: x -> Proxy x String x String IO r-putStrLnD = foreverK $ \x -> do+putStrLnD :: (Proxy p) => x -> p x String x String IO r+putStrLnD = runIdentityK $ foreverK $ \x -> do a <- request x lift $ putStrLn a respond a -- | 'putStrLn's all values flowing \'@U@\'pstream to 'stdout'-putStrLnU :: String -> Proxy String x String x IO r-putStrLnU = foreverK $ \a' -> do+putStrLnU :: (Proxy p) => String -> p String x String x IO r+putStrLnU = runIdentityK $ foreverK $ \a' -> do lift $ putStrLn a' x <- request a' respond x -- | Convert 'stdin'/'stdout' into a line-based 'Server'-promptS :: String -> Proxy x' x String String IO r-promptS = foreverK $ \send -> do+promptS :: (Proxy p) => String -> Server p String String IO r+promptS = runIdentityK $ foreverK $ \send -> do recv <- lift $ do putStrLn send getLine respond recv -- | Convert 'stdin'/'stdout' into a line-based 'Client'-promptC :: y' -> Proxy String String y' y IO r-promptC _ = forever $ do+promptC :: (Proxy p) => () -> Client p String String IO r+promptC () = runIdentityP $ forever $ do send <- lift getLine recv <- request send lift $ putStrLn recv --- | Get input from a handle one line at a time and send \'@D@\'ownstream-hGetLineD :: Handle -> y' -> Proxy x' x y' String IO r-hGetLineD h _ = forever $ do- str <- lift $ hGetLine h- respond str+-- | A 'Producer' that sends lines from a handle downstream+hGetLineS :: (Proxy p) => IO.Handle -> () -> Producer p String IO ()+hGetLineS h () = runIdentityP go where+ go = do+ eof <- lift $ IO.hIsEOF h+ if eof+ then return ()+ else do+ str <- lift $ IO.hGetLine h+ respond str+ go --- | Get input from a handle one line at a time and send \'@U@\'pstream-hGetLineU :: Handle -> y' -> Proxy String x y' y IO r-hGetLineU h _ = forever $ do- str <- lift $ hGetLine h- request str+-- | A 'CoProducer' that sends lines from a 'Handle' upstream+hGetLineC :: (Proxy p) => IO.Handle -> () -> CoProducer p String IO ()+hGetLineC h () = runIdentityP go where+ go = do+ eof <- lift $ IO.hIsEOF h+ if eof+ then return ()+ else do+ str <- lift $ IO.hGetLine h+ request str+ go {-| 'print's all values flowing through it to a 'Handle' Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\" -}-hPrintB :: (Show a, Show a') => Handle -> a' -> Proxy a' a a' a IO r-hPrintB h = foreverK $ \a' -> do+hPrintB :: (Show a, Show a', Proxy p) => IO.Handle -> a' -> p a' a a' a IO r+hPrintB h = runIdentityK $ foreverK $ \a' -> do lift $ do- hPutStr h "U: "- hPrint h a'+ IO.hPutStr h "U: "+ IO.hPrint h a' a <- request a' lift $ do- hPutStr h "D: "- hPrint h a+ IO.hPutStr h "D: "+ IO.hPrint h a respond a -- | 'print's all values flowing \'@D@\'ownstream to a 'Handle'-hPrintD :: (Show a) => Handle -> x -> Proxy x a x a IO r-hPrintD h = foreverK $ \x -> do+hPrintD :: (Show a, Proxy p) => IO.Handle -> x -> p x a x a IO r+hPrintD h = runIdentityK $ foreverK $ \x -> do a <- request x- lift $ hPrint h a+ lift $ IO.hPrint h a respond a -- | 'print's all values flowing \'@U@\'pstream to a 'Handle'-hPrintU :: (Show a') => Handle -> a' -> Proxy a' x a' x IO r-hPrintU h = foreverK $ \a' -> do- lift $ hPrint h a'+hPrintU :: (Show a', Proxy p) => IO.Handle -> a' -> p a' x a' x IO r+hPrintU h = runIdentityK $ foreverK $ \a' -> do+ lift $ IO.hPrint h a' x <- request a' respond x @@ -185,27 +197,28 @@ Prefixes upstream values with \"@U: @\" and downstream values with \"@D: @\" -}-hPutStrLnB :: Handle -> String -> Proxy String String String String IO r-hPutStrLnB h = foreverK $ \a' -> do+hPutStrLnB+ :: (Proxy p) => IO.Handle -> String -> p String String String String IO r+hPutStrLnB h = runIdentityK $ foreverK $ \a' -> do lift $ do- hPutStr h "U: "- hPutStrLn h a'+ IO.hPutStr h "U: "+ IO.hPutStrLn h a' a <- request a' lift $ do- hPutStr h "D: "- hPutStrLn h a+ IO.hPutStr h "D: "+ IO.hPutStrLn h a respond a -- | 'putStrLn's all values flowing \'@D@\'ownstream to a 'Handle'-hPutStrLnD :: Handle -> x -> Proxy x String x String IO r-hPutStrLnD h = foreverK $ \x -> do+hPutStrLnD :: (Proxy p) => IO.Handle -> x -> p x String x String IO r+hPutStrLnD h = runIdentityK $ foreverK $ \x -> do a <- request x- lift $ hPutStrLn h a+ lift $ IO.hPutStrLn h a respond a -- | 'putStrLn's all values flowing \'@U@\'pstream to a 'Handle'-hPutStrLnU :: Handle -> String -> Proxy String x String x IO r-hPutStrLnU h = foreverK $ \a' -> do- lift $ hPutStrLn h a'+hPutStrLnU :: (Proxy p) => IO.Handle -> String -> p String x String x IO r+hPutStrLnU h = runIdentityK $ foreverK $ \a' -> do+ lift $ IO.hPutStrLn h a' x <- request a' respond x
Control/Proxy/Prelude/Kleisli.hs view
@@ -1,36 +1,36 @@+{-# LANGUAGE Rank2Types #-}+ -- | Utility functions for Kleisli arrows module Control.Proxy.Prelude.Kleisli ( -- * Core utility functions- -- $utility foreverK, replicateK,- mapK+ liftK,+ hoistK,+ raiseK, ) where -import Control.Monad (forever, (>=>))+import Control.MFunctor (MFunctor(hoist)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Proxy.Class (Interact(request, respond))-import Data.Closed (C) -{- $utility- Use 'foreverK' to abstract away the following common pattern:+{-| 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 avoid the manual recursion:+ Using 'foreverK', you can instead write: > p = foreverK $ \a -> do > ... > respond b -}---- | Compose a \'@K@\'leisli arrow with itself forever foreverK :: (Monad m) => (a -> m a) -> (a -> m b)-foreverK k = let r = k >=> r in r+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 -} @@ -40,14 +40,48 @@ go n | n < 1 = return | n == 1 = k- | otherwise = k >=> go (n - 1)+ | otherwise = \a -> k a >>= go (n - 1) {-| Convenience function equivalent to @(lift .)@ -> mapK f >=> mapK g = mapK (f >=> g)+> liftK f >=> liftK g = liftK (f >=> g) >-> mapK return = return+> liftK return = return -}-mapK :: (Monad m, MonadTrans t) => (a -> m b) -> (a -> t m b)-mapK = (lift .)-{-# INLINABLE mapK #-}+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 .)
+ Control/Proxy/Synonym.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE KindSignatures #-}++{-| These type synonyms simplify type signatures when proxies do not use all+ their type variables. -}++module Control.Proxy.Synonym (+ -- * Synonyms+ Pipe,+ Producer,+ Consumer,+ CoPipe,+ CoProducer,+ CoConsumer,+ Client,+ Server,+ Session,++ -- * Closed+ C+ ) where++-- | A unidirectional 'Proxy'.+type Pipe (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a b = p () a () b++{-| A 'Pipe' that produces values++ 'Producer's never 'request'. -}+type Producer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) b = p C () () b++{-| A 'Pipe' that consumes values++ 'Consumer's never 'respond'. -}+type Consumer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a = p () a () C++-- | A 'Pipe' where everything flows upstream+type CoPipe (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a' b' = p a' () b' ()++{-| A 'CoPipe' that produces values flowing upstream++ 'CoProducer's never 'respond'. -}+type CoProducer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a' = p a' () () C++{-| A 'CoConsumer' that consumes values flowing upstream++ 'CoConsumer's never 'request'. -}+type CoConsumer (p :: * -> * -> * -> * -> (* -> *) -> * -> *) b' = p C () b' ()++{-| @Server b' b@ receives requests of type @b'@ and sends responses of type+ @b@.++ 'Server's never 'request'. -}+type Server (p :: * -> * -> * -> * -> (* -> *) -> * -> *) b' b = p C () b' b++{-| @Client a' a@ sends requests of type @a'@ and receives responses of+ type @a@.++ 'Client's never 'respond'. -}+type Client (p :: * -> * -> * -> * -> (* -> *) -> * -> *) a' a = p a' a () C++{-| A self-contained 'Session', ready to be run by 'runSession'++ 'Session's never 'request' or 'respond'. -}+type Session (p :: * -> * -> * -> * -> (* -> *) -> * -> *) = p C () () C++-- | The empty type, denoting a \'@C@\'losed end+data C = C -- Constructor not exported, but I include it to avoid EmptyDataDecls
Control/Proxy/Trans.hs view
@@ -1,38 +1,71 @@-{-| You can define your own extensions to the 'Proxy' type by writing your own- \"proxy transformers\". Proxy transformers are monad transformers that- correctly lift 'Proxy' composition from the base monad. Stack multiple- proxy transformers to chain features together. -}+{-| You can define your own proxy extensions by writing your own \"proxy+ transformers\". Proxy transformers are monad transformers that also+ correctly lift all proxy operations from the base proxy type to the+ extended proxy type. Stack multiple proxy transformers to chain features+ together.+-} module Control.Proxy.Trans ( -- * Proxy Transformers- ProxyTrans(..)- )where+ ProxyTrans(..),+ mapP + -- * Laws+ -- $laws+ ) where+ import Control.Proxy.Class -{-| 'mapP' defines a functor that preserves 'Proxy' composition and Kleisli- composition.+-- | Uniform interface to lifting proxies+class ProxyTrans t where+ liftP :: (Monad m, Proxy p) => p a' a b' b m r -> t p a' a b' b m r +{-| Lift a 'Proxy' Kleisli arrow++> mapP = (lift .)+-}+mapP :: (Monad m, Proxy p, ProxyTrans t)+ => (q -> p a' a b' b m r) -> (q -> t p a' a b' b m r)+mapP = (liftP .)++{- $laws+ 'mapP' defines a functor that preserves five categories:++ * Kleisli category++ * The two Proxy categories++ * \"request\" category++ * \"respond\" category+ Laws: * Functor between 'Proxy' categories -> mapP (f <-< g) = mapP f <-< mapP g+> mapP (f >-> g) = mapP f >-> mapP g+> > mapP idT = idT +> mapP (f >~> g) = mapP f >~> mapP g+>+> mapP idPush = idPush+ * Functor between Kleisli categories > mapP (f <=< g) = mapP f <=< mapP g+> > mapP return = return - Minimal complete definition: 'mapP' or 'liftP'. Defining 'liftP' is more- efficient.--}-class ProxyTrans t where- liftP :: (Monad (p b c d e m), Channel p)- => p b c d e m r -> t p b c d e m r- liftP f = mapP (\() -> f) ()+ * Functor between \"request\" categories - mapP :: (Monad (p b c d e m), Channel p)- => (a -> p b c d e m r) -> (a -> t p b c d e m r)- mapP = (liftP .)+> mapP (f /</ g) = mapP f /</ mapP g -- when /</ is defined+>+> mapP request = request++ * Functor between \"respond\" categories++> mapP (f \<\ g) = mapP f \<\ mapP g -- when \<\ is defined+>+> mapP respond = respond+-}
Control/Proxy/Trans/Either.hs view
@@ -1,6 +1,6 @@ -- | This module provides the proxy transformer equivalent of 'EitherT'. -{-# LANGUAGE FlexibleContexts, KindSignatures #-}+{-# LANGUAGE KindSignatures #-} module Control.Proxy.Trans.Either ( -- * EitherP@@ -17,11 +17,12 @@ ) where import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))+import Control.Monad (MonadPlus(mzero, mplus)) import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (Channel(idT, (>->))) +import Control.MFunctor (MFunctor(hoist))+import Control.PFunctor (PFunctor(hoistP))+import Control.Proxy.Class import Control.Proxy.Trans (ProxyTrans(liftP)) import Prelude hiding (catch) @@ -29,59 +30,114 @@ newtype EitherP e p a' a b' b (m :: * -> *) r = EitherP { runEitherP :: p a' a b' b m (Either e r) } -instance (Monad (p a' a b' b m)) => Functor (EitherP e p a' a b' b m) where- fmap = liftM+instance (Proxy p, Monad m)+ => Functor (EitherP e p a' a b' b m) where+ fmap f p = EitherP (+ runEitherP p ?>= \e ->+ return_P (case e of+ Left l -> Left l+ Right r -> Right (f r) ) )+ -- fmap f = EitherP . liftM (fmap f) . runEitherP -instance (Monad (p a' a b' b m)) => Applicative (EitherP e p a' a b' b m) where- pure = return- (<*>) = ap+instance (Proxy p, Monad m)+ => Applicative (EitherP e p a' a b' b m) where+ pure = return+ fp <*> xp = EitherP (+ runEitherP fp ?>= \e1 ->+ case e1 of+ Left l -> return_P (Left l)+ Right f ->+ runEitherP xp ?>= \e2 ->+ return_P (case e2 of+ Left l -> Left l+ Right x -> Right (f x) ) )+ -- fp <*> xp = EitherP ((<*>) <$> (runEitherP fp) <*> (runEitherP xp)) -instance (Monad (p a' a b' b m)) => Monad (EitherP e p a' a b' b m) where- return = right- m >>= f = EitherP $ do- e <- runEitherP m- runEitherP $ case e of- Left l -> left l- Right r -> f r+instance (Proxy p, Monad m)+ => Monad (EitherP e p a' a b' b m) where+ return = return_P+ (>>=) = (?>=) -instance (MonadPlus (p a' a b' b m))- => Alternative (EitherP e p a' a b' b m) where+instance (MonadPlusP p, Monad m)+ => Alternative (EitherP e p a' a b' b m) where empty = mzero (<|>) = mplus -instance (MonadPlus (p a' a b' b m))- => MonadPlus (EitherP e p a' a b' b m) where- mzero = EitherP mzero- mplus m1 m2 = EitherP $ mplus (runEitherP m1) (runEitherP m2)+instance (MonadPlusP p )+ => MonadPlusP (EitherP e p) where+ mzero_P = EitherP mzero_P+ mplus_P m1 m2 = EitherP (mplus_P (runEitherP m1) (runEitherP m2)) -instance (MonadTrans (p a' a b' b)) => MonadTrans (EitherP e p a' a b' b) where- lift = EitherP . lift . liftM Right+instance (MonadPlusP p, Monad m)+ => MonadPlus (EitherP e p a' a b' b m) where+ mzero = mzero_P+ mplus = mplus_P -instance (MonadIO (p a' a b' b m)) => MonadIO (EitherP e p a' a b' b m) where- liftIO = EitherP . liftIO . liftM Right+instance (Proxy p )+ => MonadTrans (EitherP e p a' a b' b) where+ lift = lift_P -instance (MFunctor (p a' a b' b)) => MFunctor (EitherP e p a' a b' b) where- mapT nat = EitherP . mapT nat . runEitherP+instance (MonadIOP p )+ => MonadIOP (EitherP e p) where+ liftIO_P m = EitherP (liftIO_P (m >>= \x -> return (Right x)))+ -- liftIO = EitherP . liftIO . liftM Right -instance (Channel p) => Channel (EitherP e p) where- idT = EitherP . idT- p1 >-> p2 = (EitherP .) $ runEitherP . p1 >-> runEitherP . p2+instance (MonadIOP p, MonadIO m)+ => MonadIO (EitherP e p a' a b' b m) where+ liftIO = liftIO_P +instance (Proxy p )+ => MFunctor (EitherP e p a' a b' b) where+ hoist = hoist_P++instance (Proxy p )+ => Proxy (EitherP e p) where+ p1 >-> p2 = \c'1 -> EitherP (+ ((\b' -> runEitherP (p1 b')) >-> (\c'2 -> runEitherP (p2 c'2))) c'1 )+ -- p1 >-> p2 = (EitherP .) $ runEitherP . p1 >-> runEitherP . p2++ p1 >~> p2 = \c'1 -> EitherP (+ ((\b' -> runEitherP (p1 b')) >~> (\c'2 -> runEitherP (p2 c'2))) c'1 )+ -- p1 >~> p2 = (EitherP .) $ runEitherP . p1 >~> runEitherP . p2++ request = \a' -> EitherP (request a' ?>= \a -> return_P (Right a ))+ respond = \b -> EitherP (respond b ?>= \b' -> return_P (Right b'))++ return_P = right+ m ?>= f = EitherP (+ runEitherP m ?>= \e ->+ runEitherP (case e of+ Left l -> left l+ Right r -> f r ) )++ lift_P m = EitherP (lift_P (m >>= \x -> return (Right x)))+ -- lift = EitherP . lift . liftM Right++ hoist_P nat p = EitherP (hoist_P nat (runEitherP p))+ -- hoist nat = EitherP . hoist nat . runEitherP+ instance ProxyTrans (EitherP e) where- liftP = EitherP . liftM Right+ liftP p = EitherP (p ?>= \x -> return_P (Right x))+ -- liftP = EitherP . liftM Right +instance PFunctor (EitherP e) where+ hoistP nat = EitherP . nat . runEitherP+ -- | Run an 'EitherP' \'@K@\'leisi arrow, returning either a 'Left' or 'Right' runEitherK :: (q -> EitherP e p a' a b' b m r) -> (q -> p a' a b' b m (Either e r))-runEitherK = (runEitherP .)+runEitherK p q = runEitherP (p q)+-- runEitherK = (runEitherP .) -- | Abort the computation and return a 'Left' result-left :: (Monad (p a' a b' b m)) => e -> EitherP e p a' a b' b m r-left = EitherP . return . Left+left :: (Monad m, Proxy p) => e -> EitherP e p a' a b' b m r+left e = EitherP (return_P (Left e))+-- left = EitherP . return . Left -- | Synonym for 'return'-right :: (Monad (p a' a b' b m)) => r -> EitherP e p a' a b' b m r-right = EitherP . return . Right+right :: (Monad m, Proxy p) => r -> EitherP e p a' a b' b m r+right r = EitherP (return_P (Right r))+-- right = EitherP . return . Right {- $symmetry 'EitherP' forms a second symmetric monad over the left type variable.@@ -100,29 +156,26 @@ -} -- | Synonym for 'left'-throw :: (Monad (p a' a b' b m)) => e -> EitherP e p a' a b' b m r+throw :: (Monad m, Proxy p) => e -> EitherP e p a' a b' b m r throw = left -- | Resume from an aborted operation catch- :: (Monad (p a' a b' b m))+ :: (Monad m, Proxy p) => EitherP e p a' a b' b m r -- ^ Original computation -> (e -> EitherP f p a' a b' b m r) -- ^ Handler -> EitherP f p a' a b' b m r -- ^ Handled computation-catch m f = EitherP $ do- e <- runEitherP m- runEitherP $ case e of+catch m f = EitherP (+ runEitherP m ?>= \e ->+ runEitherP (case e of Left l -> f l- Right r -> right r+ Right r -> right r )) -- | 'catch' with the arguments flipped handle- :: (Monad (p a' a b' b m))+ :: (Monad m, Proxy p) => (e -> EitherP f p a' a b' b m r) -- ^ Handler -> EitherP e p a' a b' b m r -- ^ Original computation -> EitherP f p a' a b' b m r -- ^ Handled computation-handle f m = EitherP $ do- e <- runEitherP m- runEitherP $ case e of- Left l -> f l- Right r -> right r+handle f m = catch m f+-- handle = flip catch
Control/Proxy/Trans/Identity.hs view
@@ -1,70 +1,136 @@ -- | This module provides the proxy transformer equivalent of 'IdentityT'. -{-# LANGUAGE FlexibleContexts, KindSignatures #-}+{-# LANGUAGE KindSignatures #-} module Control.Proxy.Trans.Identity (- -- * IdentityP+ -- * Identity Proxy Transformer IdentityP(..),+ identityK, runIdentityK ) where import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))+import Control.Monad (MonadPlus(mzero, mplus)) import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (- Channel(idT , (>->)), - Interact(request, (\>\), respond, (/>/)) )+import Control.MFunctor (MFunctor(hoist))+import Control.PFunctor (PFunctor(hoistP))+import Control.Proxy.Class import Control.Proxy.Trans (ProxyTrans(liftP)) -- | The 'Identity' proxy transformer-newtype IdentityP p a' a b' b (m :: * -> *) r- = IdentityP { runIdentityP :: p a' a b' b m r }+newtype IdentityP p a' a b' b (m :: * -> *) r =+ IdentityP { runIdentityP :: p a' a b' b m r } -instance (Monad (p a' a b' b m)) => Functor (IdentityP p a' a b' b m) where- fmap = liftM+instance (Proxy p, Monad m)+ => Functor (IdentityP p a' a b' b m) where+ fmap f p = IdentityP (+ runIdentityP p ?>= \x ->+ return_P (f x) )+ -- fmap = liftM -instance (Monad (p a' a b' b m)) => Applicative (IdentityP p a' a b' b m) where- pure = return- (<*>) = ap+instance (Proxy p, Monad m)+ => Applicative (IdentityP p a' a b' b m) where+ pure = return -instance (Monad (p a' a b' b m)) => Monad (IdentityP p a' a b' b m) where- return = IdentityP . return- m >>= f = IdentityP $ runIdentityP m >>= runIdentityP . f+ fp <*> xp = IdentityP (+ runIdentityP fp ?>= \f ->+ runIdentityP xp ?>= \x ->+ return_P (f x) )+ -- fp <*> xp = ap -instance (MonadPlus (p a' a b' b m))- => Alternative (IdentityP p a' a b' b m) where+instance (Proxy p, Monad m)+ => Monad (IdentityP p a' a b' b m) where+ return = return_P+ (>>=) = (?>=)++instance (MonadPlusP p, Monad m)+ => Alternative (IdentityP p a' a b' b m) where empty = mzero (<|>) = mplus -instance (MonadPlus (p a' a b' b m))- => MonadPlus (IdentityP p a' a b' b m) where- mzero = IdentityP mzero- mplus m1 m2 = IdentityP $ mplus (runIdentityP m1) (runIdentityP m2)+instance (MonadPlusP p )+ => MonadPlusP (IdentityP p) where+ mzero_P = IdentityP mzero_P+ mplus_P m1 m2 = IdentityP (mplus_P (runIdentityP m1) (runIdentityP m2)) -instance (MonadTrans (p a' a b' b)) => MonadTrans (IdentityP p a' a b' b) where- lift = IdentityP . lift+instance (MonadPlusP p, Monad m)+ => MonadPlus (IdentityP p a' a b' b m) where+ mzero = mzero_P+ mplus = mplus_P -instance (MonadIO (p a' a b' b m)) => MonadIO (IdentityP p a' a b' b m) where- liftIO = IdentityP . liftIO+instance (Proxy p )+ => MonadTrans (IdentityP p a' a b' b) where+ lift = lift_P -instance (MFunctor (p a' a b' b)) => MFunctor (IdentityP p a' a b' b) where- mapT nat = IdentityP . mapT nat . runIdentityP+instance (MonadIOP p )+ => MonadIOP (IdentityP p) where+ liftIO_P m = IdentityP (liftIO_P m)+ -- liftIO = IdentityP . liftIO -instance (Channel p) => Channel (IdentityP p) where- idT = IdentityP . idT- p1 >-> p2 = (IdentityP .) $ runIdentityP . p1 >-> runIdentityP . p2+instance (MonadIOP p, MonadIO m)+ => MonadIO (IdentityP p a' a b' b m) where+ liftIO = liftIO_P -instance (Interact p) => Interact (IdentityP p) where- request = IdentityP . request- p1 \>\ p2 = (IdentityP .) $ runIdentityP . p1 \>\ runIdentityP . p2- respond = IdentityP . respond- p1 />/ p2 = (IdentityP .) $ runIdentityP . p1 />/ runIdentityP . p2+instance (Proxy p )+ => MFunctor (IdentityP p a' a b' b) where+ hoist = hoist_P +instance (Proxy p )+ => Proxy (IdentityP p) where+ p1 >-> p2 = \c'1 -> IdentityP (+ ((\c'2 -> runIdentityP (p1 c'2))+ >-> (\b' -> runIdentityP (p2 b' )) ) c'1 )+ -- p1 >-> p2 = (IdentityP .) $ runIdentityP . p1 >-> runIdentityP . p2++ p1 >~> p2 = \c'1 -> IdentityP (+ ((\c'2 -> runIdentityP (p1 c'2))+ >~> (\b' -> runIdentityP (p2 b' )) ) c'1 )+ -- p1 >~> p2 = (IdentityP .) $ runIdentityP . p1 >~> runIdentityP . p2++ request = \a' -> IdentityP (request a')+ -- request = P . request++ respond = \b -> IdentityP (respond b)+ -- respond = P . respond++ return_P = \r -> IdentityP (return_P r)+ -- return = P . return++ m ?>= f = IdentityP (+ runIdentityP m ?>= \x ->+ runIdentityP (f x) )++ lift_P m = IdentityP (lift_P m)+ -- lift = P . lift++ hoist_P nat p = IdentityP (hoist_P nat (runIdentityP p))+ -- hoist nat = IdentityP . hoist nat . runIdentityP++instance (Interact p )+ => Interact (IdentityP p) where+ p1 \>\ p2 = \c'1 -> IdentityP (+ ((\b' -> runIdentityP (p1 b' ))+ \>\ (\c'2 -> runIdentityP (p2 c'2)) ) c'1 )+ -- p1 \>\ p2 = (IdentityP .) $ runIdentityP . p1 \>\ runIdentityP . p2++ p1 />/ p2 = \a1 -> IdentityP (+ ((\a2 -> runIdentityP (p1 a2))+ />/ (\b -> runIdentityP (p2 b )) ) a1 )+ -- p1 />/ p2 = (IdentityP .) $ runIdentityP . p1 />/ runIdentityP . p2+ instance ProxyTrans IdentityP where liftP = IdentityP --- | Run an 'IdentityP' \'@K@\'leisli arrow+instance PFunctor IdentityP where+ hoistP nat = IdentityP . nat . runIdentityP++-- | Wrap a \'@K@\'leisli arrow in 'IdentityP'+identityK :: (q -> p a' a b' b m r) -> (q -> IdentityP p a' a b' b m r)+identityK k q = IdentityP (k q)+-- identityK = (IdentityP .)++-- | Run an 'P' \'@K@\'leisli arrow runIdentityK :: (q -> IdentityP p a' a b' b m r) -> (q -> p a' a b' b m r)-runIdentityK = (runIdentityP .)+runIdentityK k q = runIdentityP (k q)+-- runIdentityK = (runIdentityP .)
Control/Proxy/Trans/Maybe.hs view
@@ -1,6 +1,6 @@ -- | This module provides the proxy transformer equivalent of 'MaybeT'. -{-# LANGUAGE FlexibleContexts, KindSignatures #-}+{-# LANGUAGE KindSignatures #-} module Control.Proxy.Trans.Maybe ( -- * MaybeP@@ -12,68 +12,125 @@ ) where import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))+import Control.Monad (MonadPlus(mzero, mplus)) import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (Channel(idT, (>->)))+import Control.MFunctor (MFunctor(hoist))+import Control.PFunctor (PFunctor(hoistP))+import Control.Proxy.Class import Control.Proxy.Trans (ProxyTrans(liftP)) -- | The 'Maybe' proxy transformer newtype MaybeP p a' a b' b (m :: * -> *) r = MaybeP { runMaybeP :: p a' a b' b m (Maybe r) } -instance (Monad (p a' a b' b m)) => Functor (MaybeP p a' a b' b m) where- fmap = liftM+instance (Proxy p, Monad m)+ => Functor (MaybeP p a' a b' b m) where+ fmap f p = MaybeP (+ runMaybeP p ?>= \m ->+ return_P (case m of+ Nothing -> Nothing+ Just x -> Just (f x) ) )+ -- fmap f = MaybeP . fmap (fmap f) . runMaybeP -instance (Monad (p a' a b' b m)) => Applicative (MaybeP p a' a b' b m) where- pure = return- (<*>) = ap+instance (Proxy p, Monad m)+ => Applicative (MaybeP p a' a b' b m) where+ pure = return -instance (Monad (p a' a b' b m)) => Monad (MaybeP p a' a b' b m) where- return = MaybeP . return . Just- m >>= f = MaybeP $ do- ma <- runMaybeP m- runMaybeP $ case ma of- Nothing -> nothing- Just a -> f a+ fp <*> xp = MaybeP (+ runMaybeP fp ?>= \m1 ->+ case m1 of+ Nothing -> return_P Nothing+ Just f ->+ runMaybeP xp ?>= \m2 ->+ case m2 of+ Nothing -> return_P Nothing+ Just x -> return_P (Just (f x)) )+ -- fp <*> xp = MaybeP ((<*>) <$> (runMaybeP fp) <*> (runMaybeP xp)) -instance (Monad (p a' a b' b m)) => Alternative (MaybeP p a' a b' b m) where+instance (Proxy p, Monad m)+ => Monad (MaybeP p a' a b' b m) where+ return = return_P+ (>>=) = (?>=)++instance (Proxy p, Monad m)+ => Alternative (MaybeP p a' a b' b m) where empty = mzero (<|>) = mplus -instance (Monad (p a' a b' b m)) => MonadPlus (MaybeP p a' a b' b m) where- mzero = nothing- mplus m1 m2 = MaybeP $ do- ma <- runMaybeP m1- runMaybeP $ case ma of+instance (Proxy p )+ => MonadPlusP (MaybeP p) where+ mzero_P = nothing+ mplus_P m1 m2 = MaybeP (+ runMaybeP m1 ?>= \ma ->+ runMaybeP (case ma of Nothing -> m2- Just a -> just a+ Just a -> just a ) ) -instance (MonadTrans (p a' a b' b)) => MonadTrans (MaybeP p a' a b' b) where- lift = MaybeP . lift . liftM Just+instance (Proxy p, Monad m)+ => MonadPlus (MaybeP p a' a b' b m) where+ mzero = mzero_P+ mplus = mplus_P -instance (MonadIO (p a' a b' b m)) => MonadIO (MaybeP p a' a b' b m) where- liftIO = MaybeP . liftIO . liftM Just+instance (Proxy p )+ => MonadTrans (MaybeP p a' a b' b) where+ lift = lift_P -instance (MFunctor (p a' a b' b)) => MFunctor (MaybeP p a' a b' b) where- mapT nat = MaybeP . mapT nat . runMaybeP+instance (MonadIOP p )+ => MonadIOP (MaybeP p) where+ liftIO_P m = MaybeP (liftIO_P (m >>= \x -> return (Just x)))+ -- liftIO = MaybeP . liftIO . liftM Just -instance (Channel p) => Channel (MaybeP p) where- idT = MaybeP . idT- p1 >-> p2 = (MaybeP .) $ runMaybeP . p1 >-> runMaybeP . p2+instance (MonadIOP p, MonadIO m)+ => MonadIO (MaybeP p a' a b' b m) where+ liftIO = liftIO_P +instance (Proxy p )+ => MFunctor (MaybeP p a' a b' b) where+ hoist = hoist_P++instance (Proxy p )+ => Proxy (MaybeP p) where+ p1 >-> p2 = \c'1 -> MaybeP (+ ((\b' -> runMaybeP (p1 b')) >-> (\c'2 -> runMaybeP (p2 c'2))) c'1 )+ -- p1 >-> p2 = (MaybeP .) $ runMaybeP . p1 >-> runMaybeP . p2++ p1 >~> p2 = \c'1 -> MaybeP (+ ((\b' -> runMaybeP (p1 b')) >~> (\c'2 -> runMaybeP (p2 c'2))) c'1 )+ -- p1 >~> p2 = (MaybeP .) $ runMaybeP . p1 >~> runMaybeP . p2++ request = \a' -> MaybeP (request a' ?>= \a -> return_P (Just a ))+ respond = \b -> MaybeP (respond b ?>= \b' -> return_P (Just b'))++ return_P = just+ m ?>= f = MaybeP (+ runMaybeP m ?>= \ma ->+ runMaybeP (case ma of+ Nothing -> nothing+ Just a -> f a ) )++ lift_P m = MaybeP (lift_P (m >>= \x -> return (Just x)))+ -- lift = MaybeP . lift . liftM Just++ hoist_P nat p = MaybeP (hoist_P nat (runMaybeP p))+ -- hoist nat = MaybeP . hoist nat . runMaybeP+ instance ProxyTrans MaybeP where- liftP = MaybeP . liftM Just+ liftP p = MaybeP (p ?>= \x -> return_P (Just x))+ -- liftP = MaybeP . liftM Just +instance PFunctor MaybeP where+ hoistP nat = MaybeP . nat . runMaybeP+ -- | Run a 'MaybeP' \'@K@\'leisli arrow, returning the result or 'Nothing' runMaybeK :: (q -> MaybeP p a' a b' b m r) -> (q -> p a' a b' b m (Maybe r))-runMaybeK = (runMaybeP .)+runMaybeK p q = runMaybeP (p q)+-- runMaybeK = (runMaybeP .) -- | A synonym for 'mzero'-nothing :: (Monad (p a' a b' b m)) => MaybeP p a' a b' b m r-nothing = MaybeP $ return Nothing+nothing :: (Monad m, Proxy p) => MaybeP p a' a b' b m r+nothing = MaybeP (return_P Nothing) -- | A synonym for 'return'-just :: (Monad (p a' a b' b m)) => r -> MaybeP p a' a b' b m r-just = return+just :: (Monad m, Proxy p) => r -> MaybeP p a' a b' b m r+just r = MaybeP (return_P (Just r))
Control/Proxy/Trans/Reader.hs view
@@ -1,6 +1,6 @@ -- | This module provides the proxy transformer equivalent of 'ReaderT'. -{-# LANGUAGE FlexibleContexts, KindSignatures #-}+{-# LANGUAGE KindSignatures #-} module Control.Proxy.Trans.Reader ( -- * ReaderP@@ -15,91 +15,139 @@ ) where import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))+import Control.Monad (MonadPlus(mzero, mplus)) import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (- Channel(idT, (>->)), - Interact(request, (\>\), respond, (/>/)) )+import Control.MFunctor (MFunctor(hoist))+import Control.PFunctor (PFunctor(hoistP))+import Control.Proxy.Class import Control.Proxy.Trans (ProxyTrans(liftP)) -- | The 'Reader' proxy transformer newtype ReaderP i p a' a b' b (m :: * -> *) r = ReaderP { unReaderP :: i -> p a' a b' b m r } -instance (Monad (p a' a b' b m)) => Functor (ReaderP i p a' a b' b m) where- fmap = liftM+instance (Proxy p, Monad m)+ => Functor (ReaderP i p a' a b' b m) where+ fmap f p = ReaderP (\i ->+ unReaderP p i ?>= \x ->+ return_P (f x) ) -instance (Monad (p a' a b' b m)) => Applicative (ReaderP i p a' a b' b m) where- pure = return- (<*>) = ap+instance (Proxy p, Monad m)+ => Applicative (ReaderP i p a' a b' b m) where+ pure = return+ p1 <*> p2 = ReaderP (\i ->+ unReaderP p1 i ?>= \f -> + unReaderP p2 i ?>= \x -> + return_P (f x) ) -instance (Monad (p a' a b' b m)) => Monad (ReaderP i p a' a b' b m) where- return a = ReaderP $ \_ -> return a- m >>= f = ReaderP $ \i -> do- a <- unReaderP m i- unReaderP (f a) i+instance (Proxy p, Monad m)+ => Monad (ReaderP i p a' a b' b m) where+ return = return_P+ (>>=) = (?>=) -instance (MonadPlus (p a' a b' b m))- => Alternative (ReaderP i p a' a b' b m) where+instance (MonadPlusP p, Monad m)+ => Alternative (ReaderP i p a' a b' b m) where empty = mzero (<|>) = mplus -instance (MonadPlus (p a' a b' b m))- => MonadPlus (ReaderP i p a' a b' b m) where- mzero = ReaderP $ \_ -> mzero- mplus m1 m2 = ReaderP $ \i -> mplus (unReaderP m1 i) (unReaderP m2 i)+instance (MonadPlusP p )+ => MonadPlusP (ReaderP i p) where+ mzero_P = ReaderP (\_ -> mzero_P)+ mplus_P m1 m2 = ReaderP (\i -> mplus_P (unReaderP m1 i) (unReaderP m2 i)) -instance (MonadTrans (p a' a b' b)) => MonadTrans (ReaderP i p a' a b' b) where- lift m = ReaderP $ \_ -> lift m+instance (MonadPlusP p, Monad m)+ => MonadPlus (ReaderP i p a' a b' b m) where+ mzero = mzero_P+ mplus = mplus_P -instance (MonadIO (p a' a b' b m)) => MonadIO (ReaderP i p a' a b' b m) where- liftIO m = ReaderP $ \_ -> liftIO m+instance (Proxy p )+ => MonadTrans (ReaderP i p a' a b' b) where+ lift = lift_P -instance (MFunctor (p a' a b' b)) => MFunctor (ReaderP i p a' a b' b) where- mapT nat = ReaderP . fmap (mapT nat) . unReaderP+instance (MonadIOP p )+ => MonadIOP (ReaderP i p) where+ liftIO_P m = ReaderP (\_ -> liftIO_P m) -instance (Channel p) => Channel (ReaderP i p) where- idT a = ReaderP $ \_ -> idT a- (p1 >-> p2) a = ReaderP $ \i ->- ((`unReaderP` i) . p1 >-> (`unReaderP` i) . p2) a+instance (MonadIOP p, MonadIO m)+ => MonadIO (ReaderP i p a' a b' b m) where+ liftIO = liftIO_P -instance (Interact p) => Interact (ReaderP i p) where- request a = ReaderP $ \_ -> request a- (p1 \>\ p2) a = ReaderP $ \i ->- ((`unReaderP` i) . p1 \>\ (`unReaderP` i) . p2) a- respond a = ReaderP $ \_ -> respond a- (p1 />/ p2) a = ReaderP $ \i ->- ((`unReaderP` i) . p1 />/ (`unReaderP` i) . p2) a+instance (Proxy p )+ => MFunctor (ReaderP i p a' a b' b) where+ hoist = hoist_P +instance (Proxy p )+ => Proxy (ReaderP i p) where+ p1 >-> p2 = \c'1 -> ReaderP (\i ->+ ((\b' -> unReaderP (p1 b' ) i)+ >-> (\c'2 -> unReaderP (p2 c'2) i) ) c'1 )+ {- p1 >-> p2 = \c' -> ReaderP $ \i ->+ ((`unReaderP` i) . p1 >-> (`unReaderP` i) . p2) c' -}++ p1 >~> p2 = \c'1 -> ReaderP (\i ->+ ((\b' -> unReaderP (p1 b' ) i)+ >~> (\c'2 -> unReaderP (p2 c'2) i) ) c'1 )+ {- p1 >~> p2 = \c' -> ReaderP $ \i ->+ ((`unReaderP` i) . p1 >~> (`unReaderP` i) . p2) c' -}++ return_P = \r -> ReaderP (\_ -> return_P r)+ m ?>= f = ReaderP (\i ->+ unReaderP m i ?>= \a -> + unReaderP (f a) i )++ request = \a -> ReaderP (\_ -> request a)+ respond = \a -> ReaderP (\_ -> respond a)++ lift_P m = ReaderP (\_ -> lift_P m)++ hoist_P nat p = ReaderP (\i -> hoist_P nat (unReaderP p i))+ -- hoist_P nat = ReaderP . fmap (hoist_P nat) . unReaderP++instance (Interact p)+ => Interact (ReaderP i p) where+ p1 \>\ p2 = \c'1 -> ReaderP (\i ->+ ((\b' -> unReaderP (p1 b' ) i)+ \>\ (\c'2 -> unReaderP (p2 c'2) i) ) c'1 )+ {- p1 \>\ p2 = \c' -> ReaderP $ \i ->+ ((`unReaderP` i) . p1 \>\ (`unReaderP` i) . p2) c' -}++ p1 />/ p2 = \a1 -> ReaderP (\i ->+ ((\b -> unReaderP (p1 b ) i)+ />/ (\a2 -> unReaderP (p2 a2) i) ) a1 )+ {- p1 />/ p2 = \a -> ReaderP $ \i ->+ ((`unReaderP` i) . p1 />/ (`unReaderP` i) . p2) a -}+ instance ProxyTrans (ReaderP i) where- liftP m = ReaderP $ \_ -> m+ liftP m = ReaderP (\_ -> m) +instance PFunctor (ReaderP i) where+ hoistP nat = ReaderP . (nat .) . unReaderP+ -- | Run a 'ReaderP' computation, supplying the environment runReaderP :: i -> ReaderP i p a' a b' b m r -> p a' a b' b m r runReaderP i m = unReaderP m i -- | Run a 'ReaderP' \'@K@\'leisli arrow, supplying the environment runReaderK :: i -> (q -> ReaderP i p a' a b' b m r) -> (q -> p a' a b' b m r)-runReaderK i = (runReaderP i .)+runReaderK i p q = runReaderP i (p q)+-- runReaderK i = (runReaderP i .) -- | Modify a computation's environment (a more general version of 'local') withReaderP- :: (Monad (p a' a b' b m))- => (j -> i) -> ReaderP i p a' a b' b m r -> ReaderP j p a' a b' b m r-withReaderP f r = ReaderP $ unReaderP r . f+ :: (j -> i) -> ReaderP i p a' a b' b m r -> ReaderP j p a' a b' b m r+withReaderP f p = ReaderP (\i -> unReaderP p (f i))+-- withReaderP f p = ReaderP $ unReaderP p . f -- | Get the environment-ask :: (Monad (p a' a b' b m)) => ReaderP i p a' a b' b m i-ask = ReaderP return+ask :: (Proxy p, Monad m) => ReaderP i p a' a b' b m i+ask = ReaderP return_P -- | Get a function of the environment-asks :: (Monad (p a' a b' b m)) => (i -> r) -> ReaderP i p a' a b' b m r-asks f = ReaderP (return . f)+asks :: (Proxy p, Monad m) => (i -> r) -> ReaderP i p a' a b' b m r+asks f = ReaderP (\i -> return_P (f i)) -- | Modify a computation's environment (a specialization of 'withReaderP') local- :: (Monad (p a' a b' b m))- => (i -> i) -> ReaderP i p a' a b' b m r -> ReaderP i p a' a b' b m r+ :: (i -> i) -> ReaderP i p a' a b' b m r -> ReaderP i p a' a b' b m r local = withReaderP
Control/Proxy/Trans/State.hs view
@@ -1,8 +1,6 @@-{-| This module provides the proxy transformer equivalent of 'StateT'.-- Sequencing of computations is strict. -}+-- | This module provides the proxy transformer equivalent of 'StateT'. -{-# LANGUAGE FlexibleContexts, KindSignatures #-}+{-# LANGUAGE KindSignatures #-} module Control.Proxy.Trans.State ( -- * StateP@@ -21,98 +19,148 @@ ) where import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))+import Control.Monad (MonadPlus(mzero, mplus)) import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (Channel(idT, (>->)))+import Control.MFunctor (MFunctor(hoist))+import Control.PFunctor (PFunctor(hoistP))+import Control.Proxy.Class import Control.Proxy.Trans (ProxyTrans(liftP)) --- | The strict 'State' proxy transformer+-- | The 'State' proxy transformer newtype StateP s p a' a b' b (m :: * -> *) r = StateP { unStateP :: s -> p a' a b' b m (r, s) } -instance (Monad (p a' a b' b m)) => Functor (StateP s p a' a b' b m) where- fmap = liftM+instance (Proxy p, Monad m)+ => Functor (StateP s p a' a b' b m) where+ fmap f p = StateP (\s0 ->+ unStateP p s0 ?>= \(x, s1) ->+ return_P (f x, s1) ) -instance (Monad (p a' a b' b m)) => Applicative (StateP s p a' a b' b m) where- pure = return- (<*>) = ap+{- As far as I can tell, there is no way to write this using an Applicative+ context -}+instance (Proxy p, Monad m)+ => Applicative (StateP s p a' a b' b m) where+ pure = return+ p1 <*> p2 = StateP (\s0 ->+ unStateP p1 s0 ?>= \(f, s1) ->+ unStateP p2 s1 ?>= \(x, s2) ->+ return_P (f x, s2) ) -instance (Monad (p a' a b' b m)) => Monad (StateP s p a' a b' b m) where- return a = StateP $ \s -> return (a, s)- m >>= f = StateP $ \s -> do- (a, s') <- unStateP m s- unStateP (f a) s'+instance (Proxy p, Monad m)+ => Monad (StateP s p a' a b' b m) where+ return = return_P+ (>>=) = (?>=) -instance (MonadPlus (p a' a b' b m))- => Alternative (StateP s p a' a b' b m) where+instance (MonadPlusP p, Monad m)+ => Alternative (StateP s p a' a b' b m) where empty = mzero (<|>) = mplus -instance (MonadPlus (p a' a b' b m)) => MonadPlus (StateP s p a' a b' b m) where- mzero = StateP $ \_ -> mzero- mplus m1 m2 = StateP $ \s -> mplus (unStateP m1 s) (unStateP m2 s)+instance (MonadPlusP p )+ => MonadPlusP (StateP s p) where+ mzero_P = StateP (\_ -> mzero_P)+ mplus_P m1 m2 = StateP (\s -> mplus_P (unStateP m1 s) (unStateP m2 s)) -instance (MonadTrans (p a' a b' b)) => MonadTrans (StateP s p a' a b' b) where- lift m = StateP $ \s -> lift $ liftM (\r -> (r, s)) m+instance (MonadPlusP p, Monad m)+ => MonadPlus (StateP s p a' a b' b m) where+ mzero = mzero_P+ mplus = mplus_P -instance (MonadIO (p a' a b' b m)) => MonadIO (StateP s p a' a b' b m) where- liftIO m = StateP $ \s -> liftIO $ liftM (\r -> (r, s)) m+instance (Proxy p )+ => MonadTrans (StateP s p a' a b' b) where+ lift = lift_P -instance (MFunctor (p a' a b' b)) => MFunctor (StateP s p a' a b' b) where- mapT nat = StateP . fmap (mapT nat) . unStateP+instance (MonadIOP p )+ => MonadIOP (StateP s p) where+ liftIO_P m = StateP (\s -> liftIO_P (m >>= \r -> return (r, s))) -instance (Channel p) => Channel (StateP s p) where- idT a = StateP $ \_ -> idT a- (p1 >-> p2) a = StateP $ \s ->- ((`unStateP` s) . p1 >-> (`unStateP` s) . p2) a+instance (MonadIOP p, MonadIO m)+ => MonadIO (StateP s p a' a b' b m) where+ liftIO = liftIO_P +instance (Proxy p )+ => MFunctor (StateP s p a' a b' b) where+ hoist = hoist_P++instance (Proxy p )+ => Proxy (StateP s p) where+ p1 >-> p2 = \c'1 -> StateP (\s ->+ ((\b' -> unStateP (p1 b') s) >-> (\c'2 -> unStateP (p2 c'2) s)) c'1 )+ {- (p1 >-> p2) = \c' -> StateP $ \s ->+ ((`unStateP` s) . p1 >-> (`unStateP` s) . p2) c' -}++ p1 >~> p2 = \c'1 -> StateP (\s ->+ ((\b' -> unStateP (p1 b') s) >~> (\c'2 -> unStateP (p2 c'2) s)) c'1 )+ {- (p1 >~> p2) = \c' -> StateP $ \s ->+ ((`unStateP` s) . p1 >~> (`unStateP` s) . p2) c' -}++ request = \a' -> StateP (\s -> request a' ?>= \a -> return_P (a , s))+ respond = \b -> StateP (\s -> respond b ?>= \b' -> return_P (b', s))++ return_P = \r -> StateP (\s -> return_P (r, s))+ m ?>= f = StateP (\s ->+ unStateP m s ?>= \(a, s') ->+ unStateP (f a) s' )++ lift_P m = StateP (\s -> lift_P (m >>= \r -> return (r, s)))++ hoist_P nat p = StateP (\s -> hoist_P nat (unStateP p s))+ -- hoist nat = StateP . fmap (hoist nat) . unStateP+ instance ProxyTrans (StateP s) where- liftP m = StateP $ \s -> liftM (\r -> (r, s)) m+ liftP m = StateP (\s -> m ?>= \r -> return_P (r, s)) +instance PFunctor (StateP s) where+ hoistP nat = StateP . (nat .) . unStateP+ -- | Run a 'StateP' computation, producing the final result and state runStateP :: s -> StateP s p a' a b' b m r -> p a' a b' b m (r, s) runStateP s m = unStateP m s -- | Run a 'StateP' \'@K@\'leisli arrow, procuding the final result and state runStateK :: s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m (r, s))-runStateK s = (runStateP s .)+runStateK s k q = unStateP (k q) s+-- runStateK s = (runStateP s .) -- | Evaluate a 'StateP' computation, but discard the final state evalStateP- :: (Monad (p a' a b' b m)) => s -> StateP s p a' a b' b m r -> p a' a b' b m r-evalStateP s = liftM fst . runStateP s+ :: (Proxy p, Monad m) => s -> StateP s p a' a b' b m r -> p a' a b' b m r+evalStateP s p = unStateP p s ?>= \x -> return_P (fst x)+-- evalStateP s = liftM fst . runStateP s -- | Evaluate a 'StateP' \'@K@\'leisli arrow, but discard the final state evalStateK- :: (Monad (p a' a b' b m))+ :: (Proxy p, Monad m) => s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m r)-evalStateK s = (evalStateP s .)+evalStateK s k q = evalStateP s (k q)+-- evalStateK s = (evalStateP s .) -- | Evaluate a 'StateP' computation, but discard the final result execStateP- :: (Monad (p a' a b' b m)) => s -> StateP s p a' a b' b m r -> p a' a b' b m s-execStateP s = liftM snd . runStateP s+ :: (Proxy p, Monad m) => s -> StateP s p a' a b' b m r -> p a' a b' b m s+execStateP s p = unStateP p s ?>= \x -> return_P (snd x)+-- execStateP s = liftM snd . runStateP s -- | Evaluate a 'StateP' \'@K@\'leisli arrow, but discard the final result execStateK- :: (Monad (p a' a b' b m))+ :: (Proxy p, Monad m) => s -> (q -> StateP s p a' a b' b m r) -> (q -> p a' a b' b m s)-execStateK s = (execStateP s .)+execStateK s k q = execStateP s (k q)+-- execStateK s = (execStateP s .) -- | Get the current state-get :: (Monad (p a' a b' b m)) => StateP s p a' a b' b m s-get = StateP $ \s -> return (s, s)+get :: (Proxy p, Monad m) => StateP s p a' a b' b m s+get = StateP (\s -> return_P (s, s)) -- | Set the current state-put :: (Monad (p a' a b' b m)) => s -> StateP s p a' a b' b m ()-put s = StateP $ \_ -> return ((), s)+put :: (Proxy p, Monad m) => s -> StateP s p a' a b' b m ()+put s = StateP (\_ -> return_P ((), s)) -- | Modify the current state using a function-modify :: (Monad (p a' a b' b m)) => (s -> s) -> StateP s p a' a b' b m ()-modify f = StateP $ \s -> return ((), f s)+modify :: (Proxy p, Monad m) => (s -> s) -> StateP s p a' a b' b m ()+modify f = StateP (\s -> return_P ((), f s)) -- | Get the state filtered through a function-gets :: (Monad (p a' a b' b m)) => (s -> r) -> StateP s p a' a b' b m r-gets f = StateP $ \s -> return (f s, s)+gets :: (Proxy p, Monad m) => (s -> r) -> StateP s p a' a b' b m r+gets f = StateP (\s -> return_P (f s, s))
− Control/Proxy/Trans/Tutorial.hs
@@ -1,415 +0,0 @@--- | This module provides the tutorial for the "Control.Proxy.Trans" hierarchy--module Control.Proxy.Trans.Tutorial (- -- * Motivation- -- $motivation-- -- * Proxy Transformers- -- $proxytrans-- -- * Compatibility- -- $compatibility-- -- * Proxy Transformer Stacks- -- $stacks- ) where--import Control.Monad.Trans.Class-import Control.Monad.Trans.State-import Control.Proxy-import Control.Proxy.Trans.Either-import Control.Proxy.Trans.State--{- $motivation- In a 'Session', all composed proxies share effects within the base monad.- To see how, consider the following simple 'Session':--> client1 :: () -> Client () () (StateT Int IO) r-> client1 () = forever $ do-> s <- lift get-> lift $ lift $ putStrLn $ "Client: " ++ show s-> lift $ put (s + 1)-> request ()->-> server1 :: () -> Server () () (StateT Int IO) r-> server1 () = forever $ do-> s <- lift get-> lift $ lift $ putStrLn $ "Server: " ++ show s-> lift $ put (s + 1)-> respond ()-->>> (`evalStateT` 0) $ runProxy $ client1 <-< server1-Client: 0-Server: 1-Client: 2-Server: 3-Client: 4-Server: 5-...-- The client and server share the same state, which is sometimes not what we- want. We can easily solve this by running each 'Proxy' with its own local- state by changing the order of the 'Proxy' and 'StateT' monad transformers:--> client2 :: () -> StateT Int (Client () () IO) r-> client2 () = forever $ do-> s <- get-> lift $ lift $ putStrLn $ "Client: " ++ show s-> put (s + 1)-> lift $ request ()->-> server2 :: () -> StateT Int (Server () () IO) r-> server2 () = forever $ do-> s <- get-> lift $ lift $ putStrLn $ "Server: " ++ show s-> put (s + 1)-> lift $ respond ()-- ... but then we can no longer compose them directly. We have to first- unwrap each one with 'evalStateT' before composing:-->>> runProxy $ (`evalStateT` 0) . client2 <-< (`evalStateT` 0) . server2-Client: 0-Server: 0-Client: 1-Server: 1-Client: 2-Server: 2-...-- Here's another example: suppose we want to handle errors within proxies. We- could try adding 'EitherT' to the base monad like so:--> import Control.Error->-> client3 :: () -> Client () () (EitherT String IO) ()-> client3 () = forM_ [1..] $ \i -> do-> lift $ lift $ print i-> request ()->-> server3 :: (Monad m) => () -> Server () () (EitherT String m) r-> server3 () = lift $ left "ERROR"-->>> runEithert $ runProxy $ client2 <-< server2-1-Left "ERROR"-- Unfortunately, we can't modify @server2@ to 'catchT' that error because we- cannot access the inner 'EitherT' monad transformer until we run the- 'Session'. We'd really prefer to place the 'EitherT' monad transformer- /outside/ the 'Proxy' monad transformer so that we can catch and handle- errors locally within a 'Proxy' without disturbing other proxies:--> client4 :: () -> EitherT String (Client () () IO) ()-> client4 () = forM_ [1..] $ \i -> do-> lift $ lift $ print i-> lift $ request ()->-> server4 :: () -> EitherT String (Server () () IO) ()-> server4 () = (forever $ do-> lift $ respond ()-> throwT "Error" )-> `catchT` (\str -> do-> lift $ lift $ putStrLn $ "Caught: " ++ str-> server4 () )-- However, this solution similarly requires unwrapping the client and server- using 'runEitherT' before composing them:-->>> runProxy $ runEitherT . client4 <-< runEitherT . server4-1-Caught: Error-2-Caught: Error-3-Caught: Error-...---}--{- $proxytrans- We need some way to layer monad transformers /outside/ the proxy type- without interfering with 'Proxy' composition. To do this, we overload- 'Proxy' composition using the 'Channel' type class from- "Control.Proxy.Class":--> class Channel p where-> idT :: (Monad) m => a' -> p a' a a' a m r-> (>->)-> :: (Monad m)-> => (b' -> p a' a b' b m r)-> -> (c' -> p b' b c' c m r)-> -> (c' -> p a' a c' c m r)-- Obviously, 'Proxy' implements this class:--> instance Channel Proxy where ...-- ... but we would also like our monad transformers layered outside the- 'Proxy' type to also implement the 'Channel' class so that we could compose- them directly without unwrapping. Unfortunately, these monad transformers- do not fit the signature of the 'Channel' class.-- Fortunately, the "Control.Proxy.Trans" hierarchy provides several common- monad transformers which have been upgraded to fit the 'Channel' type class.- I call these \"proxy transformers\".-- For example, "Control.Proxy.Trans.State" provides a proxy transformer- equivalent to @Control.Monad.Trans.State@. Similarly,- "Control.Proxy.Trans.Either" provides a proxy transformer equivalent to- @Control.Monad.Trans.Either@.-- Let's use a working code example to demonstrate how to use them:--> import Control.Proxy.Trans.State-> -> client5 :: () -> StateP Int Proxy () () () C IO r-> client5 () = forever $ do-> s <- get-> liftP $ lift $ putStrLn $ "Client: " ++ show s-> put (s + 1)-> liftP $ request ()->-> server5 :: () -> StateP Int Proxy C () () () IO r-> server5 () = forever $ do-> s <- get-> liftP $ lift $ putStrLn $ "Server: " ++ show s-> put (s + 1)-> liftP $ respond ()-- You'll see that our type signatures changed. Now we use 'StateP' instead of- 'StateT'. However, 'StateP' does not transform monads, but instead- transforms proxies.-- To see this, let's first study the kind of 'StateT'. If we first define:--> kind MonadKind = * -> *-- Then @StateT s@ takes a monad, and returns a new monad:--> StateT s :: MonadKind -> MonadKind-- Now consider the kind of a 'Proxy'-like type constructor suitable for the- 'Channel' type class:--> kind ProxyKind = * -> * -> * -> * -> (* -> *) -> * -> *-- Then @StateP s@ takes a 'Proxy'-like and returns a new 'Proxy'-like type:--> StateP s :: ProxyKind -> ProxyKind-- This is why I call these \"proxy transformers\" and not monad transformers.- They all take some 'Proxy'-like type that implements 'Channel' and transform- it into a new 'Proxy'-like type that also implements 'Channel'. For- example, 'StateP' implement the following instance:--> instance (Channel p) => Channel (StateP s p) where ...-- All proxy transformers guarantee that if the base proxy implements the- 'Channel' type class, then the transformed proxy also implements the- 'Channel' type class. This means that you can build a proxy transformer- stack, just like you might build a monad transformer stack.-- Unfortunately, in order to use proxy transformers, you must expand out the- 'Client' and 'Server' type synonyms, which are not compatible with proxy- transformers. Sorry! This is why there are no 'Server' or 'Client' type- synonyms in the types of our new client and server and I had to write out- all the inputs and outputs.-- Notice how the outermost 'lift' statements in our client and server have- changed to 'liftP'. 'liftP' replaces 'lift' for proxy transformers, and it- lifts any action in the base proxy to an action in the transformed proxy.- In the previous example, the base proxy was 'Proxy' and the transformed- proxy was @StateP s Proxy@, so 'liftP's type got specialized to:--> liftP :: Proxy a' a b' b m r -> StateP s Proxy a' a b' b m r-- The 'ProxyTrans' class defines 'liftP', and all proxy transformers implement- the 'ProxyTrans' class. Since proxies are still monads, 'liftP' must- behave just like 'lift' and obey the monad transformer laws:--> (liftP .) return = return->-> (liftP .) (f >=> g) = (liftP .) f >=> (liftP .) g-- But, unlike 'lift', 'liftP' obeys one extra set of laws that guarantee it - also lifts composition sensibly:--> (liftP .) idT = idT->-> (liftP .) (f >-> g) = (liftP .) f >-> (liftP .) g-- In fact, this @(liftP .)@ pattern is so ubiquitous, that the 'ProxyTrans'- class provides the additional 'mapP' method for convenience:--> mapP = (liftP .)-- Proxy transformers automatically derive how to lift composition correctly- and also guarantee that the derived composition obeys the category laws if- the base composition obeyed the category laws. Since 'Proxy' composition- obeys the category laws, any proxy transformer stack built on top of it- automatically derives a composition operation that is correct by- construction.-- Let's prove this by directly composing our 'StateP'-extended proxies without- unwrapping them:--> :t client5 <-< server5 :: () -> StateP Int Proxy C () () C IO r-- However, we still have to unwrap the final 'StateP' 'Session' before we can- pass it to 'runProxy'. We use 'runStateK' for this purpose:-->>> runProxy $ runStateK 0 $ client5 <-< server5-Client: 0-Server: 0-Client: 1-Server: 1-Client: 2-Server: 2-Client: 3-Server: 3-...-- Keep in mind that 'runStateK' takes the initial state as its first argument,- unlike 'runStateT'. I break from the @transformers@ convention for- syntactic convenience.-- We can similarly fix our 'EitherT' example, using 'EitherP' from- "Control.Proxy.Trans.Either":--> import Control.Proxy.Trans.Either as E->-> client6 :: () -> EitherP String Proxy () () () C IO ()-> client6 () = forM_ [1..] $ \i -> do-> liftP $ lift $ print i-> liftP $ request ()->-> server6 :: () -> EitherP String Proxy C () () () IO ()-> server6 () = (forever $ do-> liftP $ respond ()-> E.throw "Error" )-> `E.catch` (\str -> do-> liftP $ lift $ putStrLn $ "Caught: " ++ str-> server6 () )-->>> runProxy $ runEitherK $ client6 <-< server6-1-Caught: Error-2-Caught: Error-3-Caught: Error-...---}--{- $compatibility- Proxy transformers do more than just lift composition. They automatically- promote proxies written in the base monad. For example, what if I wanted to- use the 'takeB_' proxy from "Control.Proxy.Prelude.Base" to cap the number- of results? I can't compose it directly because it uses the 'Proxy' type:--> takeB_ :: (Monad m) => Int -> a' -> Proxy a' a a' a m ()-- ... whereas @client6@ and @server6@ use @EitherP String Proxy@. However,- this doesn't matter because we can automatically lift 'takeB_' to be- compatible with them using 'mapP':-->>> runProxy $ runEitherK $ client6 <-< mapP (takeB_ 2) <-< server6-1-Caught: Error-2-Caught:Error-- 'mapP' promotes any proxy written using the base proxy type to automatically- be compatible with proxies written using the extended proxy type. This- means you can safely write utility proxies using the smallest feature set- they require and promote them as necessary to work with more extended- feature sets. This ensures that any proxies you write always remain- forwards-compatible as people write new extensions.--}--{- $stacks- You can stack proxy transformers to combine their effects, such as in the- following example, which combines everything we've used so far:--> client7 :: () -> EitherP String (StateP Int Proxy) () Int () C IO r-> client7 () = do-> n <- liftP get-> liftP $ liftP $ lift $ print n-> n' <- liftP $ liftP $ request ()-> liftP $ put n'-> E.throw "ERROR"-->>> runProxy $ runStateK 0 $ runEitherK $ client7 <-< mapP (mapP (enumFromS 1))-0-(Left "Error", 1)-- But that's still not the full story! For calls to the base monad (i.e. 'IO'- in this case), you don't need to precede them with all those 'liftP's.- Every proxy transformer also correctly derives 'MonadTrans', so you can dig- straight to the base monad by just calling 'lift' at the outer-most level:--> client7 :: () -> EitherP String (StateP Int Proxy) () Int () C IO r-> client7 () = do-> n <- liftP get-> lift $ print n -- Much better!-> n' <- liftP $ liftP $ request ()-> liftP $ put n'-> E.throw "ERROR"-- Also, you can combine multiple proxy transformers into a single proxy- transformer, just like you would with monad transformers:--> newtype BothP e s p a' a b' b m r =-> BothP { unBothP :: EitherP e (StateP s p) a' a b' b m r }-> deriving (Functor, Applicative, Monad, MonadTrans, Channel)-> -> instance ProxyTrans (BothP e s) where-> liftP = BothP . liftP . liftP-> -> runBoth-> :: (Monad m)-> => s-> -> (b' -> BothP e s p a' a b' b m r)-> -> (b' -> p a' a b' b m (Either e r, s))-> runBoth s = runStateK s . runEitherK . fmap unBothP-> -> get' :: (Monad (p a' a b' b m), Channel p)-> => BothP e s p a' a b' b m s-> get' = BothP $ liftP get-> -> put' :: (Monad (p a' a b' b m), Channel p)-> => s -> BothP e s p a' a b' b m ()-> put' x = BothP $ liftP $ put x-> -> throw' :: (Monad (p a' a b' b m), Channel p)-> => e -> BothP e s p a' a b' b m r-> throw' e = BothP $ E.throw e-- Then we can write proxies using this new proxy transformer of ours:--> client8 :: () -> BothP String Int Proxy () Int () C IO r-> client8 () = do-> n <- get'-> lift $ print n-> n' <- liftP $ request ()-> put' n'-> throw' "ERROR"-->>> runProxy $ runBoth 0 $ client8 <-< mapP (enumFromS 1)-0-(Left "ERROR",1)-- Note that 'request' and 'respond' are not automatically liftable, because of- technical limitations with Haskell type classes. When I resolve these- issues they will also be automatically promoted by proxy transformers. For- now, you must lift them manually using 'liftP':--> request = (liftP .) request-> respond = (liftP .) respond-- The left 'request' and 'respond' in the above equations are what the lifted- definitions would be for each proxy transformer if Haskell's type class- system didn't get in my way.--}
Control/Proxy/Trans/Writer.hs view
@@ -6,7 +6,7 @@ The underlying implementation uses the state monad to avoid quadratic blowup from left-associative binds. -} -{-# LANGUAGE FlexibleContexts, KindSignatures #-}+{-# LANGUAGE KindSignatures #-} module Control.Proxy.Trans.Writer ( -- * WriterP@@ -21,11 +21,12 @@ ) where import Control.Applicative (Applicative(pure, (<*>)), Alternative(empty, (<|>)))-import Control.Monad (liftM, ap, MonadPlus(mzero, mplus))+import Control.Monad (MonadPlus(mzero, mplus)) import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.MFunctor (MFunctor(mapT))-import Control.Proxy.Class (Channel(idT, (>->)))+import Control.MFunctor (MFunctor(hoist))+import Control.PFunctor (PFunctor(hoistP))+import Control.Proxy.Class import Control.Proxy.Trans (ProxyTrans(liftP)) import Data.Monoid (Monoid(mempty, mappend)) @@ -33,51 +34,88 @@ newtype WriterP w p a' a b' b (m :: * -> *) r = WriterP { unWriterP :: w -> p a' a b' b m (r, w) } -instance (Monad (p a' a b' b m))- => Functor (WriterP w p a' a b' b m) where- fmap = liftM+instance (Proxy p, Monad m)+ => Functor (WriterP w p a' a b' b m) where+ fmap f p = WriterP (\w0 ->+ unWriterP p w0 ?>= \(x, w1) ->+ return_P (f x, w1) ) -instance (Monad (p a' a b' b m))- => Applicative (WriterP w p a' a b' b m) where- pure = return- (<*>) = ap+instance (Proxy p, Monad m)+ => Applicative (WriterP w p a' a b' b m) where+ pure = return+ fp <*> xp = WriterP (\w0 ->+ unWriterP fp w0 ?>= \(f, w1) ->+ unWriterP xp w1 ?>= \(x, w2) ->+ return_P (f x, w2) )+ -- (<*>) = ap -instance (Monad (p a' a b' b m))- => Monad (WriterP w p a' a b' b m) where- return a = WriterP $ \w -> return (a, w)- m >>= f = WriterP $ \w -> do- (a, w') <- unWriterP m w- unWriterP (f a) w'+instance (Proxy p, Monad m)+ => Monad (WriterP w p a' a b' b m) where+ return = return_P+ (>>=) = (?>=) -instance (MonadPlus (p a' a b' b m))- => Alternative (WriterP w p a' a b' b m) where+instance (MonadPlusP p, Monad m)+ => Alternative (WriterP w p a' a b' b m) where empty = mzero (<|>) = mplus -instance (MonadPlus (p a' a b' b m))- => MonadPlus (WriterP w p a' a b' b m) where- mzero = WriterP $ \_ -> mzero- mplus m1 m2 = WriterP $ \w -> mplus (unWriterP m1 w) (unWriterP m2 w)+instance (MonadPlusP p )+ => MonadPlusP (WriterP w p) where+ mzero_P = WriterP (\_ -> mzero_P)+ mplus_P m1 m2 = WriterP (\w -> mplus_P (unWriterP m1 w) (unWriterP m2 w)) -instance (MonadTrans (p a' a b' b))- => MonadTrans (WriterP w p a' a b' b) where- lift m = WriterP $ \w -> lift $ liftM (\r -> (r, w)) m+instance (MonadPlusP p, Monad m)+ => MonadPlus (WriterP w p a' a b' b m) where+ mzero = mzero_P+ mplus = mplus_P -instance (MonadIO (p a' a b' b m))- => MonadIO (WriterP w p a' a b' b m) where- liftIO m = WriterP $ \w -> liftIO $ liftM (\r -> (r, w)) m+instance (Proxy p )+ => MonadTrans (WriterP w p a' a b' b) where+ lift = lift_P -instance (MFunctor (p a' a b' b)) => MFunctor (WriterP w p a' a b' b) where- mapT nat = WriterP . fmap (mapT nat) . unWriterP+instance (MonadIOP p )+ => MonadIOP (WriterP w p) where+ liftIO_P m = WriterP (\w -> liftIO_P (m >>= \r -> return (r, w))) -instance (Channel p) => Channel (WriterP w p) where- idT a = WriterP $ \_ -> idT a- (p1 >-> p2) a = WriterP $ \w ->- ((`unWriterP` w) . p1 >-> (`unWriterP` w) . p2) a+instance (MonadIOP p, MonadIO m)+ => MonadIO (WriterP w p a' a b' b m) where+ liftIO = liftIO_P -instance (Monoid w) => ProxyTrans (WriterP w) where- liftP m = WriterP $ \w -> liftM (\r -> (r, w)) m+instance (Proxy p )+ => MFunctor (WriterP w p a' a b' b) where+ hoist = hoist_P +instance (Proxy p )+ => Proxy (WriterP w p) where+ p1 >-> p2 = \c'1 -> WriterP (\w ->+ ((\b' -> unWriterP (p1 b') w) >-> (\c'2 -> unWriterP (p2 c'2) w)) c'1 )+ {- p1 >-> p2 = \c' -> WriterP $ \w ->+ ((`unWriterP` w) . p1 >-> (`unWriterP` w) . p2) c' -}++ p1 >~> p2 = \c'1 -> WriterP (\w ->+ ((\b' -> unWriterP (p1 b') w) >~> (\c'2 -> unWriterP (p2 c'2) w)) c'1 )+ {- p1 >~> p2 = \c' -> WriterP $ \w ->+ ((`unWriterP` w) . p1 >~> (`unWriterP` w) . p2) c' -}++ request = \a' -> WriterP (\w -> request a' ?>= \a -> return_P (a, w))+ respond = \b -> WriterP (\w -> respond b ?>= \b' -> return_P (b', w))++ return_P = \r -> WriterP (\w -> return_P (r, w))+ m ?>= f = WriterP (\w ->+ unWriterP m w ?>= \(a, w') ->+ unWriterP (f a) w' )++ lift_P m = WriterP (\w -> lift_P (m >>= \r -> return (r, w)))++ hoist_P nat p = WriterP (\w -> hoist_P nat (unWriterP p w))+ -- hoist_P nat = WriterP . fmap (hoist_P nat) . unWriterP++instance ProxyTrans (WriterP w) where+ liftP m = WriterP (\w -> m ?>= \r -> return_P (r, w))++instance PFunctor (WriterP w) where+ hoistP nat = WriterP . (nat .) . unWriterP+ -- | Run a 'WriterP' computation, producing the final result and monoid runWriterP :: (Monoid w) => WriterP w p a' a b' b m r -> p a' a b' b m (r, w) runWriterP p = unWriterP p mempty@@ -86,26 +124,31 @@ runWriterK :: (Monoid w) => (q -> WriterP w p a' a b' b m r) -> (q -> p a' a b' b m (r, w))-runWriterK = (runWriterP . )+runWriterK k q = runWriterP (k q)+-- runWriterK = (runWriterP . ) -- | Evaluate a 'WriterP' computation, but discard the final result execWriterP- :: (Monad (p a' a b' b m), Monoid w)+ :: (Proxy p, Monad m, Monoid w) => WriterP w p a' a b' b m r -> p a' a b' b m w-execWriterP m = liftM snd $ runWriterP m+execWriterP m = runWriterP m ?>= \(_, w) -> return_P w+-- execWriterP m = liftM snd $ runWriterP m -- | Evaluate a 'WriterP' \'@K@\'leisli arrow, but discard the final result execWriterK- :: (Monad (p a' a b' b m), Monoid w)+ :: (Proxy p, Monad m, Monoid w) => (q -> WriterP w p a' a b' b m r) -> (q -> p a' a b' b m w)-execWriterK = (execWriterP .)+execWriterK k q= execWriterP (k q) -- | Add a value to the monoid-tell :: (Monad (p a' a b' b m), Monoid w) => w -> WriterP w p a' a b' b m ()-tell w' = WriterP $ \w -> let w'' = mappend w w' in w'' `seq` return ((), w'')+tell :: (Proxy p, Monad m, Monoid w) => w -> WriterP w p a' a b' b m ()+tell w' = WriterP (\w -> let w'' = mappend w w' in w'' `seq` return_P ((), w'')) -- | Modify the result of a writer computation censor- :: (Monad (p a' a b' b m), Monoid w)+ :: (Proxy p, Monad m, Monoid w) => (w -> w) -> WriterP w p a' a b' b m r -> WriterP w p a' a b' b m r-censor f = WriterP . fmap (liftM (\(a, w) -> (a, f w))) . unWriterP+censor f p = WriterP (\w0 ->+ unWriterP p w0 ?>= \(r, w1) ->+ return_P (r, f w1) )+-- censor f = WriterP . fmap (liftM (\(r, w) -> (r, f w))) . unWriterP
Control/Proxy/Tutorial.hs view
@@ -1,550 +1,1890 @@--- | This module provides the tutorial for "Control.Proxy"--module Control.Proxy.Tutorial (- -- * Basics- -- $basics-- -- * Types- -- $types-- -- * Composition- -- $composition-- -- * Idioms- -- $idioms-- -- * Reusability- -- $reuse-- -- * Mixing monads and composition- -- $monads-- -- * Utility proxies- -- $utility-- -- * Pipe compatibility- -- $pipes- ) where--import Control.Monad.Trans.Class-import Control.Proxy--{- $basics- The 'Proxy' type models composable chains of client-server interactions.-- A 'Proxy' is a monad transformer that extends the base monad with the- ability to 'request' input from upstream and 'respond' with output to- downstream.-- For example, consider the following toy remote procedure call- 'Server':--> import Control.Proxy-> import Control.Monad.Trans->-> incrementer :: Int -> Server Int Int IO r-> incrementer question = do-> lift $ putStrLn $ "Server received : " ++ show question-> let answer = question + 1-> lift $ putStrLn $ "Server responded: " ++ show answer-> nextQuestion <- respond answer-> incrementer nextQuestion-- We can understand what the 'Server' does just by looking at the type:--> | Question | Answer | Base monad | Return value-> Server Int Int IO r-- Our 'Server' receives questions about 'Int's, and responds with answers that- are 'Int's. The base monad is 'IO' because our 'Server' 'lift's two- 'putStrLn' statements to chat out loud. The return value is polymorphic- because our 'Server' never terminates.-- Note that the base monad doesn't always need to be 'IO'. Unlike typical- servers, these kinds of 'Server's are pure syntax trees with no side- effects unless you call 'lift'.-- Now we can write a 'Client' that interacts with our 'Server':--> import Control.Monad->-> oneTwoThree :: () -> Client Int Int IO ()-> oneTwoThree () = forM_ [1, 2, 3] $ \question -> do-> lift $ putStrLn $ "Client requested: " ++ show question-> answer <- request question-> lift $ putStrLn $ "Client received : " ++ show answer-> lift $ putStrLn "*"-- Again, the type explains what the 'Client' does:--> | Question | Answer | Base monad | Return value-> Client Int | Int | IO | ()-- Our 'Client' asks questions about 'Int's and receives answers that are- 'Int's. The 'Client' also uses 'IO' as the base monad.-- We can then compose the 'Client' and 'Server' into a 'Session' using the- ('<-<') operator:--> session :: () -> Session IO ()-> session = oneTwoThree <-< incrementer-- The 'Session' type indicates that we have a self-contained session that we- can run in the 'IO' monad. We run it using the the 'runProxy' function:-->>> runProxy session :: IO ()-Client requested: 1-Server received : 1-Server responded: 2-Client received : 2-*-Client requested: 2-Server received : 2-Server responded: 3-Client received : 3-*-Client requested: 3-Server received : 3-Server responded: 4-Client received : 4-*-- Now, let's add an intermediate 'Proxy' between the 'Client' and 'Server'- that subtly tampers with the stream going through it:--> malicious :: Int -> Proxy Int Int Int Int IO r-> malicious question = do-> question' <- if (question > 2)-> then do-> lift $ putStrLn "MUAHAHAHA!"-> return (question + 1)-> else return question-> answer <- request question'-> nextQuestion <- respond answer-> malicious nextQuestion-- The type tells us what our 'Proxy' does:--> | Upstream (Server) | Downstream (Client) |-> | Question | Answer | Question | Answer | Base monad | Return value-> Proxy Int Int Int Int IO r-- A 'Proxy' bridges two separate interfaces. The first two parameters define- the upstream interface (i.e. in the 'Server' direction) and the second two- parameters define the downstream interface (i.e. in the 'Client' direction).-- We can see if our proxy does its job correctly:-->>> runProxy $ oneTwoThree <-< malicious <-< incrementer-Client requested: 1-Server received : 1-Server responded: 2-Client received : 2-*-Client requested: 2-Server received : 2-Server responded: 3-Client received : 3-*-Client requested: 3-MUAHAHAHA!-Server received : 4-Server responded: 5-Client received : 5-*-- We can also add more proxies as we see fit:-->>> runProxy $ oneTwoThree <-< malicious <-< malicious <-< incrementer -Client requested: 1-Server received : 1-Server responded: 2-Client received : 2-*-Client requested: 2-Server received : 2-Server responded: 3-Client received : 3-*-Client requested: 3-MUAHAHAHA!-MUAHAHAHA!-Server received : 5-Server responded: 6-Client received : 6-*--}--{- $types- You probably noticed something odd: ('<-<') seems to be composing values of- different types. Sometimes it composes a 'Server' or a 'Client' or a- 'Proxy'. In reality, though, both 'Server' and 'Client' are just type- synonyms for special cases of 'Proxy':--> type Server arg ret = Proxy C () arg ret-> type Client arg ret = Proxy arg ret () C-- A 'Server' is just a 'Proxy' that has no upstream interface, and a 'Client'- is just a 'Proxy' that has no downstream interface. In fact, 'Session' is- also a 'Proxy', one with both ends closed:--> type Session = Proxy C () () C-- The 'Proxy' is the unifying type that all other types derive from and- ('<-<') always composes two 'Proxy's and returns a new 'Proxy' of the- correct type.-- You also probably noticed another odd thing: we parametrize every 'Proxy'- on its initial argument:--> +- Initial Arg-> |-> v-> incrementer :: Int -> Server Int Int IO r-> malicious :: Int -> Proxy Int Int Int Int IO r-> oneTwoThree :: () -> Client Int Int IO ()->-> session :: () -> Session IO ()-- This input initializes each 'Proxy' and corresponds to the input on the- downstream interface. I will expand the 'Server' and 'Client' type synonyms- to show this:--> +- Initial Arg = This -+-> | |-> v v-> incrementer :: Int -> Proxy C () Int Int IO r-> malicious :: Int -> Proxy Int Int Int Int IO r-> oneTwoThree :: () -> Proxy Int Int () C IO ()->-> session :: () -> Proxy C () () C IO ()-- Composition supplies the first request through this initial parameter- and all subsequent requests are bound to 'respond' statements.-- This means that the actual types you compose are all of the form:--> proxy :: req_b -> Proxy req_a resp_a req_b resp_b m r--}--{- $composition- 'Proxy' composition posseses an identity 'Proxy' that is completely- transparent to anything upstream or downstream of it:--> idT :: (Monad m) => req -> Proxy req resp req resp m r-> idT question = do-> answer <- request question-> nextQuestion <- respond answer-> idT nextQuestion-- Transparency means that:--> idT <-< p = p->-> p <-< idT = p-- Also, 'Proxy' composition has the nice property that it behaves exactly the- same way no matter how you group components:--> (p1 <-< p2) <-< p3 = p1 <-< (p2 <-< p3)-- This means that ('<-<') and 'idT' define a category, and the above equations- are the category laws. These laws guarantee the following nice- properties of components:-- * You can reason about each component's behavior independently of other- components-- * You don't encounter boundary cases between components-- * You don't encounter edge cases at the 'Server' or 'Client' ends-- The semantics of 'Proxy' composition are simple:-- * 'request' blocks until it receives a response from upstream-- * 'respond' blocks until it receives a new request from downstream-- * If any 'Proxy' in the chain terminates, the entire chain terminates--}--{- $idioms- We frequently encounter the following recurring pattern when writing- 'Proxy's:--> someProxy arg = do-> ...-> nextArg <- respond x-> someProxy nextArg-- "Control.Proxy" provides the 'foreverK' utility function which abstracts- away this manual recursion:--> foreverK f = f >=> foreverK f-- Using 'foreverK', we can simplify the definition of 'incrementer':--> incrementer = foreverK $ \question -> do-> lift $ putStrLn $ "Server received : " ++ show question-> let answer = question + 1-> lift $ putStrLn $ "Server responded: " ++ show answer-> respond answer-- ... which looks exactly like the way you might write server code in another- programming language.-- We can similarly simplify 'malicious' this way:--> malicious = foreverK $ \question -> do-> question' <- if (question > 2)-> then do-> lift $ putStrLn "MUAHAHAHA!"-> return (question + 1)-> else return question-> answer <- request question'-> respond answer-- ... or 'idT':--> idT = foreverK $ \question -> do-> answer <- request question-> respond answer->-> -- or: idT = foreverK (request >=> respond)-> -- = request >=> respond >=> request >=> respond >=> ...--}--{- $reuse- We can mix and match different components to rapidly define emergent- behaviors from a resuable set of core primitives. For example, we could- replace our client with a command line prompt where the user provides the- input to the server:--> inputPrompt :: (Read a, Show b) => () -> Client a b IO r-> inputPrompt () = forever $ do-> str <- lift $ getLine-> let a = read str-> b <- request a-> lift $ print b-> lift $ putStrLn "*"-->>> runProxy $ inputPrompt <-< incrementer-42<Enter>-Server received : 42-Server responded: 43-43-*-666<Enter>-Server received : 666-Server responded: 667-667-*-- Oh no, we lost our useful client diagnostic messages! No worries, we can- abstract that functionality away into its own component:--> diagnoseClient :: (Show a, Show b) => a -> Proxy a b a b IO r-> diagnoseClient = foreverK $ \a -> do-> lift $ putStrLn $ "Client requested: " ++ show a-> b <- request a-> lift $ putStrLn $ "Client received : " ++ show b-> respond b-->>> runProxy $ inputPrompt <-< diagnoseClient <-< incrementer-42<Enter>-Client requested: 42-Server received : 42-Server responded: 43-Client received : 43-43-*-666<Enter>-Client requested: 666-Server received : 666-Server responded: 667-Client received : 667-667-*-- Because of associativity, we can bundle @inputPrompt@ and @diagnoseClient@- into a single black box and not worry that the abstraction will leak due to- grouping issues:--> verboseInput :: (Read a, Show b, Show a) => () -> Client a b IO r-> verboseInput = inputPrompt <-< diagnoseClient-->>> runProxy $ verboseInput <-< incrementer-<Exactly same behavior>-- Or what if I want to cache the results coming out of @incrementer@? I can- define a 'Proxy' to cache all requests going through it:--> import qualified Data.Map as M->-> cache :: (Ord a) => a -> Proxy a b a b IO r-> cache = cache' M.empty->-> cache' m a =-> case M.lookup a m of-> Nothing -> do-> b <- request a-> a' <- respond b-> cache' (M.insert a b m) a'-> Just b -> do-> lift $ putStrLn "Used cache!"-> a' <- respond b-> cache' m a'-->>> runProxy $ verboseInput <-< cache <-< incrementer -42<Enter>-Client requested: 42-Server received : 42-Server responded: 43-Client received : 43-43-*-42<Enter>-Client requested: 42-Used cache!-Client received : 43-43-*-- Note that I don't distinguish between a \"reverse proxy\" or a \"forward- proxy\" since composition doesn't distinguish either. You can attach the- @cache@ 'Proxy' to a 'Client':--> client' = client <-< cache-- ... or to a 'Server':--> server' = cache <-< server-- ... or anywhere in between. It's completely up to you!--}--{- $monads- All the previous examples use a single composition chain, but you need not- restrict yourself to that design pattern. Remember that the result of- composition is a 'Proxy' itself (parametrized by an input), and 'Proxy's are- 'Monad's, so you can bind the result of composition directly within another- @do@ block to generate complex behaviors:--> mixedClient :: () -> Client Int Int IO r-> mixedClient () = do-> oneTwoThree ()-> -- Here we bind composition within a larger do block-> (inputPrompt <-< cache) ()->-> -- or: mixedClient = oneTwoThree >=> (inputPrompt <-< cache)-->>> runProxy $ mixedClient <-< incrementer-Client requested: 1-Server received : 1-Server responded: 2-Client received : 2-*-Client requested: 2-Server received : 2-Server responded: 3-Client received : 3-*-Client requested: 3-Server received : 3-Server responded: 4-Client received : 4-*-42<Enter>-Server received : 42-Server responded: 43-43-*-42<Enter>-Used cache!-43-*-- So feel free to use your imagination! Up until the moment you call- 'runProxy', you can freely mix composition or @do@ notation within each- other.--}--{- $utility- This library features several utility proxies to get you started. They all- reside under the "Control.Proxy.Prelude" hierarchy and they are imported by- default when you import "Control.Proxy".-- For example, if you wanted to print the first 3 natural numbers, you would- use:-->>> runProxy $ printD <-< enumFromToS 1 3-1-2-3-- The utility functions follow a systematic naming convention that uses the- last letter:-- * @D@: Only interacts with values going \'@D@\'ownstream towards the- 'Client'-- * @U@: Only interacts with values going \'@U@\'pstream towards the 'Server'-- * @B@: Interacts with values going \'@B@\'oth ways-- * @C@: Belongs in the \'@C@\'lient position-- * @S@: Belongs in the \'@S@\'server position-- Many utility proxies auto-forward values they receive, such as 'printD'.- This means we can easily combine multiple handling stages for processing- values:--> import Control.Proxy-> import System.IO->-> main = do-> h <- openFile "test.txt" WriteMode-> runProxy $ hPrintD h <-< printD <-< enumFromToS 1 3-> hClose h-->>> main-1-2-3-- The above program also wrote the same output to the file "test.txt":--> $ cat test.txt-> 1-> 2-> 3-- 'runProxy' discards any output that goes past the endpoints of the session,- so you don't need to worry about closing off each end.-- This library does not provide 'ByteString' or 'Text' utilities in order to- reduce the number of dependencies of the main package. These will be- released in a separate package in the near future.--}--{- $pipes- 'Proxy's generalize 'Pipe's by permitting communication upstream.- Fortunately, though, you don't need to rewrite your code if you have already- used 'Pipe's. "Control.Proxy" formulates all of the 'Pipe' types and- primitives in terms of the 'Proxy' type.-- This means that if you wish to upgrade your 'Pipe' code to take advantage of- upstream communication, you only need to import "Control.Proxy" instead- of "Control.Pipe" and everything will still work out of the box. Then you- can selectively upgrade certain components to communicate upstream as- necessary.-- To understand how 'Pipe's map onto 'Proxy's, just check out the 'Pipe'- definition in "Control.Proxy.Pipe":--> type Pipe a b = Proxy () a () b-- In other words, a 'Pipe' is just a 'Proxy' where you never pass any- information upstream.+{-| This module provides a brief introductory tutorial in the \"Introduction\"+ section followed by a lengthy discussion of the library's design and idioms.+-}++module Control.Proxy.Tutorial (+ -- * Introduction+ -- $intro++ -- * Bidirectionality+ -- $bidir++ -- * Type Synonyms+ -- $synonyms++ -- * Request and Respond+ -- $interact++ -- * Composition+ -- $composition++ -- * The Proxy Class+ -- $class++ -- * Interleaving Effects+ -- $interleave++ -- * Mixing Base Monads+ -- $hoist++ -- * Utilities+ -- $utilities++ -- * Mix Monads and Composition+ -- $mixmonadcomp++ -- * Folds+ -- $folds++ -- * Resource Management+ -- $resource++ -- * Extensions+ -- $extend++ -- * Error handling+ -- $error++ -- * Local state+ -- $state++ -- * Branching, zips, and merges+ -- $branch++ -- * Proxy Transformers+ -- $proxytrans++ -- * Conclusion+ -- $conclusion+ ) where++-- For documentation+import Control.Category+import Control.Monad.Trans.Class+import Control.MFunctor+import Control.PFunctor+import Control.Proxy+import Control.Proxy.Core.Correct (ProxyCorrect)+import Control.Proxy.Trans.Either+import Prelude hiding (catch)++{- $intro+ The @pipes@ library replaces lazy 'IO' with a safe, elegant, and+ theoretically principled alternative. Use this library if you:++ * want to write high-performance streaming programs++ * believe that lazy 'IO' was a bad idea++ * enjoy composing modular and reusable components++ * love theory and elegant code++ This library unifies many kinds of streaming abstractions, all of which are+ special cases of \"proxies\" (The @pipes@ name is a legacy of one such+ abstraction).++ Let's begin with the simplest 'Proxy': a 'Producer'. The following+ 'Producer' lazily streams lines from a 'Handle'++> import Control.Monad+> import Control.Proxy+> import System.IO+> +> -- Produces Strings ---+----------++> -- | |+> -- v v+> lines' :: (Proxy p) => Handle -> () -> Producer p String IO r+> lines' h () = runIdentityP loop where+> loop = do+> eof <- lift $ hIsEOF h+> if eof+> then return ()+> else do+> str <- lift $ hGetLine h+> respond str -- Produce the string+> loop+>+> -- Ignore the 'runIdentityP' and '()' for now++ But why limit ourselves to streaming lines from some file? Why not lazily+ generate values from an industrious user?++> -- Uses 'IO' as the base monad --++> -- |+> -- v+> promptInt :: (Proxy p) => () -> Producer p Int IO r+> promptInt () = runIdentityP $ forever $ do+> lift $ putStrLn "Enter an Integer:"+> n <- lift readLn -- 'lift' invokes an action in the base monad+> respond n++ Now we need to hook our 'Producer's up to a 'Consumer'. The following+ 'Consumer' endlessly 'request's a stream of 'Show'able values and 'print's+ them:++> -- Consumes 'a's ---+----------+ +-- Never terminates, so+> -- | | | the return value is+> -- v v v polymorphic+> printer :: (Proxy p, Show a) => () -> Consumer p a IO r+> printer () = runIdentityP $ forever $ do+> a <- request () -- Consume a value+> lift $ putStrLn "Received a value:"+> lift $ print a++ You can compose a 'Producer' and a 'Consumer' using ('>->'), which produces+ a runnable 'Session':++> -- Self-contained session ---+ +--+-- These must match+> -- | | | each component+> -- v v v+> promptInt >-> printer :: (Proxy p) => () -> Session p IO r+>+> lines' h >-> printer :: (Proxy p) => () -> Session p IO ()++ ('>->') connects each 'request' in @printer@ with a 'respond' in+ @lines'@ or @promptInt@.++ Finally, you use 'runProxy' to run the 'Session' and convert it back to the+ base monad. First we'll try our @lines'@ 'Producer', which will stream+ lines from the following file:++> $ cat test.txt+> Line 1+> Line 2+> Line 3++ The following program never brings more than a single line into memory (not+ that it matters for such a small file):++>>> withFile "test.txt" $ \h -> runProxy $ lines' h >-> printer+Received a value:+"Line 1"+Received a value:+"Line 2"+Received a value:+"Line 3"++ Similarly, we can lazily stream user input, requesting values from the user+ only when we need them:++>>> runProxy $ promptInt >-> printer :: IO r+Enter an Integer:+1<Enter>+Received a value:+1+Enter an Integer:+5<Enter>+Received a value:+5+...++ The last example proceeds endlessly until we hit @Ctrl-C@ to interrupt it.++ We would like to limit the number of iterations, so lets define an+ intermediate 'Proxy' that behaves like a verbose 'take'. I will call it a+ 'Pipe' (this library's namesake) since values flow through it:++> 'a's flow in ---+ +--- 'a's flow out+> | |+> v v+> take' :: (Proxy p) => Int -> () -> Pipe p a a IO ()+> take' n () = runIdentityP $ do+> replicateM_ n $ do+> a <- request ()+> respond a+> lift $ putStrLn "You shall not pass!"++ This 'Pipe' forwards the first @n@ values it receives undisturbed, then it+ outputs a cute message. You can compose it between the 'Producer' and+ 'Consumer' using ('>->'):++>>> runProxy $ promptInt >-> take' 2 >-> printer :: IO ()+Enter an Integer:+9<Enter>+Received a value:+9+Enter an Integer:+2<Enter>+Received a value:+2+You shall not pass!++ When @take' 2@ terminates, it brings down every 'Proxy' composed with it.++ Notice how @promptInt@ behaves lazily and only 'respond's with as many+ values as we 'request'. We 'request'ed exactly two values, so it only+ prompts the user twice.++ We can already spot several improvements upon traditional lazy 'IO':++ * You can define your own lazy components that have nothing to do with files++ * @pipes@ never uses 'unsafePerformIO' or violates referential transparency.++ * You don't need strictness hacks to ensure the proper ordering of effects++ * You can interleave effects in downstream stages, too++ However, this library can offer even more than that!+-}++{- $bidir+ So far we've only defined proxies that send information downstream in the+ direction of the ('>->') arrow. However, we don't need to limit ourselves+ to unidirectional communication and we can enhance these proxies with the+ ability to send information upstream with each 'request' that determines+ how upstream stages 'respond'.++ For example, 'Client's generalize 'Consumer's because they can supply an+ argument other than @()@ with each 'request'. The following 'Client'+ sends three 'request's upstream, each of which provides an 'Int' @argument@+ and expects a 'Bool' @result@:++> Sends out 'Int's ---+ +-- Receives back 'Bool's+> | |+> v v+> threeReqs :: (Proxy p) => () -> Client p Int Bool IO ()+> threeReqs () = runIdentityP $ forM_ [1, 3, 1] $ \argument -> do+> lift $ putStrLn $ "Client Sends: " ++ show (argument :: Int)+> result <- request argument+> lift $ putStrLn $ "Client Receives:" ++ show (result :: Bool)+> lift $ putStrLn "*"++ Notice how 'Client's use \"@request argument@\" instead of+ \"@request ()@\". This sends \"@argument@\" upstream to parametrize the+ 'request'.++ 'Server's similarly generalize 'Producer's because they receive arguments+ other than @()@. The following 'Server' receives 'Int' 'request's and+ 'respond's with 'Bool' values:++> Receives 'Int's ---+ +--- Replies with 'Bool's+> | |+> v v+> comparer :: (Proxy p) => Int -> Server p Int Bool IO r+> comparer = runIdentityK loop where+> loop argument = do+> lift $ putStrLn $ "Server Receives:" ++ show (argument :: Int)+> let result = argument > 2+> lift $ putStrLn $ "Server Sends: " ++ show (result :: Bool)+> nextArgument <- respond result+> loop nextArgument++ Notice how 'Server's receive their first argument as a parameter and bind+ each subsequent argument using 'respond'. This library provides a+ combinator which abstracts away this common pattern:++> foreverK :: (Monad m) => (a -> m a) -> a -> m b+> foreverK f = loop where+> loop argument = do+> nextArgument <- f argument+> loop nextArgument+>+> -- or: foreverK f = f >=> foreverK f+> -- = f >=> f >=> f >=> f >=> ...++ We can use this to simplify the @comparer@ 'Server':++> comparer = runIdentityK $ foreverK $ \argument -> do+> lift $ putStrLn $ "Server Receives:" ++ show argument+> let result = argument > 2+> lift $ putStrLn $ "Server Sends: " ++ show result+> respond result++ ... which looks just like the way you might write a server's main loop in+ another programming language.++ You can compose a 'Server' and 'Client' using ('>->'), and this also returns+ a runnable 'Session':++> comparer >-> threeReqs :: (Proxy p) => () -> Session p IO ()++ Running this executes the client-server session:++>>> runProxy $ comparer >-> threeReqs :: IO ()+Client Sends: 1+Server Receives: 1+Server Sends: False+Client Receives: False+*+Client Sends: 3+Server Receives: 3+Server Sends: True+Client Receives: True+*+Client Sends: 1+Server Receives: 1+Server Sends: False+Client Receives: False+*++ 'Proxy's generalize 'Pipe's because they allow information to flow upstream.+ The following 'Proxy' caches 'request's to reduce the load on the 'Server'+ if the request matches a previous one:++> import qualified Data.Map as M+>+> -- 'p' is the Proxy, as the (Proxy p) constraint indicates+>+> cache :: (Proxy p, Ord key) => key -> p key val key val IO r+> cache = runIdentityK (loop M.empty) where+> loop _map key = case M.lookup key _map of+> Nothing -> do+> val <- request key+> key2 <- respond val+> loop (M.insert key val _map) key2+> Just val -> do+> lift $ putStrLn "Used cache!"+> key2 <- respond val+> loop _map key2++ You can compose the @cache@ 'Proxy' between the 'Server' and 'Client' using+ ('>->'):++>>> runProxy $ comparer >-> cache >-> threeReqs+Client Sends: 1+Server Receives: 1+Server Sends: False+Client Receives: False+*+Client Sends: 3+Server Receives: 3+Server Sends: True+Client Receives: True+*+Client Sends: 1+Used cache!+Client Receives: False+*++ This bidirectional flow of information separates @pipes@ from other+ streaming libraries which are unable to model 'Client's, 'Server's, or+ 'Proxy's. Using @pipes@ you can define interfaces to RPC interfaces, REST+ architectures, message buses, chat clients, web servers, network protocols+ ... you name it!+-}++{- $synonyms+ You might wonder why ('>->') accepts 'Producer's, 'Consumer's, 'Pipe's,+ 'Client's, 'Server's, and 'Proxy's. It turns out that these type-check+ because they are all type synonyms that expand to the following central+ type:++> (Proxy p) => p a' a b' b m r++ Like the name suggests, a 'Proxy' exposes two interfaces: an upstream+ interface and a downstream interface. Each interface can both send and+ receive values:++> Upstream | Downstream+> +---------++> | |+> a' <== <== b'+> | Proxy |+> a ==> ==> b+> | |+> +---------+++ Proxies are monad transformers that enrich the base monad with the ability+ to send or receive values upstream or downstream:++> | Sends | Receives | Receives | Sends | Base | Return+> | Upstream | Upstream | Downstream | Downstream | Monad | Value+> p a' a b' b m r++ We can selectively close certain inputs or outputs to generate specialized+ proxies.++ For example, a 'Producer' is a 'Proxy' that can only output values to its+ downstream interface:++> Upstream | Downstream+> +----------++> | |+> C <== <== ()+> | Producer |+> () ==> ==> b+> | |+> +----------++>+> type Producer p b m r = p C () () b m r+>+> -- The 'C' type is uninhabited, so it 'C'loses an output end++ A 'Consumer' is a 'Proxy' that can only receive values on its upstream+ interface:++> Upstream | Downstream+> +----------++> | |+> () <== <== ()+> | Consumer |+> a ==> ==> C+> | |+> +----------++>+> type Consumer p a m r = p () a () C m r++ A 'Pipe' is a 'Proxy' that can only receive values on its upstream interface+ and send values on its downstream interface:++> Upstream | Downstream+> +--------++> | |+> () <== <== ()+> | Pipe |+> a ==> ==> b+> | |+> +--------++>+> type Pipe p a b m r = p () a () b m r++ When we compose proxies, the type system ensures sure that their input and+ output types match:++> promptInt >-> take' 2 >-> printer+>+> +-----------+ +---------+ +---------++> | | | | | |+> C <== <== () <== <== () <== <== ()+> | | | | | |+> | promptInt | | take' 2 | | printer |+> | | | | | |+> () ==> ==> Int ==> ==> Int ==> ==> C+> | | | | | |+> +-----------+ +---------+ +---------+++ Composition fuses these into a new 'Proxy' that has both ends closed, which+ is a 'Session':++> +-----------------------------------++> | |+> C <== <== ()+> | |+> | promptInt >-> take' 2 >-> printer |+> | |+> () ==> ==> C+> | |+> +-----------------------------------++>+> type Session p m r = p C () () C m r++ A 'Client' is a 'Proxy' that only uses its upstream interface:++> Upstream | Downstream+> +----------++> | |+> a' <== <== ()+> | Client |+> a ==> ==> C+> | |+> +----------++>+> type Client p a' a m r = p a' a () C m r++ A 'Server' is a 'Proxy' that only uses its downstream interface:+++> Upstream | Downstream+> +----------++> | |+> C <== <== b'+> | Server |+> () ==> ==> b+> | |+> +----------++>+> type Server p b' b m r = p C () b' b m r++ The compiler ensures that the types match when we compose 'Server's,+ 'Proxy's, and 'Client's.++> comparer >-> cache >-> threeReqs+>+> +----------+ +-------+ +-----------++> | | | | | |+> C <== <== Int <== <== Int <== <== ()+> | | | | | |+> | comparer | | cache | | threeReqs |+> | | | | | |+> () ==> ==> Bool ==> ==> Bool ==> ==> C+> | | | | | |+> +----------+ +-------+ +-----------+++ This similarly fuses into a 'Session':++> +----------------------------------++> | |+> C <== <== ()+> | |+> | comparer >-> cache >-> threeReqs |+> | |+> () ==> ==> C+> | |+> +----------------------------------+++ @pipes@ encourages substantial code reuse by implementing all abstractions+ as type synonyms on top of a single type class: 'Proxy'. This makes your+ life easier because:++ * You only use one composition operator: ('>->')++ * You can mix multiple abstractions together as long as the types match+-}++{- $interact+ There are only two ways to interact with other proxies: 'request' and+ 'respond'. Let's examine their type signatures to understand how they+ work:++> request :: (Monad m, Proxy p) => a' -> p a' a b' b m a+> ^ ^+> | |+> Argument --+ Result --+++ 'request' sends an argument of type @a'@ upstream, and binds a result of+ type @a@. Whenever you 'request', you block until upstream 'respond's with+ a value.+++> respond :: (Monad m, Proxy p) => b -> p a' a b' b m b'+> ^ ^+> | |+> Result --+ Next Argument --+++ 'respond' replies with a result of type @b@, and then binds the /next/+ argument of type @b'@. Whenever you 'respond', you block until downstream+ 'request's a new value.++ Wait, if 'respond' always binds the /next/ argument, where does the /first/+ argument come from? Well, it turns out that every 'Proxy' receives this+ initial argument as an ordinary parameter, as if they all began blocked on+ a 'respond' statement.+ + We can see this if we take all the previous proxies we defined and fully+ expand every type synonym. The initial argument of each 'Proxy' matches+ the type parameter corresponding to the return value of 'respond':++> These+> +-- Columns ---++> | Match |+> v v+> promptInt :: (Proxy p) => () -> p C () () Int IO r+> printer :: (Proxy p, Show a) => () -> p () a () C IO r+> take' :: (Proxy p) => Int -> () -> p () a () a IO ()+> comparer :: (Proxy p) => Int -> p C () Int Bool IO r+> cache :: (Proxy p, Ord key) => key -> p key val key val IO r++ You can also study the type of composition, which follows this same pattern.+ Composition requires two 'Proxy's blocked on a 'respond', and produces a new+ 'Proxy' similarly blocked on a 'respond':++> (>->) :: (Monad m, Proxy p)+> => (b' -> p a' a b' b m r)+> -> (c' -> p b' b c' c m r)+> -> (c' -> p a' a c' c m r)+> ^ ^+> | These |+> +---Match----+++ This is why 'Producer's, 'Consumer's, and 'Client's all take @()@ as their+ initial argument, because their corresponding 'respond' commands all have a+ return value of @()@.++ This library also provides ('>~>'), which is the dual of the ('>->')+ composition operator. ('>~>') composes two 'Proxy's blocked on a 'request'+ and returns a new 'Proxy' blocked on a 'request':++> (>~>)+> :: (Monad m, Proxy p)+> => (a -> p a' a b' b m r)+> -> (b -> p b' b c' c m r)+> -> (a -> p a' a c' c m r)++ Conceptually, ('>->') composes pull-based systems and ('>~>') composes+ push-based systems.++ In fact, if you went back through the previous code and systematically+ replaced every:++ * ('>->') with ('>~>'),++ * 'respond' with 'request', and++ * 'request' with 'respond'++ ... then everything would still work and produce identical behavior, except+ the compiler would now infer the symmetric types with all interfaces+ reversed. We can therefore conclude the obvious: pull-based systems are+ symmetric to push-based systems.++ Since these two composition operators are perfectly symmetric, I arbitrarily+ standardize on using ('>->') and I provide all standard library proxies+ blocked on 'respond' so that they work with ('>->'). This gives behavior+ more familiar to Haskell programmers that work with lazy pull-based+ functions. I only include the ('>~>') composition operator for theoretical+ completeness.+-}++{- $composition+ When we compose @(p1 >-> p2)@, composition ensures that @p1@'s downstream+ interface matches @p2@'s upstream interface. This follows from the type of+ ('>->'):++> (>->) :: (Monad m, Proxy p)+> => (b' -> p a' a b' b m r)+> -> (c' -> p b' b c' c m r)+> -> (c' -> p a' a c' c m r)++ Diagramatically, this looks like:++> p1 >-> p2+>+> +--------+ +--------++> | | | |+> a' <== <== b' <== <== c'+> | p1 | | p2 |+> a ==> ==> b ==> ==> c+> | | | |+> +--------+ +--------+++ @p1@'s downstream @(b', b)@ interface matches @p2@'s upstream @(b', b)@+ interface, so composition connects them on this shared interface. This+ fuses away the @(b', b)@ interface, leaving behind @p1@'s upstream @(a', a)@+ interface and @p2@'s downstream @(c', c)@ interface:++> +-----------------++> | |+> a' <== <== c'+> | p1 >-> p2 |+> a ==> ==> c+> | |+> +-----------------+++ Proxy composition has the very nice property that it is associative, meaning+ that it behaves the exact same way no matter how you group composition:++> (p1 >-> p2) >-> p3 = p1 >-> (p2 >-> p3)++ ... so you can safely elide the parentheses:++> p1 >-> p2 >-> p3++ Also, we can define a \'@T@\'ransparent 'Proxy' that auto-forwards values+ both ways:++> idT :: (Monad m, Proxy p) => a' -> p a' a a' a m r+> idT = runIdentityK loop where+> loop a' = do+> a <- request a'+> a'2 <- respond a+> loop a'2+>+> -- or: idT = runIdentityK $ foreverK $ request >=> respond+> -- = runIdentityK $ request >=> respond >=> request >=> respond ...++ Diagramatically, this looks like:++> +-----++> | |+> a' <======== a' <- All values pass+> | idT | straight through+> a ========> a <- immediately+> | |+> +-----+++ Transparency means that:++> idT >-> p = p+>+> p >-> idT = p++ In other words, 'idT' is an identity of composition.++ This means that proxies form a true 'Category' where ('>->') is composition+ and 'idT' is the identity. The associativity law and the two+ identity laws are just the 'Category' laws. The objects of the category are+ the 'Proxy' interfaces.++ These 'Category' laws guarantee the following important properties:++ * You can reason about each proxy's behavior independently of other proxies++ * You don't encounter weird behavior at the interface between two components++ * You don't encounter corner cases at the 'Server' or 'Client' ends of a+ 'Session'+-}++{- $class+ All the proxy code we wrote was generic over the 'Proxy' type class, which+ defines the three central operations of this library's API:++ * ('>->'): Proxy composition++ * 'request': Request input from upstream++ * 'respond': Respond with output to downstream++ @pipes@ defines everything in terms of these three operations, which is+ why all the library's utilities are polymorphic over the 'Proxy' type class.++ Let's look at some example instances of the 'Proxy' type class:++> instance Proxy ProxyFast -- Fastest implementation+> instance Proxy ProxyCorrect -- Strict monad transformer laws++ These two types provide the two alternative base implementations:++ * 'ProxyFast': This runs significantly faster on pure code segments and+ employs several rewrite rules to optimize your code into the equivalent+ hand-tuned code.++ * 'ProxyCorrect': This uses a monad transformer implementation that is+ correct by construction, but runs about 8x slower on pure code segments.+ However, for 'IO'-bound code, the performance gap is small.++ These two implementations differ only in the 'runProxy' function that they+ export, which is how the compiler selects which 'Proxy' implementation to+ use.++ "Control.Proxy" automatically selects the fast implementation for you, but+ you can always choose the correct implementation instead by replacing+ "Control.Proxy" with the following two imports:++> import Control.Proxy.Core -- Everything except the base implementation+> import Control.Proxy.Core.Correct -- The alternative base implementation++ These are not the only instances of the 'Proxy' type class! This library+ also provides several \"proxy transformers\", which are like monad+ transformers except that they also correctly lift the 'Proxy' type class:++> instance (Proxy p) => Proxy (IdentityP p)+> instance (Proxy p) => Proxy (EitherP e p)+> instance (Proxy p) => Proxy (MaybeP p)+> instance (Proxy p) => Proxy (ReaderP i p)+> instance (Proxy p) => Proxy (StateP s p)+> instance (Proxy p) => Proxy (WriterP w p)++ All of the 'Proxy' code we wrote so far also works seamlessly with all of+ these proxy transformers. The 'Proxy' class abstracts over the+ implementation details and extensions so that you can reuse the same library+ code for any feature set.++ This polymorphism comes at a price: you must embed your 'Proxy' code in at+ least one proxy transformer if you want clean type class constraints. If+ you don't use extensions then you embed your code in the identity proxy+ transformer: 'IdentityP'. This is why all the examples use 'runIdentityP'+ or 'runIdentityK' to embed their code in 'IdentityP'. "Control.Proxy.Class"+ provides a longer discussion on this subject.++ Without this 'IdentityP' embedding, the compiler infers uglier constraints,+ which are also significantly less polymorphic. We can show this by+ removing the 'runIdentityP' call from @promptInt@ and see what type the+ compiler infers:++> promptInt () = forever $ do+> lift $ putStrLn "Enter an Integer:"+> n <- lift readLn+> respond n++>>> :t promptInt -- I've substantially cleaned up the inferred type+promptInt+ :: (Monad (Producer p Int IO), MonadTrans (Producer p Int), Proxy p) =>+ () -> Producer p Int IO r++ All 'Proxy' instances are already monads and monad transformers, but the+ compiler cannot infer that without the 'IdentityP' embedding. When we embed+ @promptInt@ in 'IdentityP', the compiler collapses the 'Monad' and+ 'MonadTrans' constraints into the 'Proxy' constraint.++ Fortunately, you do not pay any performance price for this 'IdentityP'+ embedding or the type class polymorphism. Your polymorphic code will still+ run very rapidly, as fast as if you had specialized it to a concrete+ 'Proxy' instance without the 'IdentityP' embedding. I've taken great care+ to ensure that all optimizations and rewrite rules always see through these+ abstractions without any assistance on your part.+-}++{- $interleave+ When you compose two proxies, you interleave their effects in the base+ monad. The following two proxies demonstrate this interleaving of effects:++> downstream :: (Proxy p) => Consumer p () IO ()+> downstream () = runIdentityP $ do+> lift $ print 1+> request () -- Switch to upstream+> lift $ print 3+> request () -- Switch to upstream+>+> upstream :: (Proxy p) => Producer p () IO ()+> upstream () = runIdentityP $ do+> lift $ print 2+> respond () -- Switch to downstraem+> lift $ print 4++ "Control.Proxy.Class" enumerates the 'Proxy' laws, which equationally+ define how all 'Proxy' instances must behave. These laws require that+ @(upstream >-> downstream)@ must reduce to the following:++> upstream >-> downstream -- This is true no matter what feature+> = -- set or 'Proxy' instance you select+> \() -> lift $ do+> print 1+> print 2+> print 3+> print 4++ Conceptually, 'runProxy' just applies this to @()@ and removes the 'lift':++> runProxy $ upstream >-> downstream+> =+> do print 1+> print 2+> print 3+> print 4++ Let's test this:++>>> runProxy $ upstream >-> downstream+1+2+3+4++ The 'Proxy' laws let you reason about how proxies interleave effects without+ knowing any specifics about the underlying implementation. Intuitively, the+ 'Proxy' laws say that:++ * 'request' blocks until upstream 'respond's++ * 'respond' blocks until downstream 'request's++ * If a 'Proxy' terminates, it terminates every 'Proxy' composed with it++ Several of the utilities in "Control.Proxy.Prelude.Base" use these+ equational laws to rigorously prove things about their behavior. For+ example, consider the 'mapD' proxy, which applies a function @f@ to all+ values flowing downstream:++> mapD :: (Monad m, Proxy p) => (a -> b) -> x -> p x a x b m r+> mapD f = runIdentityK loop where+> loop x = do+> a <- request x+> x2 <- respond (f a)+> loop x2+>+> -- or: mapD f = runIdentityK $ foreverK $ request >=> respond . f++ We can use the 'Proxy' laws to prove that:++> mapD f >-> mapD g = mapD (g . f)+>+> mapD id = idT++ ... which is what we expect. We can fuse two consecutive 'mapD's into one+ by composing their functions, and mapping 'id' does nothing at all, just+ like the identity proxy: 'idT'.++ In fact, these are just the functor laws in disguise, where 'mapD' defines a+ functor between the category of Haskell function composition and the+ category of 'Proxy' composition. "Control.Proxy.Prelude.Base" is full of+ utilities like this that are simultaneously practical and theoretically+ elegant.+-}++{- $hoist+ Composition can't interleave two proxies if their base monads do not+ match. For instance, I might try to modify @promptInt@ to use+ @EitherT String@ to report the error instead of using exceptions:++> import Control.Monad.Trans.Either -- from the "either" package+> import Safe (readMay)+>+> promptInt2 :: (Proxy p) => () -> Producer p Int (EitherT String IO) r+> promptInt2 () = runIdentityP $ forever $ do+> str <- lift $ lift $ do+> putStrLn "Enter an Integer:"+> getLine+> case readMay str of+> Nothing -> lift $ left "Could not read Integer"+> Just n -> respond n++ However, if I try to compose it with @printer@, I receive a type error:++>>> runEitherT $ runProxy $ promptInt2 >-> printer+<interactive>:2:40:+ Couldn't match expected type `EitherT String IO'+ with actual type `IO'+ ...++ The type error says that @promptInt2@ uses @(EitherT String IO)@ for its+ base monad, but @printer@ uses 'IO' for its base monad, so composition can't+ interleave their effects.++ You can easily fix this using the 'hoist' function from the 'MFunctor' type+ class in "Control.MFunctor", which transforms the base monad of any monad+ transformer, including the 'Proxy' monad transformer. "Control.MFunctor"+ really belongs in the @transformers@ package, however it currently resides+ here because it requires the @Rank2Types@ extension.++ You will commonly use 'hoist' to 'lift' one proxy's base monad to match+ another proxy's base monad, like so:++>>> runEitherT $ runProxy $ promptInt2 >-> (hoist lift . printer)+Enter an Integer:+Hello<Enter>+Left "Could not read Integer"++ This library provides three syntactic conveniences for making this easier to+ write.++ First, ('.') has higher precedence than ('>->'), so you can drop the+ parentheses:++>>> runEitherT $ runProxy $ promptInt2 >-> hoist lift . printer+...++ Second, "lift" is such a common argument to 'hoist' that "Control.MFunctor"+ provides the 'raise' function:++> raise = hoist lift++>>> runEitherT $ runProxy $ promptInt2 >-> raise . printer+...++ Third, "Control.Proxy.Prelude.Kleisli" provides the 'hoistK' and 'raiseK'+ functions in case you think composition looks ugly:++> hoistK f = (hoist f .)+>+> raiseK = (raise .)++>>> runEitherT $ runProxy $ promptInt2 >-> raiseK printer+...++ Note that "Control.MFunctor" also provides 'MFunctor' instances for all the+ monad transformers in the @transformers@ package. This means that you can+ fix any incompatibility between two monad transformer stacks just using+ various combinations of 'hoist' and 'lift'.++ To see how, consider the following contrived pathological example where I+ want to mix two very different monad transformer stacks:++> m1 :: StateT s (ReaderT i IO) r+> m2 :: MaybeT (WriterT w IO) r++ I can interleave their transformers through judicious use of 'hoist' and+ 'lift'++> mBoth :: StateT s (MaybeT (ReaderT i (WriterT w IO))) r+> mBoth = do+> hoist (lift . hoist lift) m1+> lift (hoist lift m2)+-}++{- $utilities+ The "Control.Proxy.Prelude" heirarchy provides several utility functions+ for common tasks. We can redefine the previous example functions just by+ composing these utilities.++ For example, 'readLnS' reads values from user input, so we can read 'Int's+ just by specializing its type:++> readLnS :: (Proxy p, Read a) => () -> Producer p a IO r+>+> readIntS :: (Proxy p) => () -> Producer p Int IO r+> readIntS = readLnS++ The @S@ suffix indicates that it belongs in the \'@S@\'erver position.++ @(takeB_ n)@ allows at most @n@ value to pass through it in \'@B@\'oth+ directions:++> takeB_ :: (Monad m, Proxy p) => Int -> a' -> p a' a a' a m ()++ 'takeB_' has a more general type than @take'@ because it allows any type of+ value to flow upstream.++ 'printD' prints all values flowing \'@D@\'ownstream:++> printD :: (Proxy p, Show a) => x -> p x a x a IO r++ 'printD' has a more general type than our original @printer@ because it+ forwards all values further downstream after 'print'ing them. This means+ that you could use it as an intermediate stage as well. However, 'printD'+ still type-checks as the most downstream stage, too, since 'runProxy' just+ discards any unused outbound values.++ These utilities do not clash with the Prelude namespace or common libraries+ because they all end with a capital letter suffix that indicates their+ directionality:++ * \'@D@\' suffix: interacts with values flowing \'@D@\'ownstream++ * \'@U@\' suffix: interacts with values flowing \'@U@\'pstream++ * \'@B@\' suffix: interacts with values flowing \'@B@\'oth ways (or:+ \'@B@\'idirectional)++ * \'@S@\' suffix: belongs furthest upstream in the \'@S@\'erver position++ * \'@C@\' suffix: belongs furthest downstream in the \'@C@\'lient position++ We can assemble these functions into a silent version of our previous+ 'Session':++>>> runProxy $ readIntS >-> takeB_ 2 >-> printD+4<Enter>+4+39<Enter>+39++ Fortunately, we don't have to give up our previous useful diagnostics.+ We can use 'execU', which executes an action each time values flow upstream+ through it, and 'execD', which executes an action each time values flow+ downstream through it:++> promptInt :: (Proxy p) => () -> Producer p Int IO r+> promptInt = readLnS >-> execU (putStrLn "Enter an Integer:")+>+> printer :: (Proxy p, Show a) => x -> p x a x a IO r+> printer = execD (putStrLn "Received a value:") >-> printD++ Similarly, we can build our old @take'@ on top of 'takeB_':++> take' :: (Proxy p) => Int -> a' -> p a' a a' a m ()+> take' n a' = runIdentityP $ do -- Remember, we need 'runIdentityP' if+> takeB_ n a' -- we use 'do' notation or 'lift'+> lift $ putStrLn "You shall not pass!"++>>> runProxy $ promptInt >-> take' 2 >-> printer+<Exact same behavior>++ Or perhaps I want to skip user input for testing and mock @promptInt@ by+ replacing it with a predefined set of values:++>>> runProxy $ fromListS [4, 37, 1] >-> take'2 >-> printer+Received a value:+4+Received a value:+37++ What about our original @lines@ function? That's just 'hGetLineS':++> hGetLineS :: (Proxy p) => Handle -> () -> Producer p String IO ()++ You could hand-write loops that accomplish these same tasks, but proxies let+ you:++ * Rapidly swap in and out components for testing, debugging, and fast+ prototyping++ * Factor out common patterns into modular components++ * Mix and match simple stages to build sophisticated programs++ This compositional programming style emphasizes building a library of+ reusable components and connecting them like Unix pipes to assemble the+ desired streaming program.+-}++{- $mixmonadcomp+ Composition isn't the only way to assemble proxies. You can also sequence+ predefined proxies using @do@ notation to generate more elaborate behaviors.++ Most commonly, you will sequence two sources to combine their outputs, very+ similar to how the Unix @cat@ utility behaves:++> threeSources () = do+> source1 ()+> source2 ()+> source3 ()+>+> -- or: threeSources = source1 >=> source2 >=> source3++ As a concrete example, we could create a 'Producer' where our first source+ presets the first few values and then we let the user take over to generate+ the remaining values:++> source1 :: (Proxy p) => () -> Producer p Int IO r+> source1 () = runIdentityP $ do+> fromListS [4, 4] () -- Source 1+> readLnS () -- Source 2+>+> -- or: source1 = runIdentityK (fromListS [4, 4] >=> readLnS)++>>> runProxy $ source1 >-> printD+4+4+70<Enter>+70+34<Enter>+34+...++ What if we only want the user to provide three values? We can + selectively throttle it with 'takeB_':++> source2 :: (Proxy p) => () -> Producer p Int IO ()+> source2 () = runIdentityP $ do+> fromListS [4, 4] ()+> (readLnS >-> takeB_ 3) () -- You can compose inside a do block!+>+> -- or: source2 = runIdentityK (fromListS [4, 4] >=> (readLnS >-> takeB_ 3))++ Notice that composition works inside of a @do@ block! This is a very handy+ trick!++>>> runProxy $ source2 >-> printD+4+4+56<Enter>+56+41<Enter>+41+80<Enter>+80++ You can also concatenate sinks, too:++> sink1 :: (Proxy p) => () -> Consumer p Int IO ()+> sink1 () = do+> (takeB_ 3 >-> printD) () -- Sink 1+> (takeWhileD (< 4) >-> printD) () -- Sink 2+>+> -- or: sink1 = (takeB_ 3 >-> printD) >=> (takeWhileD (< 4) >-> printD)++>>> runProxy $ source2 >-> sink1+4 -- The first sink+4 -- handles these+68<Enter> --+68+1<Enter> -- The second sink+1 -- handles these+5<Enter> --++ ... but the above example is gratuitous because you can simply concatenate+ the intermediate stages:++> sink2 :: (Proxy p) => () -> Consumer p Int IO ()+> sink2 () = intermediate >-> printD where+> intermediate () = do+> takeB_ 3 () -- Intermediate stage 1+> takeWhileD (< 4) -- Intermediate stage 2+>+> -- or: sink2 = (takeB_ 3 >=> takeWhileD (< 4)) >-> printD++>>> runProxy $ source2 >-> sink2+<Exact same behavior>++ These examples demonstrate the two principal ways to combine proxies:++ * \"Vertical\" composition, using ('>=>') from the Kleisli category++ * \"Horizontal\" composition: using ('>->') from the Proxy category++ You assemble most proxies simply by composing them in one or both of these+ two categories.+-}++{- $folds+ You can fold a stream of values in two ways, both of which use the base+ monad:++ * Use 'WriterT' in the base monad and 'tell' the values to fold++ * Use 'StateT' in the base monad and 'put' strict values++ 'WriterT' is more elegant in principle but leaks space for a large number of+ values to fold. 'StateT' does not leak space if you keep the accumulator+ strict, but is less elegant and doesn't guarantee write-only behavior. To+ remedy this, I am currently working on a stricter 'WriterT' implementation+ that does not leak space to add to the @transformers@ package.++ "Control.Proxy.Prelude.Base" provides several common folds using 'WriterT'+ as the base monad, such as:++ * 'lengthD': Count how many values flow downstream++> lengthD :: (Monad m, Proxy p) => x -> p x a x a (WriterT (Sum Int) m) r++ * 'toListD': Fold the values flowing downstream into a list.++> toListD :: (Monad m, Proxy p) => x -> p x a x a (WriterT [a] m) r++ * 'anyD': Determine whether any values satisfy the predicate++> anyD :: (Monad m, Proxy p) => (a -> Bool) -> x -> p x a x a (WriterT Any m) r++ These 'WriterT' versions demonstrate how the elegant approach should work in+ principle and they should be okay for folding a medium number of values+ until I release the fixed 'WriterT'. If space leaks cause problems, you can+ temporarily rewrite the 'WriterT' folds using the following two strict+ 'StateT' folds:++ * 'foldlD'': Strictly fold values flowing downstream++> foldlD'+> :: (Monad m, Proxy p) => (b -> a -> b) -> x -> p x a x a (StateT b m) r++ * 'foldlU'': Strictly fold values flowing upstream++> foldU'+> :: (Monad m, Proxy p) => (b -> a' -> b) -> a' -> p a' x a' x (StateT b m) r++ Now, let's try these folds out and see if we can build a list from user+ input:++>>> runWriterT $ runProxy $ raiseK promptInt >-> takeB_ 3 >-> toListD+Enter an Integer:+1<Enter>+Enter an Integer:+66<Enter>+Enter an Integer:+5<Enter>+((), [1, 66, 5])++ Notice that @promptInt@ uses 'IO' as its base monad, but 'toListD' uses+ @(WriterT [Int] m)@ as its base monad, so I use 'raiseK' to get the base+ monads to match.++ You can insert these folds anywhere in the middle of a pipeline and they+ still work:++>>> runWriterT $ runProxy $ fromListS [5, 7, 4] >-> lengthD >-> raiseK printD+5+7+4+((), Sum 3)++ You can also run multiple folds at the same time just by adding more+ 'WriterT' layers to your base monad:++>>> runWriterT $ runWriterT $ fromListS [9, 10] >-> anyD even >-> raiseK sumD+(((), Any {getAny = True},Sum {getSum = 19})++ I designed certain special folds to terminate the 'Session' early if they+ can compute their result prematurely, in order to draw as little input as+ possible. These folds end with an underscore, such as 'headD_', which+ terminates the stream once it receives an input:++> headD_ :: (Monad m, Proxy p) => x -> p x a x a (WriterT (First a) m) ()++>>> runWriterT $ runProxy $ fromListS [3, 4, 9] >-> raiseK printD >-> headD_+3+((), First {getFirst = Just 3})++ Compare this to 'headD' without underscore, which folds the entire input:++>>> runWriterT $ runProxy $ fromListS [3, 4, 9] >-> raiseK printD >-> headD+3+4+9+((), First {getFirst = Just 3})++ Use the versions that don't prematurely terminate if you are running+ multiple folds or if you want to continue to use the rest of the input when+ the fold is done. Use the versions that do prematurely terminate if+ collecting that single fold is the entire purpose of the session.+-}++{- $resource+ This core library provides utilities for lazily streaming from resources,+ but does not provide utilities for lazily managing resource allocation and+ deallocation. To frame the problem, let's assume that we try to be clever+ and write a streaming utility that lazily opens a file only in response to+ a 'request', such as the following 'Producer':++> readFile' :: FilePath -> () -> Producer p String IO+> readFile' file () = runIdentityP $ do+> h <- lift $ openFile file ReadMode+> lift $ putStrLn "Opening file"+> hGetLineS h ()+> lift $ putStrLn "Closing file"+> lift $ hClose h++ This works well if we fully demand the file:++>>> runProxy $ readFile' "test.txt" >-> printD+Opening file+"Line 1"+"Line 2"+"Line 3"+Closing file++ This also works well if we never demand the file at all, in which case we+ never open it:++>>> runProxy $ readFile' "test.txt" >-> return+-- Outputs nothing++ But it gives exactly the wrong behavior if we partially demand the file:++>>> runProxy $ readFile' "test.txt" >-> takeB_ 1 >-> printD+Opening file+"Line 1"++ Notice that this does not close the file, because once @takeB_ 1@ terminates+ it terminates the entire 'Session' and @readFile'@ does not get a chance to+ finalize the file.++ I will release a separate library in the near future that offers lazy+ resource management, too, but in the meantime I advise that you use one of+ the following two strategies to guarantee deterministic resource+ deallocation.++ The first approach opens all resources before running the session and close+ them all afterward. For example, if I wanted to emulate the Unix @cp@+ command, streaming one line at a time, I would write:++> import System.IO+>+> cp :: FilePath -> FilePath -> IO ()+> cp inFile outFile =+> withFile file1 ReadMode $ \hIn ->+> withFile file2 WriteMode $ \hOut ->+> runProxy $ hGetLineS hIn >-> hPutLineS hOut2++ The advantage of this approach is that it:++ * is straightforward,++ * requires no special integration with existing libraries, and++ * is exception safe.++ The disadvantage is that this does not lazily allocate resources, nor does+ this promptly deallocate them.++ The second approach is to use something like 'ResourceT' (from the+ @resourceT@ package) to register finalizers and ensure they get released+ deterministically. You may prefer this approach if you have previously used+ the @conduit@ library, which uses 'ResourceT' in its base monad to offer+ resource determinism. You can use 'ResourceT' with @pipes@, too, just by+ including it in the base monad.++ I plan to release a lazy resource management library very soon built on top+ of @pipes@ that behaves similarly to 'ResourceT'. The main advantages of+ this upcoming implementation will be that it:++ * uses a simpler and pure implementation++ * obeys several useful theoretical laws++ * requires no dependencies other than @pipes@++ However, if you don't need this extra power, then just stick to the former+ simpler approach. I plan to release all standard libraries to be agnostic+ of the finalization approach to let you use which one you prefer.+-}++{- $extend+ This library provides several extensions that add features on top of the+ base 'Proxy' API. These extensions behave like monad transformers, except+ that they also lift the 'Proxy' class through the extension so that the+ extended proxy can still 'request', 'respond', compose with other proxies:++> instance (Proxy p) => Proxy (IdentityP p) -- Equivalent to IdentityT+> instance (Proxy p) => Proxy (EitherP e p) -- Equivalent to EitherT+> instance (Proxy p) => Proxy (MaybeP p) -- Equivalent to MaybeT+> instance (Proxy p) => Proxy (StateP s p) -- Equivalent to StateT+> instance (Proxy p) => Proxy (WriterP w p) -- Equivalent to WriterT++ Each of these proxy transformers provides the same API as the equivalent+ monad transformer (sometimes even more). The following sections show some+ common problems that these proxy transformers solve.+-}++{- $error++ Our previous @promptInt@ example suffered from one major flaw:++> promptInt2 :: (Proxy p) => () -> Producer p Int (EitherT String IO) r+> promptInt2 () = runIdentityP $ forever $ do+> str <- lift $ lift $ do+> putStrLn "Enter an Integer:"+> getLine+> case readMay str of+> Nothing -> lift $ left "Could not read Integer"+> Just n -> respond n++ There is no way to recover from the error and resume streaming data. You+ can only handle 'Left' value after using 'runProxy', but by then it is too + late.++ We can solve this by switching the order of the two monad transformers, but+ using 'EitherP' this time instead of 'EitherT':++> import qualified Control.Proxy.Trans.Either as E+>+> -- Proxy transformers play+> -- nice with type synonyms --++> -- |+> -- v+> promptInt3 :: (Proxy p) => () -> Producer (E.EitherP String p) Int IO r+> -- i.e. (Proxy p) => () -> EitherP String p C () () Int IO r+>+> promptInt3 () = forever $ do+> str <- lift $ do+> putStrLn "Enter an Integer:"+> getLine+> case readMay str of+> Nothing -> E.throw "Could not read Integer"+> Just n' -> respond n++ This example does not need 'runIdentityP' (nor would that type-check)+ because the 'EitherP' proxy transformer gives the compiler enough+ information to generalize the constraints.++ We've swapped the order of the transformers, so now we use 'runEitherK'+ first to unwrap the 'EitherP' followed by 'runProxy'.++> runEitherK+> :: (q -> EitherP p a' a b' b m r) -> (q -> p a' a b' b m (Either e r))++>>> runProxy $ runEitherK $ promptInt3 >-> printer :: IO (Either String r)+Enter an Integer:+Hello<Enter>+Left "Could not read Integer"++ Notice how we can directly compose @printer@ with @promptInt@.+ This works because @printer@'s base proxy type is completely polymorphic+ over the 'Proxy' type class and doesn't use any features specific to any+ proxy transformers:++> 'p' type-checks as anything --++> that implements 'Proxy' |+> v+> printer :: (Proxy p, Show a) => () -> Consumer p a IO r++ This means that you can compose @printer@ with anything that implements the+ 'Proxy' type class, including 'EitherP', without any lifting.++ 'EitherP' lets us catch and handle errors locally without disturbing other+ proxies. For example, I can define a heartbeat function that just restarts+ a given proxy each time it raises an error:++> heartbeat+> :: (Proxy p)+> => E.EitherP String p a' a b' b IO r+> -> E.EitherP String p a' a b' b IO r+> heartbeat p = p `E.catch` \err -> do+> lift $ putStrLn err -- Print the error+> heartbeat p -- Restart 'p'++ This uses the 'catch' function from "Control.Proxy.Trans.Either", which+ lets you catch and handle errors locally without disturbing other proxies.++>>> runProxy $ E.runEitherK $ (heartbeat . promptInt3) >-> takeB_ 2 >-> printer+Enter an Integer:+Hello<Enter>+Could not read Integer+Enter an Integer+8+Received a value:+8+Enter an Integer+0+Received a value:+0++ It's very easy to prove that 'EitherP' has only a local effect. In fact,+ we can run it entirely locally like so:++>>> runProxy $ (E.runEitherK $ heartbeat . promptInt3) >-> takeB_ 2 >-> printer++ Proxy transformers do not use the base monad at all, so you can use them to+ isolate effects from other proxies, as the next section demonstrates.+-}++{- $state+ The 'StateP' proxy lets you embed local state into any 'Proxy' computation.+ For example, we might want to gratuitously use state to generate successive+ numbers:++> import qualified Control.Proxy.Trans.State as S+>+> increment :: (Monad m, Proxy p) => () -> Producer (S.StateP Int p) Int m r+> increment () = forever $ do+> n <- S.get+> respond n+> S.put (n + 1)++ We could then embed it locally into any 'Proxy', such as the following one:++> numbers :: (Monad m, Proxy p) => () -> Producer p Int m ()+> numbers () = runIdentityP $ do+> (takeB_ 5 <-< S.evalStateK 10 increment) ()+> S.evalStateK 1 (takeB_ 3 <-< increment) () -- This works, too!++>>> runProxy $ numbers >-> printD+10+11+12+13+14+1+2+3++ We can also prove the effect is local even when you directly compose two+ 'StateP' proxies before running them. Let's define a stateful consumer:++> increment2 :: (Proxy p) => () -> Consumer (S.StateP Int p) Int IO r+> increment2 () = forever $ do+> nOurs <- S.get+> nTheirs <- request ()+> lift $ print (nTheirs, nOurs)+> S.put (nOurs + 2)++ .. and hook it up directly to @increment@:++>>> runProxy $ S.evalStateK 0 $ increment >-> takeB_ 3 >-> increment2+(0, 0)+(1, 2)+(2, 4)++ They each share the same initial state, but they isolate their own side+ effects completely from each other.+-}++{- $branch+ So far we've only considered linear chains of proxies, but @pipes@ allows+ you to branch these chains and generate more sophisticated topologies. The+ trick is to simply nest the 'Proxy' monad transformer within itself.++ For example, if I want to zip two inputs, I can just define the following+ triply nested proxy:++> zipD+> :: (Monad m, Proxy p1, Proxy p2, Proxy p3)+> => () -> Consumer p1 a (Consumer p2 b (Consumer p3 (a, b) m)) r+> zipD = runIdentityP . hoist (runIdentityP . hoist runIdentityP) $ forever $ do+> -- Yes, this 'runIdentityP' mess is necessary. Sorry!+>+> a <- request () -- Request from the outer 'Consumer'+> b <- lift $ request () -- Request from the inner 'Consumer'+> lift $ lift $ respond (a, b) -- Respond to the 'Producer'++ 'zipD' behaves analogously to a curried function. We partially apply it to+ each layer using composition and 'runProxyK' or 'runProxy':++> -- 1st application+> p1 = runProxyK $ zipD <-< fromListS [1..3]+>+> -- 2nd application+> p2 = runProxyK $ p1 <-< fromListS [4..]+>+> -- 3rd application+> p3 = runProxy $ printD <-< p2++>>> p3+(1, 4)+(2, 5)+(3, 6)++ You can use this trick to fork output, too:++> fork+> :: (Monad m, Proxy p1, Proxy p2, Proxy p3)+> => () -> Consumer p1 a (Producer p2 a (Producer p3 a m)) r+> fork () =+> runIdentityP . hoist (runIdentityP . hoist runIdentityP) $ forever $ do+> a <- request () -- Request output from the 'Consumer'+> lift $ respond a -- Send output to the outer 'Producer'+> lift $ lift $ respond a -- Send output to the inner 'Producer'++ Again, we just keep partially applying it until it is fully applied:++> -- 1st application+> p1 = runProxyK $ fork <-< fromListS [1..3]+>+> -- 2nd application+> p2 = runProxyK $ raiseK printD <-< mapD (> 2) <-< p1+>+> -- 3rd application+> p3 = runProxy $ printD <-< mapD show <-< p2++>>> p3+False+"1"+False+"2"+True+"3"++ You can even merge or fork proxies that use entirely different feature sets:++> p1 = runProxyK $ S.evalStateK 0 $ fork <-< increment+>+> p2 = runProxyK $ raiseK printD <-< mapD (+ 10) <-< p1+>+> p3 = runProxy $ E.runEitherK $ printD <-< (takeB_ 3 >=> E.throw) <-< p2++>>> p3+10+0+11+1+12+2+Left ()++ We just forked a @(StateP p1)@ proxy and read out the result in both a+ generic @p2@ proxy and an @(EitherP p3)@ proxy. That's pretty crazy, but it+ gives you a sense of how versatile and robust proxies can be.++ You can implement arbitrary branching topologies using this trick. However,+ I want to mention a few caveats:++ * The intermediate partially applied type signatures will be ugly as sin.+ I warned you.++ * You cannot implement cyclic topologies (and cyclic topologies do not make+ sense for proxies anyway)++ * You cannot use this trick to implement a polymorphic zip function of the+ following form:++> zip' -- You can't define this+> :: (Monad m, Proxy p)+> => (() -> Producer p a m r)+> -> (() -> Producer p b m r)+> -> (() -> Producer p (a, b) m r)++ Partial application requires selecting a 'Proxy' instance, which is why you+ cannot define @zip'@. You /can/ define a @zip'@ specialized to a concrete+ 'Proxy' instance, but I don't really recommend doing that since you should+ always strive to write polymorphic proxies to avoid locking your user into+ a particular feature set.++ With those caveats out of the way, this approach affords many indispensable+ features that other approaches do not allow:++ * It does not require extending the 'Proxy' type class++ * It handles almost every branching scenario, including more complicated+ situations like concurrent interleavings++ * You can branch and merge mixtures of 'Server's, 'Client's, and 'Proxy's++ * You can branch and merge heterogeneous feature sets++ * It is completely polymorphic over the 'Proxy' class and uses no+ implementation-specific details+-}++{- $proxytrans+ There is one last scenario that you will eventually encounter: mixing+ proxies that have incompatible proxy transformer stacks. You solve this the+ exact same way you mix different monad transformer stacks, except that+ instead of using 'lift' and 'hoist' you use 'liftP' and 'hoistP'.++ For example, we might want to mix @promptInt3@ and @increment2@:++> promptInt3 :: (Proxy p) => () -> Producer (E.EitherP String p) Int IO r+>+> increment2 :: (Proxy p) => () -> Consumer (S.StateP Int p) Int IO r++ Unfortunately, they use two different feature sets so neither one is fully+ polymorphic over the 'Proxy' class and we cannot directly compose them.++ Fortunately, all proxy transformers implement the 'ProxyTrans' class,+ analogous to the 'MonadTrans' class for transformers:++> class ProxyTrans t where+> liftP+> :: (Monad m, Proxy p)+> => p a' a b' b m r -> t p a' a b' b m r+>+> -- mapP is slightly more elegant+> mapP+> :: (Monad m, Proxy p)+> => (q -> p a' a b' b m r) -> (q -> t p a' a b' b m r)+> mapP = (liftP . )++ It's very easy to use. Just use 'mapP' (equivalent to @(liftP .)@ to lift+ one proxy transformer to match another one. For example, we can 'mapP'+ @increment2@ to match @promptInt3@:++> promptInt3 >-> mapP increment2+> :: (Proxy p) => () -> Session (EitherP String (StateP Int p)) IO r++>>> runProxy $ S.evalStateK 0 $ E.runEitherK $ promptInt3 >-> mapP increment2+Enter an Integer:+4<Enter>+(4, 0)+Enter an Integer:+5<Enter>+(5, 2)+Enter an Integer:+Hello<Enter>+Left "Could not read Integer"++ ... or we could instead 'mapP' @promptInt3@ to match @increment2@ and switch+ the order of the two proxy transformers:++> mapP promptInt3 >-> increment2+> :: (Proxy p) => () -> Session (StateP Int (EitherP String p)) IO r++>>> runProxy $ E.runEitherK $ S.evalStateK 0 $ mapP promptInt3 >-> increment2+Enter an Integer:+4<Enter>+(4, 0)+Enter an Integer:+5<Enter>+(5, 2)+Enter an Integer:+Hello<Enter>+Left "Could not read Integer"++ Like monad transformers, proxy transformers lift a base 'Monad' instance+ to an extended 'Monad' instance. 'liftP' exactly mirrors the 'lift'+ function from 'MonadTrans'. 'liftP' takes some base proxy, @p@, that+ implements 'Monad' and \"lift\"s it to an extended proxy, @(t p)@, that also+ implements 'Monad'.++ So for example, I could do something like:++> do liftP $ actionInBaseProxy+> actionInExtendedProxy++ Monad transformers impose certain laws to ensure that this lifting is+ correct. These are known as the monad transformer laws;++> (lift .) (f >=> g) = (lift .) f >=> (lift .) g+>+> (lift .) return = return++ If you convert these laws to @do@ notation, they just say:++> do x <- lift m = lift $ do x <- m+> lift (f x) f x+>+> lift (return r) = return r++ Proxy transformers require the exact same laws to ensure that they lift the+ base monad to the extended monad correctly. Just replace 'lift' with+ 'liftP':++> (liftP .) (f >=> g) = (liftP .) f >=> (liftP .) g+>+> (liftP .) return = return++ The only difference is that I also include 'mapP' in the 'ProxyTrans' type+ class for convenience, which sweetens these laws a little bit:++> mapP = (lift .)+>+> mapP (f >=> g) = mapP f >=> mapP g -- These are functor laws!+>+> mapP return = return++ However, proxy transformers do one extra thing above and beyond ordinary+ monad transformers. Proxy transformers lift the 'Proxy' type class, meaning+ that if the base type implements 'Proxy', so does the extended type.++ This means that we need a set of laws that guarantee that the proxy+ transformer lifts the 'Proxy' instance correctly. I call these laws the+ \"proxy transformer laws\":++> mapP (f >-> g) = mapP f >-> mapP g -- These are functor laws, too!+>+> mapP idT = idT++ In other words, a proxy transformer defines a functor from the base+ composition to the extended composition! Neat!++ But we're not even done, because proxies actually form three other+ categories, only one of which I have mentioned so far, and proxy+ transformers lift these three other categories, too:++> -- The push-based category+>+> mapP (f >~> g) = mapP f >~> mapP g+>+> mapP coidT = coidT++> -- The "request" category+>+> mapP (f \>\ g) = mapP f \>\ mapP g+>+> mapP request = request++> -- The "respond" category+>+> mapP (f />/ g) = mapP f />/ mapP g+>+> mapP respond = respond++ I never even mentioned those last two categories because they are more+ exotic and you probably never need to use them. However, even if we never+ use those categories they still guarantee two really important laws that we+ should remember:++> mapP request = request+>+> mapP respond = respond++ We can translate those to 'liftP' to get:++> liftP $ request a' = request a'+>+> liftP $ respond b = respond b++ In other words, 'request' and 'respond' in the extended proxy must behave+ exactly the same as lifting 'request' and 'respond' from the base proxy.++ All the proxy transformers in this library obey the proxy transformer laws,+ which ensure that 'liftP' / 'mapP' always do \"the right thing\".++ Proxy transformers also implement 'hoistP' from the 'PFunctor' class in+ "Control.PFunctor". This exactly parallels 'hoist' for monad transformers.++ Just like monad transformers, we can mix two completely exotic proxy+ transformer stacks using a combination of 'liftP' and 'hoistP'. Here's the+ proxy transformer equivalent of the previous example I gave:++> p1 :: (Proxy p) => a' -> StateP s (ReaderP i p) a' a a' a m r+> p2 :: (Proxy p) => a' -> MaybeP (WriterP w p) a' a a' a m r++ As before, I can interleave their proxy transformers through judicious use+ of 'hoistP' and 'liftP'++> pSequence+> :: (Proxy p) => StateP s (MaybeP (ReaderP i (WriterP w p))) a' a a' a r+> pSequence a' = do+> hoistP (liftP . hoistP liftP) (p1 a')+> liftP (hoistP liftP (p2 a'))++ ... but unlike ordinary monad transformers I could instead mix them by+ composition, too!++> pCompose+> :: (Proxy p) => StateP s (MaybeP (ReaderP i (WriterP w p))) a' a a' a r+> pCompose =+> hoistP (liftP . hoistP liftP) . p1+> >-> liftP . hoistP liftP . p2+-}++{- $conclusion+ The @pipes@ library emphasizes the reuse of a small set of core abstractions+ grounded in theory to implement all functionality:++ * Monads++ * Proxies: ('>->'), 'request', and 'respond'++ * Monad Transformers and Functors on Monads: 'lift' and 'hoist'++ * Proxy Transformers and Functors on Proxies: 'liftP' and 'hoistP'++ However, I don't expect everybody to immediately understand how so few+ primitives can implement such a wide variety of features. This tutorial+ gives a taste of how many interesting ways you can combine these few+ abstractions, but these examples barely scratch the surface, despite this+ tutorial's length. So if you don't know how to implement something using+ @pipes@, just ask me and I will be happy to help. -}
− Data/Closed.hs
@@ -1,9 +0,0 @@-{-| An empty type that gives cleaner type signatures. -}--module Data.Closed (- -- * Closed- C- ) where---- | The empty type, denoting a \'@C@\'losed end-data C = C -- Not exported, but I write it to keep the library Haskell98
pipes.cabal view
@@ -1,5 +1,5 @@ Name: pipes-Version: 2.5.0+Version: 3.0.0 Cabal-Version: >=1.14.0 Build-Type: Simple License: BSD3@@ -7,40 +7,33 @@ Copyright: 2012 Gabriel Gonzalez Author: Gabriel Gonzalez Maintainer: Gabriel439@gmail.com-Stability: Experimental Bug-Reports: https://github.com/Gabriel439/Haskell-Pipes-Library/issues Synopsis: Compositional pipelines Description:- \"Iteratees done right\". This library implements- iteratees\/enumerators\/enumeratees simply and elegantly, using different- naming conventions.+ \"Coroutines done right\". This library generalizes+ iteratees\/enumerators\/enumeratees simply and elegantly. .- Advantages over traditional iteratee implementations:+ Advantages over traditional iteratee\/coroutine implementations: .- * /Concise API/: This library uses a few simple abstractions with a very high- power-to-weight ratio to reduce adoption time.+ * /Concise API/: Use three simple commands: ('>->'), 'request', and 'respond' .- * /Bidirectionality/: The library offers bidirectional communication+ * /Bidirectionality/: Implement duplex channels .- * /Blazing fast/: Currently the fastest iteratee implementation+ * /Blazing fast/: Implementation tuned for speed .- * /Clear semantics/: All abstractions are grounded in category theory, which- leads to intuitive behavior (and fewer bugs, if any!).+ * /Elegant semantics/: Use practical category theory .- * /Extension Framework/: You can elegantly mix and match extensions to the- base type and easily create your own!+ * /Extension Framework/: Mix and match extensions and create your own .+ * /Lightweight Dependency/: @pipes@ depends only on @transformers@ and+ compiles rapidly+ . * /Extensive Documentation/: Second to none! .- I recommend you begin by reading "Control.Pipe.Tutorial" which introduces the- basic concepts using the simpler unidirectional 'Pipe' API. Then move on to- "Control.Proxy.Tutorial", which introduces the 'Proxy' type which forms the- core abstraction of this library. To use extensions or define your own, check- out "Control.Proxy.Trans.Tutorial".+ Import "Control.Proxy" to use the library. .- I will soon replace "Control.Frame" with a superior resource-management- solution, so new users of the library should avoid using it.-Category: Control, Pipe, Proxies+ Read "Control.Proxy.Tutorial" for a really extensive tutorial.+Category: Control, Pipes, Proxies Tested-With: GHC ==7.4.1 Source-Repository head Type: git@@ -49,32 +42,28 @@ Library Build-Depends: base >= 4 && < 5,- index-core, transformers >= 0.2.0.0 Exposed-Modules:- Control.Frame,- Control.Frame.Tutorial,- Control.IMonad.Trans.Free, Control.MFunctor,+ Control.PFunctor,+ Control.Pipe, Control.Proxy,- Control.Proxy.Core, Control.Proxy.Class,+ Control.Proxy.Core,+ Control.Proxy.Core.Fast,+ Control.Proxy.Core.Correct, Control.Proxy.Pipe,+ Control.Proxy.Synonym, Control.Proxy.Trans, Control.Proxy.Trans.Either, Control.Proxy.Trans.Identity, Control.Proxy.Trans.Maybe, Control.Proxy.Trans.Reader, Control.Proxy.Trans.State,- Control.Proxy.Trans.Tutorial, Control.Proxy.Trans.Writer, Control.Proxy.Tutorial, Control.Proxy.Prelude, Control.Proxy.Prelude.Base, Control.Proxy.Prelude.IO,- Control.Proxy.Prelude.Kleisli,- Control.Pipe,- Control.Pipe.Core,- Control.Pipe.Tutorial,- Data.Closed+ Control.Proxy.Prelude.Kleisli Default-Language: Haskell98