pipe-enumerator 0.1.0.0 → 0.2.0.0
raw patch · 2 files changed
+125/−68 lines, 2 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Pipes.Enumerator: fromStream :: Stream a -> Maybe [a]
+ Pipes.Enumerator: iterateeToPipe :: Monad m => Iteratee a m r -> Proxy () (Maybe a) b' b m (Either SomeException (r, Maybe [a]))
+ Pipes.Enumerator: pipeToIteratee :: Monad m => Proxy a' (Maybe a) () b m r -> Iteratee a m r
+ Pipes.Enumerator: stepToPipe :: Monad m => Step a m r -> Proxy () (Maybe a) b' b m (Either SomeException (r, Maybe [a]))
+ Pipes.Enumerator: toSingletonStream :: Maybe a -> Stream a
+ Pipes.Enumerator: toStream :: Maybe [a] -> Stream a
Files
- pipe-enumerator.cabal +12/−4
- src/Pipes/Enumerator.lhs +113/−64
pipe-enumerator.cabal view
@@ -1,6 +1,6 @@ name: pipe-enumerator-version: 0.1.0.0-synopsis: Pipes/iteratees bridge library+version: 0.2.0.0+synopsis: A bidirectional bridge between pipes and iteratees homepage: https://github.com/zadarnowski/pipe-enumerator category: Control, Pipes stability: alpha@@ -12,8 +12,16 @@ description: - This library provides a bridge between pipe-based and iteratee-based programs.+ This library defines a set of functions that convert between+ the "Pipes" and "Data.Enumerator" paradigms. The conversion+ is bidirectional: an appropriately-typed pipe can be converted+ into an 'E.Iteratee' and back into a pipe. In addition, a pipe+ can be fed into an iteratee (or, more specifically, 'E.Step'),+ resulting in an 'E.Enumerator'. + The library has been designed specifically for use with "Snap",+ but I'm sure that many other interesting uses of it exist.+ cabal-version: >= 1.18 build-type: Simple license: BSD3@@ -26,7 +34,7 @@ source-repository this type: git location: https://github.com/zadarnowski/pipe-enumerator.git- tag: 0.1.0.0+ tag: 0.2.0.0 library hs-source-dirs: src
src/Pipes/Enumerator.lhs view
@@ -1,61 +1,122 @@+> -- | Module: Pipes.Enumerator+> -- Description: A bidirectional bridge between pipes and iteratees+> -- Copyright: © 2015 Patryk Zadarnowski <pat@jantar.org>+> -- License: BSD3+> -- Maintainer: pat@jantar.org+> -- Stability: experimental+> -- Portability: portable+> --+> -- This module defines a set of functions that convert between+> -- the "Pipes" and "Data.Enumerator" paradigms. The conversion+> -- is bidirectional: an appropriately-typed pipe can be converted+> -- into an 'E.Iteratee' and back into a pipe. In addition, a pipe+> -- can be fed into an iteratee (or, more specifically, 'E.Step'),+> -- resulting in an 'E.Enumerator'.+> --+> -- The library has been designed specifically for use with "Snap",+> -- but I'm sure that many other interesting uses of it exist.+ > module Pipes.Enumerator (+> fromStream, toStream, toSingletonStream,+> iterateeToPipe, stepToPipe,+> pipeToIteratee, > pipeToEnumerator > ) where +> import Control.Exception (SomeException) > import Control.Monad.Trans.Class > import qualified Pipes.Internal as P > import qualified Data.Enumerator as E + Data Type Reference =================== - 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+ Definitions of the relevant Pipe and Iteratee types, for reference: - type Pipe a b = Proxy () a () b+ data P.Proxy a' a b' b m r+ = P.Request a' (a -> P.Proxy a' a b' b m r )+ | P.Respond b (b' -> P.Proxy a' a b' b m r )+ | P.M (m (P.Proxy a' a b' b m r))+ | P.Pure r - type Enumerator a m b = Step a m b -> Iteratee a m b+ type P.Pipe a b = P.Proxy () a () b - data Step a m b =- Continue (Stream a -> Iteratee a m b)- | Yield b (Stream a)- | Error SomeException+ type E.Enumerator a m b = E.Step a m b -> E.Iteratee a m b - newtype Iteratee a m b = Iteratee { runIteratee :: m (Step a m b) }+ data E.Step a m b =+ E.Continue (E.Stream a -> E.Iteratee a m b)+ | E.Yield b (E.Stream a)+ | E.Error SomeException - data Stream a =- Chunks [a]- | EOF+ newtype E.Iteratee a m b = E.Iteratee { E.runIteratee :: m (E.Step a m b) } - Connect a pipe to an iteratee, effectively converting it into an enumerator,- generalised slightly to allow distinct input and output types. The chunks of- the input stream are fed into the pipe one element at the time, and the pipe's- output is fed to the iteratee one element per chunk. Once the input reaches- @E.EOF@, the pipe is fed an infinite stream of @Nothing@ until it ends with- a pure value. In effect, both the pipe and the iteratee are always consumed- fully, with the resulting enumerator returning the results of both.+ data E.Stream a =+ E.Chunks [a]+ | E.EOF +> -- | Converts a 'E.Stream' to an optional list.+> fromStream :: E.Stream a -> Maybe [a]+> fromStream (E.Chunks cs) = Just cs+> fromStream (E.EOF) = Nothing++> -- | Converts an optional list to a 'E.Stream'.+> toStream :: Maybe [a] -> E.Stream a+> toStream (Just cs) = E.Chunks cs+> toStream (Nothing) = E.EOF++> -- | Converts an optional value to a singleton 'E.Stream'.+> toSingletonStream :: Maybe a -> E.Stream a+> toSingletonStream (Just c) = E.Chunks [c]+> toSingletonStream (Nothing) = E.EOF++> -- | Converts an 'E.Iteratee' into a 'P.Consumer'.+> iterateeToPipe :: Monad m => E.Iteratee a m r -> P.Proxy () (Maybe a) b' b m (Either SomeException (r, Maybe [a]))+> iterateeToPipe = P.M . fmap stepToPipe . E.runIteratee++> -- | Converts a 'E.Step' into a 'P.Consumer'.+> stepToPipe :: Monad m => E.Step a m r -> P.Proxy () (Maybe a) b' b m (Either SomeException (r, Maybe [a]))+> stepToPipe (E.Continue ik) = P.Request () (P.M . fmap stepToPipe . E.runIteratee . ik . toSingletonStream)+> stepToPipe (E.Yield r xs) = return (Right (r, fromStream xs))+> stepToPipe (E.Error e) = return (Left e)++> -- | Converts a 'P.Pipe' into an 'E.Iteratee'.+> -- Any output of the pipe is quietly discarded.+> pipeToIteratee :: Monad m => P.Proxy a' (Maybe a) () b m r -> E.Iteratee a m r+> pipeToIteratee = convert1 []+> where+> convert1 cs (P.Request _ pk) = convert2 pk cs+> convert1 cs (P.Respond _ pk) = convert1 cs (pk ())+> convert1 cs (P.M m) = lift m >>= convert1 cs+> convert1 cs (P.Pure r) = E.yield r (E.Chunks cs)+> convert2 pk (c:cs) = convert1 cs (pk (Just c))+> convert2 pk [] = E.continue (convert3 pk)+> convert3 pk (E.Chunks cs) = convert2 pk cs+> convert3 pk (E.EOF) = convert4 pk+> convert4 pk = convert5 (pk Nothing)+> convert5 (P.Request _ pk) = convert4 pk+> convert5 (P.Respond _ pk) = convert5 (pk ())+> convert5 (P.M m) = lift m >>= convert5+> convert5 (P.Pure r) = E.yield r E.EOF++> -- | Feed the output of a 'P.Pipe' to a 'E.Step', effectively converting it into+> -- an 'E.Enumerator', generalised slightly to allow distinct input and output+> -- types. The chunks of the input stream are fed into the pipe one element at+> -- the time, and the pipe's output is fed to the iteratee one element per chunk.+> -- Once the input reaches 'E.EOF', the pipe is fed an infinite stream of 'Nothing'+> -- until it ends with a pure value. In effect, both the pipe and the iteratee are+> -- always consumed fully, with the resulting enumerator returning the results of both. > pipeToEnumerator :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r) > pipeToEnumerator = advanceIteratee [] > where - State Machine 1- ===============-- Advance a single iteratee step, keeping track of any input @cs@ that has been- supplied by the upstream enumerator but not yet used. If the target iteratee blocks,- we will supply it with input obtained by advancing source pipe; otherwise, we'll- simply drain the pipe to completion, in order to retrieve the remainder of the- upstream input and the pipe's return value:- > advanceIteratee :: Monad m => [a] -> P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r) > advanceIteratee cs p (E.Continue ik) = advancePipe cs ik p > advanceIteratee cs p s = drainPipe cs s p - Advance a pipe, feeding its response into a blocked iteratee @ik@.+> advanceIteratee' :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)+> advanceIteratee' p (E.Continue ik) = advancePipe' ik p+> advanceIteratee' p s = drainPipe' s p > advancePipe :: Monad m => [a] -> (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r) > advancePipe cs ik (P.Request _ pk) = advancePipeWithChunks ik pk cs@@ -63,57 +124,45 @@ > advancePipe cs ik (P.M m) = lift m >>= advancePipe cs ik > advancePipe cs ik (P.Pure q) = ik (E.EOF) E.>>== finish q (E.Chunks cs) -> advancePipeWithChunks :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)-> advancePipeWithChunks ik pk (c:cs) = advancePipe cs ik (pk (Just c))-> advancePipeWithChunks ik pk [] = E.continue (advancePipeWithStream ik pk)+> advancePipe' :: Monad m => (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)+> advancePipe' ik (P.Request _ pk) = advancePipeWithEOF ik pk+> advancePipe' ik (P.Respond x pk) = ik (E.Chunks [x]) E.>>== advanceIteratee' (pk ())+> advancePipe' ik (P.M m) = lift m >>= advancePipe' ik+> advancePipe' ik (P.Pure q) = ik (E.EOF) E.>>== finish q (E.EOF) > advancePipeWithStream :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r) > advancePipeWithStream ik pk (E.Chunks cs) = advancePipeWithChunks ik pk cs > advancePipeWithStream ik pk (E.EOF) = advancePipeWithEOF ik pk +> advancePipeWithChunks :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)+> advancePipeWithChunks ik pk (c:cs) = advancePipe cs ik (pk (Just c))+> advancePipeWithChunks ik pk [] = E.continue (advancePipeWithStream ik pk)++> advancePipeWithEOF :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)+> advancePipeWithEOF ik pk = advancePipe' ik (pk Nothing)+ > drainPipe :: Monad m => [a] -> E.Step b m r -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r) > drainPipe cs s (P.Request _ pk) = drainPipeWithChunks s pk cs > drainPipe cs s (P.Respond _ pk) = drainPipe cs s (pk ()) > drainPipe cs s (P.M m) = lift m >>= drainPipe cs s > drainPipe cs s (P.Pure q) = finish q (E.Chunks cs) s -> drainPipeWithChunks :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)-> drainPipeWithChunks s pk (c:cs) = drainPipe cs s (pk (Just c))-> drainPipeWithChunks s pk [] = E.continue (drainPipeWithStream s pk)--> drainPipeWithStream :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)-> drainPipeWithStream s pk (E.Chunks cs) = drainPipeWithChunks s pk cs-> drainPipeWithStream s pk (E.EOF) = drainPipeWithEOF s pk--- State Machine 2- ===============--> advanceIteratee' :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)-> advanceIteratee' p (E.Continue ik) = advancePipe' ik p-> advanceIteratee' p s = drainPipe' s p--> advancePipe' :: Monad m => (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)-> advancePipe' ik (P.Request _ pk) = advancePipeWithEOF ik pk-> advancePipe' ik (P.Respond x pk) = ik (E.Chunks [x]) E.>>== advanceIteratee' (pk ())-> advancePipe' ik (P.M m) = lift m >>= advancePipe' ik-> advancePipe' ik (P.Pure q) = ik (E.EOF) E.>>== finish q (E.EOF)--> advancePipeWithEOF :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)-> advancePipeWithEOF ik pk = advancePipe' ik (pk Nothing)- > drainPipe' :: Monad m => E.Step b m r -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r) > drainPipe' s (P.Request _ pk) = drainPipeWithEOF s pk > drainPipe' s (P.Respond _ pk) = drainPipe' s (pk ()) > drainPipe' s (P.M m) = lift m >>= drainPipe' s > drainPipe' s (P.Pure q) = finish q (E.EOF) s -> drainPipeWithEOF :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)-> drainPipeWithEOF s pk = drainPipe' s (pk Nothing)+> drainPipeWithStream :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)+> drainPipeWithStream s pk (E.Chunks cs) = drainPipeWithChunks s pk cs+> drainPipeWithStream s pk (E.EOF) = drainPipeWithEOF s pk +> drainPipeWithChunks :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)+> drainPipeWithChunks s pk (c:cs) = drainPipe cs s (pk (Just c))+> drainPipeWithChunks s pk [] = E.continue (drainPipeWithStream s pk) - Final Step- ==========+> drainPipeWithEOF :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)+> drainPipeWithEOF s pk = drainPipe' s (pk Nothing) > finish :: Monad m => q -> E.Stream a -> E.Step b m r -> E.Iteratee a m (q, r) > finish q xs (E.Yield r _) = E.yield (q, r) xs