packages feed

pipes-core (empty) → 0.0.1

raw patch · 10 files changed

+1397/−0 lines, 10 filesdep +basedep +categoriesdep +lifted-basesetup-changed

Dependencies added: base, categories, lifted-base, monad-control, mtl, void

Files

+ Control/Category/Multiplicative.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE MultiParamTypeClasses #-}++-- | This module contains Multiplicative and Comultiplicative type classes,+-- which provide a generalization of 'splitP' and 'joinP' to arbitrary monoidal+-- categories.+module Control.Category.Multiplicative where++import Control.Category.Monoidal++-- | Monoidal category with a multiplication natural transformation.+--+-- A multiplicative structure on @k@ is the same thing as a monoid object+-- structure on the identity functor, when End(k) is given the pointwise+-- monoidal structure.+--+-- Laws:+--+-- > first unit . mult = idl+-- > second unit . mult = idr+-- > mult . first mult = mult . second mult . associate+--+class Monoidal k p => Multiplicative k p where+  unit :: k (Id k p) a+  mult :: k (p a a) a++-- | Comonoidal category with a comultiplication natural transformation.+--+-- A comultiplicative structure on @k@ is the same thing as a coalgebra object+-- structure on the identity functor, when End(k) is given the pointwise+-- comonoidal structure.+--+-- Laws:+--+-- > first counit . comult = coidl+-- > second counit . comult = coidr+-- > first diag . diag = disassociate . second diag . diag+--+class Monoidal k p => Comultiplicative k p where+  counit :: k a (Id k p)+  comult :: k a (p a a)
+ Control/Pipe.hs view
@@ -0,0 +1,450 @@+module Control.Pipe (+  -- * Tutorial+  --+  -- | This library provides a single data type: 'Pipe'.+  --+  -- '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 stream.+  --+  -- Let's 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' allows the first @n@ values it receives to pass through+  -- undisturbed, then it outputs a cute message and shuts down.  Shutdown is+  -- automatic when you reach the end of the monad.  You don't need to send a+  -- special signal to connected 'Pipe's to let them know you are done handling+  -- input or generating output.+  --+  -- 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 of type @a@ from upstream 'Pipe's and 'yield's+  -- output of type @a@ to downstream 'Pipe's.  @take'@ uses 'IO' as its base+  -- monad because it invokes the 'putStrLn' function.  If we remove the call to+  -- 'putStrLn' the compiler infers the following type instead, which is+  -- polymorphic in the base monad:+  --+  -- > take' :: (Monad m) => Int -> Pipe a a m ()+  --+  -- 'Pipe's are conservative about using the base monad.  In fact, you can only+  -- invoke the base monad by using the 'lift' function.  If you never use+  -- 'lift', your 'Pipe' will translate into pure code.+  --+  -- Now let's create a function that converts a list into a 'Pipe' by+  -- 'yield'ing each element of the list:+  --+  -- > fromList :: (Monad m) => [a] -> Pipe () a m ()+  -- > fromList = mapM_ yield+  --+  -- We use @()@ as the input type of the 'Pipe' since it doesn't need any input+  -- from an upstream 'Pipe'.  You can think of @fromList@ as a one way 'Pipe'+  -- that can only deliver output, which makes it suitable for the first stage in+  -- a 'Pipeline'.  We provide a type synonym for this common case:+  --+  -- > type Producer b m r = Pipe () b m r+  --+  -- You can then rewrite the type signature for @fromList@ as:+  --+  -- > fromList :: (Monad m) => [a] -> Producer a m ()+  --+  -- The compiler would be ok with a polymorphic input type, since without any+  -- calls to 'await' it doesn't need to constrain it.  However, using @()@ makes+  -- it clear in the types that this 'Pipe' is designed to be used as a 'Producer',+  -- and statically prevents a number of mistakes when 'Pipe's are combined.+  --+  -- 'Producer's resemble enumerators in other libraries because they are a data+  -- source.  It is not illegal to use 'await' in a 'Producer', it just returns+  -- @()@ immediately without blocking.+  --+  -- Now let's create a 'Pipe' that prints every value delivered to it and never+  -- terminates:+  --+  -- > printer :: (Show a) => Pipe a Void IO b+  -- > printer = forever $ do+  -- >     x <- await+  -- >     lift $ print x+  --+  -- The 'Void' in @printer@'s type signature indicates that it never delivers+  -- output downstream, so it represents the final stage in a 'Pipeline'.  Again,+  -- we provide a type synonym for this common case:+  --+  -- > type Consumer a m r = Pipe a Void m r+  --+  -- So we could instead write @printer@'s type as:+  --+  -- > printer :: (Show a) => Consumer a IO b+  --+  -- 'Consumer's resemble iteratees in other libraries because they are a data+  -- sink.  'Consumer's never use 'yield' statements.+  --+  -- What makes 'Pipe's useful is the ability to compose them into 'Pipeline's.+  -- For that, we provide a '>+>' operator (and its right-to-left counterpart+  -- '<+<'):+  --+  -- > (>+>) :: Pipe a b m r -> Pipe b c m r -> Pipe a c m r+  -- > (<+<) :: Pipe b c m r -> Pipe a b m r -> Pipe a c m r+  --+  -- For example, here is how you can compose the above 'Pipe's:+  --+  -- > pipeline :: Pipe () Void IO ()+  -- > pipeline = fromList [1..] >+> take' 3 >+> printer+  --+  -- This represents a self-contained 'Pipeline' and we provide a type synonym+  -- for this common case:+  --+  -- > type Pipeline m r = Pipe () Void m r+  --+  -- Like many other monad transformers, you convert the 'Pipe' monad back to the+  -- base monad using some sort of \"@run...@\" function.  In this case, we+  -- provide a 'runPipe' function:+  --+  -- > runPipe :: Pipeline IO r -> IO r+  --+  -- 'runPipe' is actually more general, since it works with any+  -- 'MonadBaseControl', but we will work with the above simplified signature in+  -- this tutorial.+  --+  -- There are also more general versione of 'runPipe' which work in+  -- any monad, but don't have any exception-safety guarantees, so they should+  -- only be used for 'Pipe's that don't allocate any scarce resources.+  --+  -- > runPipePipe :: (Monad m) => Pipeline m r -> m (Either SomeException r)+  -- > runPurePipe_ :: (Monad m) => Pipeline m r -> m r+  --+  -- 'runPipe', 'runPurePipe' and 'runPurePipe_' only work on self-contained+  -- 'Pipeline's.  We explicitly require @()@ as input type and 'Void' as output+  -- type to ensure that the pipeline doesn't 'await' or 'yield' any value.  If a+  -- 'Pipe' is polymorphic in its input type (for example because it never uses+  -- 'await'), then it can always be used as the first stage of a 'Pipeline'.+  -- Similarly, a 'Pipe' that is polymorphic in its output type can be used as+  -- the final stage.+  --+  -- It is generally good practice to use @()@ (resp. 'Void') explicitly as the+  -- input (resp. output) type of a producer (resp. consumer), since it gives the+  -- compiler more information on the intent of the 'Pipe', and makes some common+  -- errors detectable at compile time.+  --+  -- Let's try using 'runPipe':+  --+  -- >>> runPipe pipeline+  -- 1+  -- 2+  -- 3+  -- You shall not pass!+  --+  -- Fascinating!  Our 'Pipe' terminated even though @printer@ never terminates+  -- and @fromList@ never terminates when given an infinite list.  To illustrate+  -- why our 'Pipe' terminated, let's outline the flow control rules for 'Pipe'+  -- composition.+  --+  -- * Execution begins at the most downstream 'Pipe' (@printer@ in our example).+  --+  -- * If a 'Pipe' 'await's input, it blocks and transfers control to the next+  --   'Pipe' upstream until that 'Pipe' 'yield's back a value.+  --+  -- * If a 'Pipe' 'yield's output, it restores control to the original+  --   downstream 'Pipe' that was 'await'ing its input and binds its result to+  --   the return value of the 'await' command.+  --+  -- * If a 'Pipe' terminates, it terminates every other 'Pipe' composed with it.+  --+  -- The last rule is crucial.  If a 'Pipe' terminates then every downstream+  -- 'Pipe' depending on its output cannot proceed, and upstream 'Pipe's are+  -- never evaluated because the terminated 'Pipe' will not request values from+  -- them any longer.+  --+  -- So in our previous example, the 'Pipeline' terminated because @take' 3@+  -- terminated and brought down the entire 'Pipeline' with it.+  --+  -- 'Pipe's promote loose coupling, allowing you to mix and match them+  -- transparently using composition.  For example, we can define a new+  -- 'Producer' pipe 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 compose it with any of our previous 'Pipe's:+  --+  -- >>> runPipe $ prompt >+> take' 3 >+> printer+  -- Enter a number:+  -- 1<Enter>+  -- 1+  -- Enter a number:+  -- 2<Enter>+  -- 2+  -- Enter a number:+  -- 3<Enter>+  -- 3+  -- You shall not pass!+  --+  -- 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 $ (fromList [1..3] >> fromList [10..12]) >+> printer+  -- 1+  -- 2+  -- 3+  -- 10+  -- 11+  -- 12+  --+  -- Here's how you would concatenate 'Consumer's:+  --+  -- > print' :: (Show a) => Int -> Consumer a IO ()+  -- > print' n = take' n >+> printer+  -- +  -- >>> runPipe $ fromList [1..] >+> (print' 3 >> print' 4)+  -- 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 $ fromList [1..] >+> (take' 3 >> take' 4) >+> printer+  -- 1+  -- 2+  -- 3+  -- You shall not pass!+  -- 4+  -- 5+  -- 6+  -- 7+  -- You shall not pass!+  --+  -- Pipe composition imposes an important limitation: You can only compose+  -- 'Pipe's that have the same return type.  For example, we could write the+  -- following function:+  --+  -- > deliver :: (Monad m) => Int -> Consumer a m [a]+  -- > deliver n = replicateM n await+  --+  -- ... and we might try to compose it with @fromList@:+  --+  -- >>> runPipe $ fromList [1..10] >+> deliver 3 -- wrong!+  --+  -- ... but this wouldn't type-check, because @fromList@ has a return type of+  -- @()@ and @deliver@ has a return type of @[Int]@.  All 'Pipe's in a+  -- composition need to have the same return type, since the return value of the+  -- composed 'Pipe' is taken from the 'Pipe' that terminates first, and there's+  -- no general way to determine which one it is statically.+  --+  -- Fortunately, we don't have to rewrite the @fromList@ function because we can+  -- add a return value using vertical concatenation:+  --+  -- >>> runPipe $ (fromList [1..10] >> return []) >+> deliver 3+  -- [1,2,3]+  --+  -- ... although a more idiomatic Haskell version would be:+  --+  -- >>> runPipe $ (fromList [1..10] *> pure Nothing) >+> (Just <$> deliver 3)+  -- Just [1,2,3]+  --+  -- which can be written using the 'Control.Pipe.Combinators.$$' operator:+  --+  -- >>> runPipe $ fromList [1..10] $$ deliver 3+  -- 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+  -- make a mistake and request more input than @fromList@ can deliver:+  --+  -- >>> runPipe $ fromList [1..10] $$ deliver 99+  -- Nothing+  --+  -- The type system saved me by forcing me to handle all possible ways my+  -- program could terminate.+  --+  -- Now what if you want 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?  In 'Control.Pipe.Combinators' we find:+  --+  -- > consume :: (Monad m) => Consumer a m [a]+  --+  -- but it turns out that it's not possible to write such a 'Pipe' using only+  -- the primitive introduced so far, since we need a way to intercept upstream+  -- termination and return the current accumulated list of input values before+  -- terminating ourselves.+  --+  -- So we need to introduce a new primitive operation:+  --+  -- > tryAwait :: (Monad m) => Pipe a b m (Maybe a)+  --+  -- 'tryAwait' works very similarly to 'await', with two key differences:+  --+  --   1. When upstream 'yield's some value @x@, 'tryAwait' returns @Just x@.+  --+  --   2. When upstream terminates, 'tryAwait' returns @Nothing@ instead of+  --      terminating the current 'Pipe' immediately.+  --+  -- When 'tryAwait' returns @Nothing@, the current 'Pipe' has a chance to+  -- perform some final actions (typically 'yield' a final value or terminate+  -- with a result) before being forcefully shut down.  At that stage, further+  -- invocations of 'tryAwait' will keep returning @Nothing@, while using 'await'+  -- will terminate the pipe immediately.+  --+  -- Note that 'Pipe' termination only propagates through composition.  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+  -- @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.+  --+  -- We now turn our attention to a very important feature of pipes: resource+  -- finalization.+  --+  -- Say we have the file \"test.txt\" with the following contents:+  --+  -- > This is a test.+  -- > Don't panic!+  -- > Calm down, please!+  --+  -- .. and we wish to lazily read a line at a time from it:+  --+  -- > handleReader' :: Handle -> Producer Text IO ()+  -- > handleReader' h = do+  -- >     eof <- lift $ hIsEOF h+  -- >     unless eof $ do+  -- >         s <- lift $ pack <$> hGetLine h+  -- >         yield s+  -- >         handleReader' h+  --+  -- Suppose, for the sake of example, that we know in advance how many lines we+  -- need to read from the file. We can then use composition and the 'Monad'+  -- instance to try to build a resource-efficient version that only reads as+  -- many lines as we request:+  --+  -- > read' :: Int -> Producer Text IO ()+  -- > read' n = do+  -- >     lift $ putStrLn "Opening file ..."+  -- >     h <- lift $ openFile "test.txt" ReadMode+  -- >     take' n <+< handleReader' h+  -- >     lift $ putStrLn "Closing file ..."+  -- >     lift $ hClose h+  --+  -- Now compose!+  --+  -- >>> runPipe $ read' 2 >+> printer+  -- Opening file ...+  -- "This is a test."+  -- "Don't panic!"+  -- You shall not pass!+  -- Closing file ...+  --+  -- >>> runPipe $ read' 99 >+> printer+  -- Opening file ...+  -- "This is a test."+  -- "Don't panic!"+  -- "Calm down, please!"+  -- Closing file ...+  --+  -- In the first example, the pipeline terminates because @take'@ only requested+  -- 2 lines.  In the second example, it terminates because @readFile'@ ran out+  -- of input.  However, in both cases the 'Pipe' never reads more lines than we+  -- request and frees \"test.txt\" immediately when it is no longer needed.+  --+  -- Even more importantly, the @file@ is never opened if we replace @printer@+  -- with a 'Pipe' that never demands input:+  --+  -- >>> runPipe $ read' 2 >+> lift (putStrLn "I don't need input")+  -- I don't need input+  --+  -- However, this @read'@ is not resource-safe in certain situations. For+  -- example, take the following pipe:+  --+  -- >>> runPipe $ read' 3 >+> take' 1 >+> printer+  -- Opening file ...+  -- "This is a test."+  -- You shall not pass!+  --+  -- Oh no!  Our 'Pipe' didn't properly close our file!  @take' 1@ terminated+  -- before @read' 3@, preventing @read' 3@ from properly closing \"test.txt\".+  --+  -- Similarly, any exception thrown during execution of the 'Pipeline' can cause+  -- the @hClose@ statement to be skipped, leaking an open handle.+  --+  -- We can force the @read' 3@ 'Pipe' to always close the file handle regardless+  -- of exceptions or premature termination by using the 'bracket' function:+  --+  -- > safeRead' :: Int -> Producer Text IO ()+  -- > safeRead' n = bracket+  -- >   (putStrLn "Opening file..." >> openFile "test.txt" ReadMode)+  -- >   (\h -> putStrLn "Closing file..." >> hClose h)+  -- >   (\h -> handleReader' h >+> take' n)+  --+  -- 'bracket' is similar to the homonymous function in 'Control.Exception': it+  -- takes a function that creates some \"resource\", a function that disposes of+  -- the created resource, and a function which takes the resource and returns a+  -- 'Pipe':+  --+  -- > bracket :: Monad m+  -- >         => m r                   -- create resource+  -- >         -> (r -> m y)            -- destroy resource+  -- >         -> (r -> Pipe a b m x)   -- use resource in a 'Pipe'+  -- >         -> Pipe a b m x+  --+  -- Note that the \"create\" and \"destroy\" actions operate within the base+  -- monad, so it's not possible to use 'yield' and 'await' there.+  --+  -- Using @safeRead'@ instead of @read'@ will now produce the desired behavior:+  --+  -- >>> runPipe $ safeRead' 3 >+> take' 1 >+> printer+  -- Opening file...+  -- "This is a test."+  -- You shall not pass!+  -- Closing file...+  --+  -- We also provide exception-handling primitives like 'catch' and+  -- 'onException'. See 'Control.Pipe.Exception' for more details on exception+  -- handling and a complete list of primitives.+  --+  -- Resource finalization and exception handling functionalities work in any+  -- base monad, so we provide a 'Pipe'-specific mechanism for throwing+  -- exceptions which does not suffer from the limitation of only being catchable+  -- in the @IO@ monad:+  --+  -- > throw :: (Monad m, Exception e) => e -> Pipe a b m r+  --+  -- However, exceptions thrown by other means (like 'error' or @throw@ in+  -- 'Control.Exception'), can only be caught when the 'Pipeline' is run with+  -- 'runPipe'.  If you use 'runPurePipe', such an exception will abruptly+  -- terminate the whole 'Pipeline', and resource finalization will not be+  -- guaranteed.++  -- * Implementation+  module Control.Pipe.Common,+  module Control.Pipe.Monoidal+  ) where++import Control.Pipe.Common+import Control.Pipe.Monoidal
+ Control/Pipe/Category.hs view
@@ -0,0 +1,76 @@+{-# LANGUAGE MultiParamTypeClasses, TypeFamilies #-}++module Control.Pipe.Category (+  -- | This module contains category-theoretic instances corresponding to basic+  -- pipe combinators in 'Control.Pipe.Common' and 'Control.Pipe.Monoidal'.+  PipeC(..),+  IFunctor(..),+  ) where++import Control.Categorical.Bifunctor+import Control.Category+import Control.Category.Associative+import Control.Category.Braided+import Control.Category.Monoidal+import Control.Category.Multiplicative+import Control.Monad+import Control.Pipe.Common+import Control.Pipe.Monoidal+import Data.Void+import Prelude hiding ((.), id)++-- | Category of pipes.+--+-- Composition corresponds to '<+<' and identity to 'idP'.+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 p2 . PipeC p1 = PipeC (p2 <+< p1)++-- | Identity-on-objects functor.+--+-- This is part of the interface of Arrow.+class Category k => IFunctor k where+  arr :: (a -> b) -> k a b++instance Monad m => IFunctor (PipeC m r) where+  arr = PipeC . pipe++instance Monad m => PFunctor Either (PipeC m r) (PipeC m r) where+  first = PipeC . firstP . unPipeC where++instance Monad m => QFunctor Either (PipeC m r) (PipeC m r) where+  second = PipeC . secondP . unPipeC where++instance Monad m => Bifunctor Either (PipeC m r) (PipeC m r) (PipeC m r) where+  bimap f g = first f >>> second g++instance Monad m => Associative (PipeC m r) Either where+  associate = PipeC associateP++instance Monad m => Disassociative (PipeC m r) Either where+  disassociate = PipeC disassociateP++type instance Id (PipeC m r) Either = Void++instance Monad m => Monoidal (PipeC m r) Either where+  idl = arr idl+  idr = arr idr++instance Monad m => Comonoidal (PipeC m r) Either where+  coidl = arr coidl+  coidr = arr coidr++instance Monad m => Braided (PipeC m r) Either where+  braid = arr braid++instance Monad m => Symmetric (PipeC m r) Either where++instance Monad m => Comultiplicative (PipeC m r) Either where+  counit = PipeC discard+  comult = PipeC splitP++instance Monad m => Multiplicative (PipeC m r) Either where+  unit = arr absurd+  mult = PipeC joinP
+ Control/Pipe/Combinators.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE ScopedTypeVariables #-}+-- | Basic pipe combinators.+module Control.Pipe.Combinators (+  -- ** Control operators+  tryAwait,+  forP,+  -- ** Composition+  ($$),+  -- ** Producers+  fromList,+  -- ** Folds+  -- | Folds are pipes that consume all their input and return a value. Some of+  -- them, like 'fold1', do not return anything when they don't receive any+  -- input at all. That means that the upstream return value will be returned+  -- instead.+  --+  -- Folds are normally used as 'Consumer's, but they are actually polymorphic+  -- in the output type, to encourage their use in the implementation of+  -- higher-level combinators.+  fold,+  fold1,+  consume,+  consume1,+  -- ** List-like pipe combinators+  -- The following combinators are analogous to the corresponding list+  -- functions, when the stream of input values is thought of as a (potentially+  -- infinite) list.+  take,+  drop,+  takeWhile,+  takeWhile_,+  dropWhile,+  intersperse,+  groupBy,+  filter,+  -- ** Other combinators+  pipeList,+  nullP,+  feed,+  ) where++import Control.Applicative+import Control.Monad+import Control.Pipe+import Control.Pipe.Exception+import Data.Maybe+import Prelude hiding (until, take, drop, concatMap, filter, takeWhile, dropWhile, catch)++-- | Like 'await', but returns @Just x@ when the upstream pipe yields some value+-- @x@, and 'Nothing' when it terminates.+--+-- Further calls to 'tryAwait' after upstream termination will keep returning+-- 'Nothing', whereas calling 'await' will terminate the current pipe+-- immediately.+tryAwait :: Monad m => Pipe a b m (Maybe a)+tryAwait = catch (Just <$> await) $ \(_ :: BrokenUpstreamPipe) -> return Nothing++-- | Execute the specified pipe for each value in the input stream.+--+-- Any action after a call to 'forP' will be executed when upstream terminates.+forP :: Monad m => (a -> Pipe a b m r) -> Pipe a b m ()+forP f = tryAwait >>= maybe (return ()) (\a -> f a >> forP f)++-- | Connect producer to consumer, ignoring producer return value.+infixr 5 $$+($$) :: Monad m => Pipe x a m r' -> Pipe a y m r -> Pipe x y m (Maybe r)+p1 $$ p2 = (p1 >> return Nothing) >+> fmap Just p2++-- | Successively yield elements of a list.+fromList :: Monad m => [a] -> Pipe x a m ()+fromList = mapM_ yield++-- | A pipe that terminates immediately.+nullP :: Monad m => Pipe a b m ()+nullP = return ()++-- | A fold pipe. Apply a binary function to successive input values and an+-- accumulator, and return the final result.+fold :: Monad m => (b -> a -> b) -> b -> Pipe a x m b+fold f = go+  where+    go x = tryAwait >>= maybe (return x) (go . f x)++-- | A variation of 'fold' without an initial value for the accumulator. This+-- pipe doesn't return any value if no input values are received.+fold1 :: Monad m => (a -> a -> a) -> Pipe a x m a+fold1 f = tryAwait >>= maybe discard (fold f)++-- | Accumulate all input values into a list.+consume :: Monad m => Pipe a x m [a]+consume = pipe (:) >+> (fold (.) id <*> pure [])++-- | Accumulate all input values into a non-empty list.+consume1 :: Monad m => Pipe a x m [a]+consume1 = pipe (:) >+> (fold1 (.) <*> pure [])++-- | Act as an identity for the first 'n' values, then terminate.+take :: Monad m => Int -> Pipe a a m ()+take n = replicateM_ n $ await >>= yield++-- | Remove the first 'n' values from the stream, then act as an identity.+drop :: Monad m => Int -> Pipe a a m r+drop n = replicateM_ n await >> idP++-- | Apply a function with multiple return values to the stream.+pipeList :: Monad m => (a -> [b]) -> Pipe a b m r+pipeList f = forever $ await >>= mapM_ yield . f++-- | Act as an identity until as long as inputs satisfy the given predicate.+-- Return the first element that doesn't satisfy the predicate.+takeWhile :: Monad m => (a -> Bool) -> Pipe a a m a+takeWhile p = go+  where+    go = await >>= \x -> if p x then yield x >> go else return x++-- | Variation of 'takeWhile' returning @()@.+takeWhile_ :: Monad m => (a -> Bool) -> Pipe a a m ()+takeWhile_ p = takeWhile p >> return ()++-- | Remove inputs as long as they satisfy the given predicate, then act as an+-- identity.+dropWhile :: Monad m => (a -> Bool) -> Pipe a a m r+dropWhile p = (takeWhile p >+> discard) >>= yield >> idP++-- | Yield Nothing when an input satisfying the predicate is received.+intersperse :: Monad m => (a -> Bool) -> Pipe a (Maybe a) m r+intersperse p = forever $ do+  x <- await+  when (p x) $ yield Nothing+  yield $ Just x++-- | Group input values by the given predicate.+groupBy :: Monad m => (a -> a -> Bool) -> Pipe a [a] m r+groupBy p = streaks >+> createGroups+  where+    streaks = await >>= \x -> yield (Just x) >> streaks' x+    streaks' x = do+      y <- await+      unless (p x y) $ yield Nothing+      yield $ Just y+      streaks' y+    createGroups = forever $+      takeWhile_ isJust >+>+      pipe fromJust >+>+      (consume1 >>= yield)++-- | Remove values from the stream that don't satisfy the given predicate.+filter :: Monad m => (a -> Bool) -> Pipe a a m r+filter p = forever $ takeWhile_ p++-- | Feed an input element to a pipe.+feed :: Monad m => a -> Pipe a b m r -> Pipe a b m r++-- this could be implemented as+-- feed x p = (yield x >> idP) >+> p+-- but this version is more efficient+feed _ (Pure r) = return r+feed _ (Throw e) = throw e+feed a (Free c h) = case go a c of+  (False, p) -> p >>= feed a+  (True, p)  -> join p+  where+    go a (Await k) = (True, return $ k a)+    go _ (Yield y c) = (False, yield y >> return c)+    go _ (M m s) = (False, liftP s m)
+ Control/Pipe/Common.hs view
@@ -0,0 +1,330 @@+{-# LANGUAGE DeriveDataTypeable, FlexibleContexts #-}+module Control.Pipe.Common (+  -- ** Types+  Pipe(..),+  Producer,+  Consumer,+  Pipeline,+  Void,++  -- ** Primitives+  --+  -- | 'await' and 'yield' are the two basic 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 trnasformer, you can use+  -- 'lift' to invoke the base monad. For example:+  --+  -- > check :: Pipe a a IO r+  -- > check = forever $ do+  -- >   x <- await+  -- >   lift $ putStrLn $ "Can " ++ show x ++ " pass?"+  -- >   ok <- lift $ read <$> getLine+  -- >   when ok $ yield x+  await,+  yield,+  masked,++  -- ** Basic combinators+  pipe,+  idP,+  discard,+  (>+>),+  (<+<),++  -- ** Running pipes+  runPipe,+  runPurePipe,+  runPurePipe_,++  -- ** Low level types+  BrokenDownstreamPipe,+  BrokenUpstreamPipe,+  PipeF(..),+  MaskState(..),++  -- ** Low level primitives+  --+  -- | These functions can be used to implement exception-handling combinators.+  -- For normal use, prefer the functions defined in 'Control.Pipe.Exception'.+  throwP,+  catchP,+  liftP,+  ensure,+  ) where++import Control.Applicative+import Control.Category+import Control.Exception (SomeException, Exception)+import qualified Control.Exception.Lifted as E+import Control.Monad+import Control.Monad.Trans (MonadTrans, lift)+import Control.Monad.Trans.Control+import Data.Maybe+import Data.Typeable+import Data.Void+import Prelude hiding (id, (.), catch)++-- | The 'BrokenDownstreamPipe' exception is used to signal termination of the+-- downstream portion of a 'Pipeline' after the current pipe.+--+-- There is usually no need to catch this exception explicitly, a pipe will+-- terminate automatically when the downstream pipe terminates.+data BrokenDownstreamPipe = BrokenDownstreamPipe+  deriving (Show, Typeable)++instance Exception BrokenDownstreamPipe++-- | The 'BrokenUpstreamPipe' exception is used to signal termination of the+-- upstream portion of a 'Pipeline' before the current pipe+--+-- A 'BrokenUpstreamPipe' exception can be caught to perform cleanup actions+-- immediately before termination, like returning a result or yielding+-- additional values.+data BrokenUpstreamPipe = BrokenUpstreamPipe+  deriving (Show, Typeable)++instance Exception BrokenUpstreamPipe++-- | Type of action in the base monad.+data MaskState+  = Masked                    -- ^ Action to be run with asynchronous exceptions masked.+  | Unmasked                  -- ^ Action to be run with asynchronous exceptions unmasked.+  | Ensure                    -- ^ Action to be run regardless of downstream failure.+  | Finalizer SomeException   -- ^ Finalizer action.++data PipeF a b m x+  = M (m x) MaskState+  | Await (a -> x)+  | Yield b x++instance Monad m => Functor (PipeF a b m) where+  fmap f (M m s) = M (liftM f m) s+  fmap f (Await k) = Await (f . k)+  fmap f (Yield b c) = Yield b (f c)++-- | The base type for pipes.+--+--  [@a@] The type of input received fom upstream pipes.+--+--  [@b@] The type of output delivered to downstream pipes.+--+--  [@c@] The base monad.+--+--  [@d@] The type of the monad's final result.+data Pipe a b m r+  -- Pipe is a free monad over the functor+  --+  -- data PipeF' a b m r+  --   = Catch (PipeF a b m r) (SomeException -> r)+  --   | Throw e+  -- +  -- but is implemented inline because it makes the code simpler.+  = Pure r+  | Free (PipeF a b m (Pipe a b m r))+         (SomeException -> Pipe a b m r)+  | Throw SomeException++-- | A pipe that can only produce values.+type Producer b m = Pipe () b m++-- | A pipe that can only consume values.+type Consumer a m = Pipe a Void m++-- | A self-contained pipeline that is ready to be run.+type Pipeline m = Pipe () Void m++instance Monad m => Monad (Pipe a b m) where+  return = Pure+  Pure r >>= f = f r+  Free c h >>= f = Free (fmap (>>= f) c)+                        (h >=> f)+  Throw e >>= _ = Throw e++instance Monad m => Functor (Pipe a b m) where+  fmap = liftM++instance Monad m => Applicative (Pipe a b m) where+  pure = return+  (<*>) = ap++liftF :: Monad m => PipeF a b m r -> Pipe a b m r+liftF c = Free (fmap return c) throwP++-- | Throw an exception within the 'Pipe' monad.+throwP :: Monad m => SomeException -> Pipe a b m r+throwP = Throw++-- | Catch an exception within the pipe monad.+catchP :: Monad m+       => Pipe a b m r+       -> (SomeException -> Pipe a b m r)+       -> Pipe a b m r+catchP (Pure r) _ = return r+catchP (Free c h1) h2 = Free+  (fmap (`catchP` h2) c)+  (\e -> catchP (h1 e) h2)+catchP (Throw e) h = h e++-- | Wait for input from upstream within the 'Pipe' monad.+--+-- 'await' blocks until input is ready.+await :: Monad m => Pipe a b m a+await = liftF $ Await id++-- | Pass output downstream within the 'Pipe' monad.+--+-- 'yield' blocks until the downstream pipe calls 'await' again.+yield :: Monad m => b -> Pipe a b m ()+yield x = liftF $ Yield x ()++-- | Execute an action in the base monad with the given 'MaskState'.+liftP :: Monad m => MaskState -> m r -> Pipe a b m r+liftP s m = liftF (M m s)++instance MonadTrans (Pipe a b) where+  lift = liftP Unmasked++-- | Execute an action in the base monad with asynchronous exceptions masked.+--+-- This function is effective only if the 'Pipeline' is run with 'runPipe',+-- otherwise it is identical to 'lift'+masked :: Monad m => m r -> Pipe a b m r+masked = liftP Masked++-- | Ensure an action is executed regardless of downstream termination.+ensure :: Monad m => m r -> Pipe a b m r+ensure = liftP Ensure++finalizer :: Monad m => SomeException -> m r -> Pipe a b m r+finalizer e = liftP (Finalizer e)++-- | 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++-- | The identity pipe.+idP :: Monad m => Pipe a a m r+idP = pipe id++-- | The 'discard' pipe silently discards all input fed to it.+discard :: Monad m => Pipe a b m r+discard = forever await++data Composition a b c m x y+  = AdvanceFirst (Pipe a c m x)+  | AdvanceSecond (Pipe a c m y)+  | AdvanceBoth x y++compose :: Monad m+   => PipeF a b m x+   -> PipeF b c m y+   -> Maybe (Composition a b c m x y)+compose (Yield b x) (Await k) = Just $ AdvanceBoth x (k b)+compose _ (Yield c y) = Just $ AdvanceSecond (yield c >> return y)+compose _ (M m s) = Just $ AdvanceSecond (liftP s m)+compose (M _ (Finalizer _)) _ = Nothing+compose (M m s) _ = Just $ AdvanceFirst (liftP s m)+compose (Await k) _ = Just $ AdvanceFirst (liftM k await)++finalize2 :: Monad m+          => PipeF b c m r+          -> Maybe (Pipe a c m r)+finalize2 (Await _) = Nothing+finalize2 (M m s) = Just $ liftP s m+finalize2 (Yield c r) = Just $ yield c >> return r++finalize1 :: Monad m+          => Maybe SomeException+          -> PipeF a b m r+          -> Maybe (Pipe a c m r)+finalize1 e c = case c of+  M m Ensure -> go m+  M m (Finalizer _) -> go m+  _ -> Nothing+  where+    go m = Just $+      finalizer (fromMaybe (E.toException BrokenUpstreamPipe) e) m++infixl 9 >+>+-- | Left to right pipe composition.+(>+>) :: Monad m => Pipe a b m r -> Pipe b c m r -> Pipe a c m r+p1 >+> p2 = case (p1, p2) of+  (Free c1 h1, Free c2 h2) -> case compose c1 c2 of+    Nothing -> p1 >+> h2 (E.toException BrokenUpstreamPipe)+    Just (AdvanceFirst comp) -> catchP comp (return . h1) >>= \p1' -> p1' >+> p2+    Just (AdvanceSecond comp) -> catchP comp (return . h2) >>= \p2' -> p1 >+> p2'+    Just (AdvanceBoth p1' p2') -> p1' >+> p2'+  (Throw e, Free c h) -> terminate2 c h (Just e)+  (Pure r, Free c h) -> terminate2 c h Nothing+  (Free c h, Throw e) -> terminate1 c h (Just e)+  (Free c h, Pure r) -> terminate1 c h Nothing+  (Pure r, Throw e) -> case (E.fromException e :: Maybe BrokenUpstreamPipe) of+    Nothing -> throwP e+    Just _  -> return r+  (_, Throw e) -> throwP e+  (_, Pure r) -> return r+  where+    terminate1 c h e = case finalize1 e c of+      Nothing   -> h (fromMaybe (E.toException BrokenDownstreamPipe) e) >+> p2+      Just comp -> catchP comp (return . h) >>= \p1' -> p1' >+> p2+    terminate2 c h e = case finalize2 c of+      Nothing   -> p1 >+> h (fromMaybe (E.toException BrokenUpstreamPipe) e)+      Just comp -> catchP comp (return . h) >>= \p2' -> p1 >+> p2'++infixr 9 <+<+-- | Right to left pipe composition.+(<+<) :: Monad m => Pipe b c m r -> Pipe a b m r -> Pipe a c m r+p2 <+< p1 = p1 >+> p2++-- | Run a self-contained 'Pipeline', converting it to an action in the base+-- monad.+--+-- This function is exception-safe. Any exception thrown in the base monad+-- during execution of the pipeline will be captured by+-- 'Control.Pipe.Exception.catch' statements in the 'Pipe' monad.+runPipe :: MonadBaseControl IO m => Pipeline m r -> m r+runPipe p = E.mask $ \restore -> run restore p+  where+    run restore = go+      where+        go (Pure r) = return r+        go (Free c h) = stepPipe try c >>= \x -> case x of+          Left e   -> go $ h e+          Right p' -> go p'+        go (Throw e) = E.throwIO e++        try m s = E.try $ case s of+          Unmasked -> restore m+          _ -> m++-- | Run a self-contained pipeline over an arbitrary monad, with fewer+-- exception-safety guarantees than 'runPipe'.+--+-- Only pipe termination exceptions and exceptions thrown using+-- 'Control.Pipe.Exception.throw' will be catchable within the 'Pipe' monad.+-- Any other exception will terminate execution immediately and finalizers will+-- not be called.+--+-- Any captured exception will be returned in the left component of the result.+runPurePipe :: Monad m => Pipeline m r -> m (Either SomeException r)+runPurePipe (Pure r) = return $ Right r+runPurePipe (Throw e) = return $ Left e+runPurePipe (Free c h) = stepPipe try c >>= runPurePipe . either h id+  where try m _ = liftM Right m++-- | A version of 'runPurePipe' which rethrows any captured exception instead+-- of returning it.+runPurePipe_ :: Monad m => Pipeline m r -> m r+runPurePipe_ = runPurePipe >=> either E.throw return++stepPipe :: Monad m+         => (m r -> MaskState -> m (Either SomeException r))+         -> PipeF () Void m r+         -> m (Either SomeException r)+stepPipe _ (Await k) = return . Right $ k ()+stepPipe _ (Yield x _) = absurd x+stepPipe try (M m s) = try m s
+ Control/Pipe/Exception.hs view
@@ -0,0 +1,116 @@+module Control.Pipe.Exception (+  throw,+  catch,+  bracket,+  bracket_,+  bracketOnError,+  finally,+  onException,+  ) where++import qualified Control.Exception as E+import Control.Pipe.Common+import Prelude hiding (catch)++-- | Catch an exception within the 'Pipe' monad.+--+-- This function takes a 'Pipe', runs it, and if an exception is raised it+-- executes the handler, passing it the value of the exception.  Otherwise, the+-- result is returned as normal.+--+-- For example, given a 'Pipe':+--+-- > reader :: Pipe () String IO ()+--+-- we can use 'catch' to resume after an exception. For example:+--+-- > safeReader :: Pipe () (Either SomeException String) IO ()+-- > safeReader = catch (reader >+> 'Pipe' Right) $ \e -> do+-- >   yield $ Left e+--+-- Note that there is no guarantee that the handler will actually be executed,+-- as any action in a 'Pipe': 'Pipe's at either side can terminate before the+-- handler has a chance to be executed.+--+-- It is therefore common to use 'ensure' within an exception handler to+-- perform cleanup or finalization of resources.  However, we recommend using+-- 'finally' or 'bracket' for such use cases.+catch :: (Monad m, E.Exception e)+      => Pipe a b m r               -- ^ 'Pipe' to run+      -> (e -> Pipe a b m r)        -- ^ handler function+      -> Pipe a b m r+catch p h = catchP p $ \e -> case E.fromException e of+  Nothing -> throwP e+  Just e' -> h e'++-- | Throw an exception within the 'Pipe' monad.+--+-- An exception thrown with 'throw' can be caught by 'catch' with any base+-- monad.+--+-- If the exception is not caught in the 'Pipeline' at all, it will be rethrown+-- as a normal Haskell exception when using 'runPipe'.  Note that 'runPurePipe'+-- returns the exception in an 'Either' value, instead.+throw :: (Monad m, E.Exception e) => e -> Pipe a b m r+throw = throwP . E.toException++-- | Like 'finally', but only performs the final action if there was an+-- exception raised by the 'Pipe'.+onException :: Monad m+            => Pipe a b m r       -- ^ 'Pipe' to run first+            -> Pipe a b m s       -- ^ 'Pipe' to run if an exception happens+            -> Pipe a b m r+onException p w = catchP p $ \e -> w >> throw e++-- | A specialized variant of 'bracket' with just a computation to run+-- afterwards.+finally :: Monad m+        => Pipe a b m r           -- ^ 'Pipe' to run first+        -> m s                    -- ^ finalizer action+        -> Pipe a b m r+finally p w = do+  r <- onException p (ensure w)+  ensure w+  return r++-- | Allocate a resource within the base monad, run a 'Pipe', then ensure the+-- resource is released.+--+-- The typical example is reading from a file:+--+-- > bracket+-- >   (openFile "filename" ReadMode)+-- >   hClose+-- >   (\handle -> do+-- >       line <- lift $ hGetLine handle+-- >       yield line+-- >       ...)+bracket :: Monad m+        => m r                  -- ^ action to acquire resource+        -> (r -> m y)           -- ^ action to release resource+        -> (r -> Pipe a b m x)  -- ^ 'Pipe' to run in between+        -> Pipe a b m x+bracket open close run = do+  r <- liftP Masked open+  finally (run r) (close r)++-- | A variant of 'bracket' where the return value from the allocation action+-- is not required.+bracket_ :: Monad m+         => m r                 -- ^ action to run first+         -> m y                 -- ^ action to run last+         -> Pipe a b m x        -- ^ 'Pipe' to run in between+         -> Pipe a b m x+bracket_ open close run =+  bracket open (const close) (const run)++-- | Like 'bracket', but only performs the \"release\" action if there was an+-- exception raised by the 'Pipe'.+bracketOnError :: Monad m+               => m r                     -- ^ action to acquire resource+               -> (r -> m y)              -- ^ action to release resource+               -> (r -> Pipe a b m x)     -- ^ 'Pipe' to run in between+               -> Pipe a b m x+bracketOnError open close run = do+  r <- liftP Masked open+  onException (run r) (ensure $ close r)
+ Control/Pipe/Monoidal.hs view
@@ -0,0 +1,128 @@+module Control.Pipe.Monoidal (+  -- | The combinators in this module allow you to create and manipulate+  -- multi-channel pipes. Multiple input or output channels are represented with+  -- 'Either' types.+  --+  -- Most of the combinators are generalizations of the corresponding functions+  -- in 'Control.Arrow', and obey appropriately generalized laws.+  firstP,+  secondP,+  (***),+  associateP,+  disassociateP,+  discardL,+  discardR,+  swapP,+  joinP,+  splitP,+  loopP,+  ) where++import Control.Category.Associative+import Control.Category.Braided+import Control.Category.Monoidal+import Control.Monad+import qualified Control.Monad.Trans as T+import Control.Monad.State+import Control.Pipe.Common++-- | Create a 'Pipe' that behaves like the given 'Pipe' of the left component+-- of the input, and lets values in the right component pass through.+firstP :: Monad m+       => Pipe a b m r+       -> Pipe (Either a c) (Either b c) m r+firstP (Pure r) = return r+firstP (Throw e) = Throw e+firstP (Free c h) = catchP (step c) (return . h) >>= firstP+  where+    step (M m s) = liftP s m+    step (Yield b x) = yield (Left b) >> return x+    step (Await k) = go+      where+        go = await >>= either (return . k)+                              (yield . Right >=> const go)++-- | This function is the equivalent of 'firstP' for the right component.+secondP :: Monad m+        => Pipe a b m r+        -> Pipe (Either c a) (Either c b) m r+secondP (Pure r) = return r+secondP (Throw e) = Throw e+secondP (Free c h) = catchP (step c) (return . h) >>= secondP+  where+    step (M m s) = liftP s m+    step (Yield b x) = yield (Right b) >> return x+    step (Await k) = go+      where+        go = await >>= either (yield . Left >=> const go)+                              (return . k)++-- | Combine two pipes into a single pipe that behaves like the first on the+-- left component, and the second on the right component.+(***) :: Monad m+      => Pipe a b m r+      -> Pipe a' b' m r+      -> Pipe (Either a a') (Either b b') m r+p1 *** p2 = firstP p1 >+> secondP p2++-- | Convert between the two possible associations of a triple sum.+associateP :: Monad m+           => Pipe (Either (Either a b) c) (Either a (Either b c)) m r+associateP = pipe associate++-- | Inverse of 'associateP'.+disassociateP :: Monad m+              => Pipe (Either a (Either b c)) (Either (Either a b) c) m r+disassociateP = pipe disassociate++-- | Discard all values on the left component.+discardL :: Monad m => Pipe (Either x a) a m r+discardL = firstP discard >+> pipe idl++-- | Discard all values on the right component.+discardR :: Monad m => Pipe (Either a x) a m r+discardR = secondP discard >+> pipe idr++-- | Swap the left and right components.+swapP :: Monad m => Pipe (Either a b) (Either b a) m r+swapP = pipe swap++-- | Yield all input values into both the left and right components of the+-- output.+splitP :: Monad m => Pipe a (Either a a) m r+splitP = forever $ await >>= yield2+  where+    yield2 x = yield (Left x) >> yield (Right x)++-- | Yield both components of input values into the output.+joinP :: Monad m => Pipe (Either a a) a m r+joinP = pipe $ either id id++data Queue a = Queue ![a] ![a]++emptyQueue :: Queue a+emptyQueue = Queue [] []++enqueue :: a -> Queue a -> Queue a+enqueue x (Queue xs ys) = Queue (x : xs) ys++dequeue :: Queue a -> (Queue a, Maybe a)+dequeue (Queue (x : xs) ys) = (Queue xs ys, Just x)+dequeue q@(Queue [] []) = (q, Nothing)+dequeue (Queue [] ys) = dequeue (Queue (reverse ys) [])++-- | The 'loopP' combinator allows to create 'Pipe's whose output value is fed+-- back to the 'Pipe' as input.+loopP :: Monad m => Pipe (Either a c) (Either b c) m r -> Pipe a b m r+loopP = go emptyQueue+  where+    go _ (Pure r) = return r+    go _ (Throw e) = throwP e+    go q (Free c h) = case step q c of+      (q', p') -> catchP p' (return . h) >>= go q'++    step q (Await k) = case dequeue q of+      (q', x) -> (q', maybe (liftM (k . Left) await) (return . k . Right) x)+    step q (Yield (Right x) c) = (enqueue x q, return c)+    step q (Yield (Left x) c) = (q, yield x >> return c)+    step q (M m s) = (q, liftP s m)
+ LICENSE view
@@ -0,0 +1,25 @@+Copyright (c) 2012, Gabriel Gonzalez+Copyright (c) 2012, Paolo Capriotti+All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:+    * Redistributions of source code must retain the above copyright notice,+      this list of conditions and the following disclaimer.+    * Redistributions in binary form must reproduce the above copyright notice,+      this list of conditions and the following disclaimer in the documentation+      and/or other materials provided with the distribution.+    * Neither the name of Gabriel Gonzalez nor the names of other contributors+      may be used to endorse or promote products derived from this software+      without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ pipes-core.cabal view
@@ -0,0 +1,65 @@+Name: pipes-core+Version: 0.0.1+Cabal-Version: >=1.10.1+Build-Type: Simple+License: BSD3+License-File: LICENSE+Copyright: 2012 Gabriel Gonzalez, 2012 Paolo Capriotti+Author: Gabriel Gonzalez, Paolo Capriotti+Maintainer: p.capriotti@gmail.com+Stability: Experimental+Homepage: https://github.com/pcapriotti/pipes-core+Bug-Reports: https://github.com/pcapriotti/pipes-core/issues+Synopsis: Compositional pipelines+Description:+  This library offers an abstraction similar in scope to+  iteratees\/enumerators\/enumeratees, but with different characteristics and+  naming conventions.+  .+  Difference with traditional iteratees:+  .+  * /Simpler semantics/: There is only one data type ('Pipe'), two primitives+    ('await' and 'yield'), and only one way to compose 'Pipe's ('>+>').  In+    fact, ('>+>') is just convenient syntax for the composition operator in+    'Category'. Most pipes can be implemented just using the 'Monad' instance+    and composition.+  .+  * /Different naming conventions/: Enumeratees are called 'Pipe's, Enumerators+    are 'Producer's, and Iteratees are 'Consumer's.  'Producer's and 'Consumer's+    are just type synonyms for 'Pipe's with either the input or output end+    closed.+  .+  * /Pipes form a Category/: that means that composition is associative, and+    that there is an identity 'Pipe'.+  .+  * /"Vertical" concatenation works on every 'Pipe'/: ('>>'),+    concatenates 'Pipe's. Since everything is a 'Pipe', you can use it to+    concatenate 'Producer's, 'Consumer's, and even intermediate 'Pipe' stages.+    Vertical concatenation can be combined with composition to create elaborate+    combinators, without the need of executing pipes in \"passes\" or resuming+    partially executed pipes.+  .+  Check out "Control.Pipe" for a copious introduction (in the spirit of the+  @iterIO@ library), and "Control.Pipe.Combinators" for some basic combinators+  and 'Pipe' examples.+Category: Control, Enumerator+Tested-With: GHC ==7.0.3+Source-Repository head+    Type: git+    Location: https://github.com/pcapriotti/pipes-core++Library+    Build-Depends: base >= 4 && < 5+                 , mtl+                 , categories+                 , void+                 , lifted-base+                 , monad-control+    Exposed-Modules: Control.Pipe+                   , Control.Pipe.Category+                   , Control.Pipe.Combinators+                   , Control.Pipe.Common+                   , Control.Pipe.Exception+                   , Control.Pipe.Monoidal+                   , Control.Category.Multiplicative+    Default-Language: Haskell2010