Permalink
Browse files

Commit before first public release.

  • Loading branch information...
1 parent 623aa27 commit d44a85aa11799569b03fdc2eaf96e2eca3c65596 @Gabriel439 Gabriel439 committed Jan 9, 2012
Showing with 258 additions and 287 deletions.
  1. +5 −232 Data/Pipe.hs
  2. +246 −0 Data/Pipe/Common.hs
  3. +0 −26 Data/Pipe/IO.hs
  4. +0 −24 Data/Pipe/Pure.hs
  5. +2 −0 Setup.hs
  6. +5 −5 pipe.cabal
View
@@ -286,237 +286,10 @@ You shall not pass!
that the type system will prevent you from unintentionally trying to use
them.
+ I would like to acknowledge Mario Blazevic, whose excellent concurrency
+ article in Issue 19 of The Monad Reader was the inspiration for this
+ library.
-}
-module Data.Pipe (
- -- * Types
- Pipe,
- Zero,
- Producer,
- Consumer,
- Pipeline,
- -- * Creating Pipes
- {-|
- 'yield' and 'await' are the only two primitives you need to create
- 'Pipe's. Because 'Pipe' is a monad, you can assemble them using
- ordinary @do@ notation. Since 'Pipe' is also a monad transformer, you
- can use 'lift' to invoke the base monad. For example:
-
-> runItByMe :: Pipe a a IO ()
-> runItByMe = forever $ do
-> x <- await
-> ok <- lift $ emailMeAndWaitForResponse x
-> when ok (yield x)
- -}
- await,
- yield,
- pipe,
- -- * Composing Pipes
- {-|
- There are two possible category implementations for 'Pipe':
-
- ['Lazy' composition]
-
- * Evaluate downstream stages before upstream stages
-
- * Flow terminates when the consumer cannot proceed
-
- * The most downstream pipe that cleanly terminates produces the
- return value
-
- ['Strict' composition]
-
- * Evaluate upstream stages before downstream stages
-
- * Flow terminates when the producer cannot proceed
-
- * The most upstream pipe that cleanly terminates produces the return
- value
-
- You probably want 'Lazy' composition.
-
- Both category implementations satisfy the category laws:
-
- * Composition is associative. You will get the exact same sequence of
- monadic actions and the same return value upon running the pipe
- regardless of how you group composition.
-
- * 'id' is the identity. Composing a pipe with 'id' will not affect the
- pipe's sequence of monadic actions or return value when you run it.
- -}
- Lazy(..),
- Strict(..),
- -- ** Composition operators
- {-|
- I provide convenience functions for composition that take care of
- newtype wrapping and unwrapping. For example:
-
-> p1 <+< p2 = unLazy $ Lazy p1 <<< Lazy p2
-
- '<+<' and '<-<' correspond to '<<<' from "Control.Category"
-
- '>+>' and '>+>' correspond to '>>>' from "Control.Category"
-
- '<+<' and '>+>' use 'Lazy' composition (Mnemonic: + for optimistic
- evaluation)
-
- '<-<' and '>->' use 'Strict' composition (Mnemonic: - for pessimistic
- evaluation)
- -}
- (<+<),
- (>+>),
- (<-<),
- (>->),
- -- * Running Pipes
- runPipe,
- discard
- ) where
-
-import Control.Applicative
-import Control.Category
-import Control.Monad
-import Control.Monad.Trans
-import Prelude hiding ((.), id)
-
-{-|
- The base type for pipes
-
- [@a@] The type of input received from upstream pipes
+module Data.Pipe (module Data.Pipe.Common) where
- [@b@] The type of output delivered to downstream pipes
-
- [@m@] The base monad
-
- [@r@] The type of the monad's final result
--}
-data Pipe a b m r =
- Pure r -- pure = Pure
- | M (m (Pipe a b m r)) -- Monad
- | Await (a -> Pipe a b m r ) -- Functor
- | Yield (b, Pipe a b m r ) -- Functor
-
-instance (Monad m) => Functor (Pipe a b m) where
- fmap f c = case c of
- Pure r -> Pure $ f r
- M mc -> M $ liftM (fmap f) mc
- Await fc -> Await $ fmap (fmap f) fc
- Yield fc -> Yield $ fmap (fmap f) fc
-
-instance (Monad m) => Applicative (Pipe a b m) where
- pure = Pure
- f <*> x = case f of
- Pure r -> fmap r x
- M mc -> M $ liftM (<*> x) mc
- Await fc -> Await $ fmap (<*> x) fc
- Yield fc -> Yield $ fmap (<*> x) fc
-
-instance (Monad m) => Monad (Pipe a b m) where
- return = pure
- m >>= f = case m of
- Pure r -> f r
- M mc -> M $ liftM (>>= f) mc
- Await fc -> Await $ fmap (>>= f) fc
- Yield fc -> Yield $ fmap (>>= f) fc
-
-instance MonadTrans (Pipe a b) where lift = M . liftM pure
-
--- | A datatype with no exposed constructors
-data Zero = Zero
-
--- | A pipe that can only produce values
-type Producer b m r = Pipe Zero b m r
-
--- | A pipe that can only consume values
-type Consumer a m r = Pipe a Zero m r
-
--- | A self-contained pipeline that is ready to be run
-type Pipeline m r = Pipe Zero Zero m r
-
-{-|
- Wait for input from upstream within the 'Pipe' monad:
-
- 'await' blocks until input is ready.
-
-> do
-> x <- await
-> ...
--}
-await :: Pipe a b m a
-await = Await Pure
-
-{-|
- Pass output downstream within the 'Pipe' monad:
-
- 'yield' blocks until the output has been received.
-
-> do
-> ...
-> yield x
--}
-yield :: b -> Pipe a b m ()
-yield x = Yield (x, Pure ())
-
-{-|
- Convert a pure function into a pipe
-
-> pipe = forever $ do
-> x <- await
-> yield (f x)
--}
-pipe :: (Monad m) => (a -> b) -> Pipe a b m r
-pipe f = forever $ await >>= yield . f
-
-newtype Lazy m r a b = Lazy { unLazy :: Pipe a b m r}
-newtype Strict m r a b = Strict { unStrict :: Pipe a b m r}
-
-(<+<), (<-<) :: (Monad m) => Pipe b c m r -> Pipe a b m r -> Pipe a c m r
-p1 <+< p2 = unLazy (Lazy p1 <<< Lazy p2)
-p1 <-< p2 = unStrict (Strict p1 <<< Strict p2)
-
-(>+>), (>->) :: (Monad m) => Pipe a b m r -> Pipe b c m r -> Pipe a c m r
-p1 >+> p2 = unLazy (Lazy p1 >>> Lazy p2)
-p1 >-> p2 = unStrict (Strict p1 >>> Strict p2)
-
--- The associativities help pipe chains detect termination quickly
-infixr 9 <+<, >->
-infixl 9 >+>, <-<
-
-instance (Monad m) => Category (Lazy m r) where
- id = Lazy $ pipe id
- Lazy p1' . Lazy p2' = Lazy $ case (p1', p2') of
- (Yield (x1, p1), p2 ) -> yield x1 >> p1 <+< p2
- (M m1 , p2 ) -> lift m1 >>= \p1 -> p1 <+< p2
- (Pure r1 , _ ) -> Pure r1
- (Await f1 , Yield (x2, p2)) -> f1 x2 <+< p2
- (p1 , Await f2 ) -> await >>= \x -> p1 <+< f2 x
- (p1 , M m2 ) -> lift m2 >>= \p2 -> p1 <+< p2
- (_ , Pure r2 ) -> Pure r2
-
-instance (Monad m) => Category (Strict m r) where
- id = Strict $ pipe id
- Strict p1' . Strict p2' = Strict $ case (p1', p2') of
- (_ , Pure r2 ) -> Pure r2
- (p1 , M m2 ) -> lift m2 >>= \p2 -> p1 <-< p2
- (p1 , Await f2 ) -> await >>= \x -> p1 <-< f2 x
- (Await f1 , Yield (x2, p2)) -> f1 x2 <-< p2
- (Pure r1 , _ ) -> Pure r1
- (M m1 , p2 ) -> lift m1 >>= \p1 -> p1 <-< p2
- (Yield (x1, p1), p2 ) -> yield x1 >> p1 <-< p2
-
-{-|
- Run the 'Pipe' monad transformer, converting it back into the base monad
-
- 'runPipe' will not work on a pipe that has loose input or output ends. If
- your pipe is still generating output, use the 'discard' pipe to discard the
- output. If your pipe still requires input, then how do you expect to run
- it?
--}
-runPipe :: (Monad m) => Pipeline m r -> m r
-runPipe p' = case p' of
- Pure r -> return r
- M mp -> mp >>= runPipe
- Await f -> runPipe $ f Zero
- Yield (Zero, p) -> runPipe p
-
--- | The 'discard' pipe silently discards all input fed to it.
-discard :: (Monad m) => Pipe a Zero m r
-discard = forever await
+import Data.Pipe.Common
Oops, something went wrong.

0 comments on commit d44a85a

Please sign in to comment.