enumerator 0.4.8 → 0.4.9
raw patch · 13 files changed
+741/−244 lines, 13 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Enumerator: ($=) :: Monad m => Enumerator ao m (Step ai m b) -> Enumeratee ao ai m b -> Enumerator ai m b
+ Data.Enumerator: (=$) :: Monad m => Enumeratee ao ai m b -> Iteratee ai m b -> Iteratee ao m b
+ Data.Enumerator: checkContinue0 :: Monad m => (Enumerator a m b -> (Stream a -> Iteratee a m b) -> Iteratee a m b) -> Enumerator a m b
+ Data.Enumerator: checkContinue1 :: Monad m => ((s1 -> Enumerator a m b) -> s1 -> (Stream a -> Iteratee a m b) -> Iteratee a m b) -> s1 -> Enumerator a m b
+ Data.Enumerator: tryIO :: MonadIO m => IO b -> Iteratee a m b
+ Data.Enumerator.Binary: mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee ByteString ByteString m b
+ Data.Enumerator.Binary: mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee ByteString ByteString m b
+ Data.Enumerator.List: mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b
+ Data.Enumerator.List: mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b
+ Data.Enumerator.Text: mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee Text Text m b
+ Data.Enumerator.Text: mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee Text Text m b
Files
- enumerator.cabal +1/−1
- hs/Data/Enumerator.hs +126/−0
- hs/Data/Enumerator/Binary.hs +66/−38
- hs/Data/Enumerator/List.hs +52/−26
- hs/Data/Enumerator/Text.hs +59/−28
- hs/Data/Enumerator/Util.hs +0/−11
- src/api-docs.anansi +122/−0
- src/enumerator.anansi +2/−2
- src/io.anansi +24/−27
- src/list-analogues.anansi +124/−62
- src/public-interface.anansi +18/−3
- src/utilities.anansi +66/−13
- tests/Properties.hs +81/−33
enumerator.cabal view
@@ -1,5 +1,5 @@ name: enumerator-version: 0.4.8+version: 0.4.9 synopsis: Reliable, high-performance processing with left-fold enumerators license: MIT license-file: license.txt
hs/Data/Enumerator.hs view
@@ -39,6 +39,8 @@ , ($$) , (>==>) , (<==<)+ , (=$)+ , ($=) -- ** Running iteratees , run@@ -54,9 +56,12 @@ , joinE , Data.Enumerator.sequence , enumEOF+ , checkContinue0+ , checkContinue1 , checkDoneEx , checkDone , isEOF+ , tryIO -- ** Testing and debugging , printChunks@@ -451,7 +456,36 @@ check (Yield x _) = return x check (Error e) = throwError e +infixr 0 =$ ++-- | @enum =$ iter = 'joinI' (enum $$ iter)+--+-- “Wraps” an iteratee /inner/ in an enumeratee /wrapper/.+-- The resulting iteratee will consume /wrapper/’s input type and+-- yield /inner/’s output type.+--+-- Note: if the inner iteratee yields leftover input when it finishes,+-- that extra will be discarded.+--+-- As an example, consider an iteratee that converts a stream of UTF8-encoded+-- bytes into a single 'TL.Text':+--+-- > consumeUTF8 :: Monad m => Iteratee ByteString m Text+--+-- It could be written with either 'joinI' or '(=$)':+--+-- > import Data.Enumerator.Text as ET+-- >+-- > consumeUTF8 = joinI (decode utf8 $$ ET.consume)+-- > consumeUTF8 = decode utf8 =$ ET.consume+--+-- Since: 0.4.9++(=$) :: Monad m => Enumeratee ao ai m b -> Iteratee ai m b -> Iteratee ao m b+enum =$ iter = joinI (enum $$ iter)++ -- | Flatten an enumerator/enumeratee pair into a single enumerator. joinE :: Monad m@@ -465,7 +499,37 @@ Yield x _ -> return x Continue _ -> error "joinE: divergent iteratee" +infixr 0 $= ++-- | @enum $= enee = 'joinE' enum enee@+--+-- “Wraps” an enumerator /inner/ in an enumeratee /wrapper/.+-- The resulting enumerator will generate /wrapper/’s output type.+--+-- As an example, consider an enumerator that yields line character counts+-- for a text file (e.g. for source code readability checking):+--+-- > enumFileCounts :: FilePath -> Enumerator Int IO b+--+-- It could be written with either 'joinE' or '($=)':+--+-- > import Data.Text as T+-- > import Data.Enumerator.List as EL+-- > import Data.Enumerator.Text as ET+-- >+-- > enumFileCounts path = joinE (enumFile path) (EL.map T.length)+-- > enumFileCounts path = enumFile path $= EL.map T.length+--+-- Since: 0.4.9++($=) :: Monad m+ => Enumerator ao m (Step ai m b)+ -> Enumeratee ao ai m b+ -> Enumerator ai m b+($=) = joinE++ -- | Feeds outer input elements into the provided iteratee until it yields -- an inner input, passes that to the inner iteratee, and then loops. @@ -522,6 +586,68 @@ isEOF = continue $ \s -> case s of EOF -> yield True s _ -> yield False s+++-- | Try to run an IO computation. If it throws an exception, the exception+-- is caught and converted into an {\tt Error}.+--+-- Since: 0.4.9++tryIO :: MonadIO m => IO b -> Iteratee a m b+tryIO io = Iteratee $ do+ tried <- liftIO (Exc.try io)+ return $ case tried of+ Right b -> Yield b (Chunks [])+ Left err -> Error err+++-- | A common pattern in 'Enumerator' implementations is to check whether+-- the inner 'Iteratee' has finished, and if so, to return its output.+-- 'checkContinue0' passes its parameter a continuation if the 'Iteratee'+-- can still consume input; if not, it returns the iteratee's step.+--+-- The type signature here is a bit crazy, but it's actually very easy to+-- use. Take this code:+--+-- > repeat :: Monad m => a -> Enumerator a m b+-- > repeat x = loop where+-- > loop (Continue k) = k (Chunks [x]) >>== loop+-- > loop step = returnI step+--+-- And rewrite it without the boilerplate:+--+-- > repeat :: Monad m => a -> Enumerator a m b+-- > repeat x = checkContinue0 $ \loop k -> k (Chunks [x] >>== loop+--+-- Since: 0.4.9++checkContinue0 :: Monad m+ => (Enumerator a m b+ -> (Stream a -> Iteratee a m b)+ -> Iteratee a m b)+ -> Enumerator a m b+checkContinue0 inner = loop where+ loop (Continue k) = inner loop k+ loop step = returnI step+++-- | Like 'checkContinue0', but allows each loop step to use a state value:+--+-- > iterate :: Monad m => (a -> a) -> a -> Enumerator a m b+-- > iterate f = checkContinue1 $ \loop a k -> k (Chunks [a]) >>== loop (f a)+--+-- Since: 0.4.9++checkContinue1 :: Monad m+ => ((s1 -> Enumerator a m b)+ -> s1+ -> (Stream a -> Iteratee a m b)+ -> Iteratee a m b)+ -> s1+ -> Enumerator a m b+checkContinue1 inner = loop where+ loop s (Continue k) = inner loop s k+ loop _ step = returnI step
hs/Data/Enumerator/Binary.hs view
@@ -40,6 +40,10 @@ , Data.Enumerator.Binary.concatMap , concatMapM + -- ** Accumulating maps+ , mapAccum+ , mapAccumM+ -- ** Infinite streams , Data.Enumerator.Binary.iterate , iterateM@@ -77,11 +81,9 @@ import Data.Enumerator hiding ( head, drop, iterateM, repeatM, replicateM , generateM, filterM, consume, foldM , concatMapM)-import Data.Enumerator.Util (tryIO) import Control.Monad.IO.Class (MonadIO) import qualified Data.ByteString as B import qualified System.IO as IO-import Data.Function (fix) import qualified Control.Exception as Exc import System.IO.Error (isEOFError) import Data.Word (Word8)@@ -121,11 +123,9 @@ -- Since: 0.4.8 unfold :: Monad m => (s -> Maybe (Word8, s)) -> s -> Enumerator B.ByteString m b-unfold f = loop where- loop s (Continue k) = case f s of- Nothing -> continue k- Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s'- loop _ step = returnI step+unfold f = checkContinue1 $ \loop s k -> case f s of+ Nothing -> continue k+ Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s' -- | Enumerates a stream of bytes by repeatedly applying a computation to@@ -136,13 +136,11 @@ -- Since: 0.4.8 unfoldM :: Monad m => (s -> m (Maybe (Word8, s))) -> s -> Enumerator B.ByteString m b-unfoldM f = loop where- loop s (Continue k) = do- fs <- lift (f s)- case fs of- Nothing -> continue k- Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s'- loop _ step = returnI step+unfoldM f = checkContinue1 $ \loop s k -> do+ fs <- lift (f s)+ case fs of+ Nothing -> continue k+ Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s' -- | @'map' f@ applies /f/ to each input byte and feeds the@@ -189,6 +187,41 @@ checkDoneEx (Chunks [B.pack xs]) (\k' -> loop k' xs) +-- | Similar to 'map', but with a stateful step function.+--+-- Since: 0.4.9++mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b+mapAccum f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case B.uncons x of+ Nothing -> loop s k xs+ Just (b, x') -> case f s b of+ (s', ai) -> k (Chunks [B.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))+++-- | Similar to 'mapM', but with a stateful step function.+--+-- Since: 0.4.9++mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b+mapAccumM f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case B.uncons x of+ Nothing -> loop s k xs+ Just (b, x') -> do+ (s', ai) <- lift (f s b)+ k (Chunks [B.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))++ -- | @'iterate' f x@ enumerates an infinite stream of repeated applications -- of /f/ to /x/. --@@ -197,9 +230,7 @@ -- Since: 0.4.8 iterate :: Monad m => (Word8 -> Word8) -> Word8 -> Enumerator B.ByteString m b-iterate f = loop where- loop byte (Continue k) = k (Chunks [B.singleton byte]) >>== loop (f byte)- loop _ step = returnI step+iterate f = checkContinue1 $ \loop s k -> k (Chunks [B.singleton s]) >>== loop (f s) -- | Similar to 'iterate', except the iteration function is monadic.@@ -207,11 +238,10 @@ -- Since: 0.4.8 iterateM :: Monad m => (Word8 -> m Word8) -> Word8 -> Enumerator B.ByteString m b-iterateM f base = loop (return base) where- loop m_byte (Continue k) = do+iterateM f base = worker (return base) where+ worker = checkContinue1 $ \loop m_byte k -> do byte <- lift m_byte k (Chunks [B.singleton byte]) >>== loop (f byte)- loop _ step = returnI step -- | Enumerates an infinite stream of a single byte.@@ -449,16 +479,13 @@ => Integer -- ^ Buffer size -> IO.Handle -> Enumerator B.ByteString m b-enumHandle bufferSize h = do+enumHandle bufferSize h = checkContinue0 $ \loop k -> do let intSize = fromInteger bufferSize - fix $ \loop step -> case step of- Continue k -> do- bytes <- tryIO (getBytes h intSize)- if B.null bytes- then continue k- else k (Chunks [bytes]) >>== loop- _ -> returnI step+ bytes <- tryIO (getBytes h intSize)+ if B.null bytes+ then continue k+ else k (Chunks [bytes]) >>== loop -- | Read bytes (in chunks of the given buffer size) from the handle, and@@ -494,19 +521,20 @@ Just off -> tryIO (IO.hSeek h IO.AbsoluteSeek off) enum = case count of- Just n -> loop n s+ Just n -> enumRange n s Nothing -> enumHandle bufferSize h s - loop n (Continue k) =- let rem = fromInteger (min bufferSize n) in- if rem <= 0+ enumRange = checkContinue1 $ \loop n k -> let+ rem = fromInteger (min bufferSize n)+ keepGoing = do+ bytes <- tryIO (getBytes h rem)+ if B.null bytes+ then continue k+ else feed bytes+ feed bs = k (Chunks [bs]) >>== loop (n - (toInteger (B.length bs)))+ in if rem <= 0 then continue k- else do- bytes <- tryIO (getBytes h rem)- if B.null bytes- then continue k- else k (Chunks [bytes]) >>== loop (n - (toInteger (B.length bytes)))- loop _ step = returnI step+ else keepGoing getBytes :: IO.Handle -> Int -> IO B.ByteString getBytes h n = do
hs/Data/Enumerator/List.hs view
@@ -33,6 +33,10 @@ , Data.Enumerator.List.concatMap , concatMapM + -- ** Accumulating maps+ , mapAccum+ , mapAccumM+ -- ** Infinite streams , Data.Enumerator.List.iterate , iterateM@@ -114,11 +118,9 @@ -- Since: 0.4.8 unfold :: Monad m => (s -> Maybe (a, s)) -> s -> Enumerator a m b-unfold f = loop where- loop s (Continue k) = case f s of- Nothing -> continue k- Just (a, s') -> k (Chunks [a]) >>== loop s'- loop _ step = returnI step+unfold f = checkContinue1 $ \loop s k -> case f s of+ Nothing -> continue k+ Just (a, s') -> k (Chunks [a]) >>== loop s' -- | Enumerates a stream of elements by repeatedly applying a computation to@@ -129,13 +131,11 @@ -- Since: 0.4.8 unfoldM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Enumerator a m b-unfoldM f = loop where- loop s (Continue k) = do- fs <- lift (f s)- case fs of- Nothing -> continue k- Just (a, s') -> k (Chunks [a]) >>== loop s'- loop _ step = returnI step+unfoldM f = checkContinue1 $ \loop s k -> do+ fs <- lift (f s)+ case fs of+ Nothing -> continue k+ Just (a, s') -> k (Chunks [a]) >>== loop s' -- | @'concatMapM' f@ applies /f/ to each input element and feeds the@@ -186,6 +186,37 @@ mapM f = concatMapM (\x -> Prelude.mapM f [x]) +-- | Similar to 'map', but with a stateful step function.+--+-- Since: 0.4.9++mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b+mapAccum f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case f s x of+ (s', ai) -> k (Chunks [ai]) >>==+ checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)+++-- | Similar to 'mapM', but with a stateful step function.+--+-- Since: 0.4.9++mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b+mapAccumM f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = do+ (s', ai) <- lift (f s x)+ k (Chunks [ai]) >>==+ checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)++ -- | @'iterate' f x@ enumerates an infinite stream of repeated applications -- of /f/ to /x/. --@@ -194,9 +225,7 @@ -- Since: 0.4.8 iterate :: Monad m => (a -> a) -> a -> Enumerator a m b-iterate f = loop where- loop a (Continue k) = k (Chunks [a]) >>== loop (f a)- loop _ step = returnI step+iterate f = checkContinue1 $ \loop s k -> k (Chunks [s]) >>== loop (f s) -- | Similar to 'iterate', except the iteration function is monadic.@@ -205,11 +234,10 @@ iterateM :: Monad m => (a -> m a) -> a -> Enumerator a m b-iterateM f base = loop (return base) where- loop m_a (Continue k) = do+iterateM f base = worker (return base) where+ worker = checkContinue1 $ \loop m_a k -> do a <- lift m_a k (Chunks [a]) >>== loop (f a)- loop _ step = returnI step -- | Enumerates an infinite stream of a single element.@@ -219,7 +247,7 @@ -- Since: 0.4.8 repeat :: Monad m => a -> Enumerator a m b-repeat a = Data.Enumerator.List.iterate (const a) a+repeat a = checkContinue0 $ \loop k -> k (Chunks [a]) >>== loop -- | Enumerates an infinite stream of element. Each element is computed by@@ -266,13 +294,11 @@ generateM :: Monad m => m (Maybe a) -> Enumerator a m b-generateM getNext = loop where- loop (Continue k) = do- next <- lift getNext- case next of- Nothing -> continue k- Just x -> k (Chunks [x]) >>== loop- loop step = returnI step+generateM getNext = checkContinue0 $ \loop k -> do+ next <- lift getNext+ case next of+ Nothing -> continue k+ Just x -> k (Chunks [x]) >>== loop -- | Applies a predicate to the stream. The inner iteratee only receives
hs/Data/Enumerator/Text.hs view
@@ -38,6 +38,10 @@ , Data.Enumerator.Text.concatMap , concatMapM + -- ** Accumulating maps+ , mapAccum+ , mapAccumM+ -- ** Infinite streams , Data.Enumerator.Text.iterate , iterateM@@ -88,7 +92,7 @@ import Data.Enumerator hiding ( head, drop, generateM, filterM, consume , concatMapM, iterateM, repeatM, replicateM , foldM)-import Data.Enumerator.Util (tryIO, tSpanBy, tlSpanBy, reprWord, reprChar, textToStrict)+import Data.Enumerator.Util (tSpanBy, tlSpanBy, reprWord, reprChar, textToStrict) import Control.Monad.IO.Class (MonadIO) import qualified Control.Exception as Exc import Control.Arrow (first)@@ -140,11 +144,9 @@ -- Since: 0.4.8 unfold :: Monad m => (s -> Maybe (Char, s)) -> s -> Enumerator T.Text m b-unfold f = loop where- loop s (Continue k) = case f s of- Nothing -> continue k- Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s'- loop _ step = returnI step+unfold f = checkContinue1 $ \loop s k -> case f s of+ Nothing -> continue k+ Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s' -- | Enumerates a stream of characters by repeatedly applying a computation@@ -155,13 +157,11 @@ -- Since: 0.4.8 unfoldM :: Monad m => (s -> m (Maybe (Char, s))) -> s -> Enumerator T.Text m b-unfoldM f = loop where- loop s (Continue k) = do- fs <- lift (f s)- case fs of- Nothing -> continue k- Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s'- loop _ step = returnI step+unfoldM f = checkContinue1 $ \loop s k -> do+ fs <- lift (f s)+ case fs of+ Nothing -> continue k+ Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s' -- | @'map' f@ applies /f/ to each input character and feeds the@@ -208,6 +208,41 @@ checkDoneEx (Chunks [T.pack xs]) (\k' -> loop k' xs) +-- | Similar to 'map', but with a stateful step function.+--+-- Since: 0.4.9++mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee T.Text T.Text m b+mapAccum f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case T.uncons x of+ Nothing -> loop s k xs+ Just (c, x') -> case f s c of+ (s', ai) -> k (Chunks [T.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))+++-- | Similar to 'mapM', but with a stateful step function.+--+-- Since: 0.4.9++mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee T.Text T.Text m b+mapAccumM f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case T.uncons x of+ Nothing -> loop s k xs+ Just (c, x') -> do+ (s', ai) <- lift (f s c)+ k (Chunks [T.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))++ -- | @'iterate' f x@ enumerates an infinite stream of repeated applications -- of /f/ to /x/. --@@ -216,9 +251,7 @@ -- Since: 0.4.8 iterate :: Monad m => (Char -> Char) -> Char -> Enumerator T.Text m b-iterate f = loop where- loop char (Continue k) = k (Chunks [T.singleton char]) >>== loop (f char)- loop _ step = returnI step+iterate f = checkContinue1 $ \loop s k -> k (Chunks [T.singleton s]) >>== loop (f s) -- | Similar to 'iterate', except the iteration function is monadic.@@ -226,11 +259,10 @@ -- Since: 0.4.8 iterateM :: Monad m => (Char -> m Char) -> Char -> Enumerator T.Text m b-iterateM f base = loop (return base) where- loop m_char (Continue k) = do+iterateM f base = worker (return base) where+ worker = checkContinue1 $ \loop m_char k -> do char <- lift m_char k (Chunks [T.singleton char]) >>== loop (f char)- loop _ step = returnI step -- | Enumerates an infinite stream of a single character.@@ -469,19 +501,18 @@ enumHandle :: MonadIO m => IO.Handle -> Enumerator T.Text m b-enumHandle h = loop where- loop (Continue k) = do- maybeText <- tryIO getText- case maybeText of- Nothing -> continue k- Just text -> k (Chunks [text]) >>== loop- - loop step = returnI step- getText = Exc.catch+enumHandle h = checkContinue0 $ \loop k -> do+ let getText = Exc.catch (Just `fmap` TIO.hGetLine h) (\err -> if isEOFError err then return Nothing else Exc.throwIO err)+ + maybeText <- tryIO getText+ case maybeText of+ Nothing -> continue k+ Just text -> k (Chunks [text]) >>== loop+ -- | Opens a file path in text mode, and passes the handle to 'enumHandle'.
hs/Data/Enumerator/Util.hs view
@@ -1,23 +1,12 @@ {-# LANGUAGE CPP #-} module Data.Enumerator.Util where-import Data.Enumerator import Data.Char (toUpper, intToDigit, ord) import Data.Word (Word8) import qualified Data.Text as T import qualified Data.Text.Lazy as TL--import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Control.Exception as Exc import Numeric (showIntAtBase)--tryIO :: MonadIO m => IO b -> Iteratee a m b-tryIO io = Iteratee $ do- tried <- liftIO (Exc.try io)- return $ case tried of- Right b -> Yield b (Chunks [])- Left err -> Error err pad0 :: Int -> String -> String pad0 size str = padded where
src/api-docs.anansi view
@@ -111,6 +111,54 @@ -- Since: 0.1.1 : +:d apidoc Data.Enumerator.($=)+-- | @enum $= enee = 'joinE' enum enee@+--+-- “Wraps” an enumerator /inner/ in an enumeratee /wrapper/.+-- The resulting enumerator will generate /wrapper/’s output type.+--+-- As an example, consider an enumerator that yields line character counts+-- for a text file (e.g. for source code readability checking):+--+-- > enumFileCounts :: FilePath -> Enumerator Int IO b+--+-- It could be written with either 'joinE' or '($=)':+--+-- > import Data.Text as T+-- > import Data.Enumerator.List as EL+-- > import Data.Enumerator.Text as ET+-- >+-- > enumFileCounts path = joinE (enumFile path) (EL.map T.length)+-- > enumFileCounts path = enumFile path $= EL.map T.length+--+-- Since: 0.4.9+:++:d apidoc Data.Enumerator.(=$)+-- | @enum =$ iter = 'joinI' (enum $$ iter)+--+-- “Wraps” an iteratee /inner/ in an enumeratee /wrapper/.+-- The resulting iteratee will consume /wrapper/’s input type and+-- yield /inner/’s output type.+--+-- Note: if the inner iteratee yields leftover input when it finishes,+-- that extra will be discarded.+--+-- As an example, consider an iteratee that converts a stream of UTF8-encoded+-- bytes into a single 'TL.Text':+--+-- > consumeUTF8 :: Monad m => Iteratee ByteString m Text+--+-- It could be written with either 'joinI' or '(=$)':+--+-- > import Data.Enumerator.Text as ET+-- >+-- > consumeUTF8 = joinI (decode utf8 $$ ET.consume)+-- > consumeUTF8 = decode utf8 =$ ET.consume+--+-- Since: 0.4.9+:+ :d apidoc Data.Enumerator.(<==<) -- | @'(\<==\<)' = flip '(>==>)'@ --@@ -199,6 +247,37 @@ -- Since: 0.1.1 : +:d apidoc Data.Enumerator.checkContinue0+-- | A common pattern in 'Enumerator' implementations is to check whether+-- the inner 'Iteratee' has finished, and if so, to return its output.+-- 'checkContinue0' passes its parameter a continuation if the 'Iteratee'+-- can still consume input; if not, it returns the iteratee's step.+--+-- The type signature here is a bit crazy, but it's actually very easy to+-- use. Take this code:+--+-- > repeat :: Monad m => a -> Enumerator a m b+-- > repeat x = loop where+-- > loop (Continue k) = k (Chunks [x]) >>== loop+-- > loop step = returnI step+--+-- And rewrite it without the boilerplate:+--+-- > repeat :: Monad m => a -> Enumerator a m b+-- > repeat x = checkContinue0 $ \loop k -> k (Chunks [x] >>== loop+--+-- Since: 0.4.9+:++:d apidoc Data.Enumerator.checkContinue1+-- | Like 'checkContinue0', but allows each loop step to use a state value:+--+-- > iterate :: Monad m => (a -> a) -> a -> Enumerator a m b+-- > iterate f = checkContinue1 $ \loop a k -> k (Chunks [a]) >>== loop (f a)+--+-- Since: 0.4.9+:+ :d apidoc Data.Enumerator.checkDone -- | @'checkDone' = 'checkDoneEx' ('Chunks' [])@ --@@ -628,6 +707,18 @@ -- Since: 0.4.8 : +:d apidoc Data.Enumerator.Binary.mapAccum+-- | Similar to 'map', but with a stateful step function.+--+-- Since: 0.4.9+:++:d apidoc Data.Enumerator.Binary.mapAccumM+-- | Similar to 'mapM', but with a stateful step function.+--+-- Since: 0.4.9+:+ :d apidoc Data.Enumerator.Binary.repeat -- | Enumerates an infinite stream of a single byte. --@@ -824,6 +915,18 @@ -- Since: 0.4.8 : +:d apidoc Data.Enumerator.List.mapAccum+-- | Similar to 'map', but with a stateful step function.+--+-- Since: 0.4.9+:++:d apidoc Data.Enumerator.List.mapAccumM+-- | Similar to 'mapM', but with a stateful step function.+--+-- Since: 0.4.9+:+ :d apidoc Data.Enumerator.List.repeat -- | Enumerates an infinite stream of a single element. --@@ -1063,6 +1166,18 @@ -- Since: 0.4.8 : +:d apidoc Data.Enumerator.Text.mapAccum+-- | Similar to 'map', but with a stateful step function.+--+-- Since: 0.4.9+:++:d apidoc Data.Enumerator.Text.mapAccumM+-- | Similar to 'mapM', but with a stateful step function.+--+-- Since: 0.4.9+:+ :d apidoc Data.Enumerator.Text.repeat -- | Enumerates an infinite stream of a single character. --@@ -1134,4 +1249,11 @@ -- Similar to 'iterateM'. -- -- Since: 0.4.8+:++:d apidoc Data.Enumerator.tryIO+-- | Try to run an IO computation. If it throws an exception, the exception+-- is caught and converted into an {\tt Error}.+--+-- Since: 0.4.9 :
src/enumerator.anansi view
@@ -30,10 +30,10 @@ \newcommand{\io}{{\sc i/o}} -\title{enumerator\_0.4.8}+\title{enumerator\_0.4.9} \author{John Millikin\\ \href{mailto:"John Millikin" <jmillikin@gmail.com>}{\tt jmillikin@gmail.com}}-\date{March 19, 2011}+\date{March 29, 2011} \begin{document}
src/io.anansi view
@@ -15,16 +15,13 @@ => Integer -- ^ Buffer size -> IO.Handle -> Enumerator B.ByteString m b-enumHandle bufferSize h = do+enumHandle bufferSize h = checkContinue0 $ \loop k -> do let intSize = fromInteger bufferSize - fix $ \loop step -> case step of- Continue k -> do- bytes <- tryIO (getBytes h intSize)- if B.null bytes- then continue k- else k (Chunks [bytes]) >>== loop- _ -> returnI step+ bytes <- tryIO (getBytes h intSize)+ if B.null bytes+ then continue k+ else k (Chunks [bytes]) >>== loop : :d binary IO@@ -41,19 +38,20 @@ Just off -> tryIO (IO.hSeek h IO.AbsoluteSeek off) enum = case count of- Just n -> loop n s+ Just n -> enumRange n s Nothing -> enumHandle bufferSize h s - loop n (Continue k) =- let rem = fromInteger (min bufferSize n) in- if rem <= 0+ enumRange = checkContinue1 $ \loop n k -> let+ rem = fromInteger (min bufferSize n)+ keepGoing = do+ bytes <- tryIO (getBytes h rem)+ if B.null bytes+ then continue k+ else feed bytes+ feed bs = k (Chunks [bs]) >>== loop (n - (toInteger (B.length bs)))+ in if rem <= 0 then continue k- else do- bytes <- tryIO (getBytes h rem)- if B.null bytes- then continue k- else k (Chunks [bytes]) >>== loop (n - (toInteger (B.length bytes)))- loop _ step = returnI step+ else keepGoing : :d binary IO@@ -110,19 +108,18 @@ |apidoc Data.Enumerator.Text.enumHandle| enumHandle :: MonadIO m => IO.Handle -> Enumerator T.Text m b-enumHandle h = loop where- loop (Continue k) = do- maybeText <- tryIO getText- case maybeText of- Nothing -> continue k- Just text -> k (Chunks [text]) >>== loop- - loop step = returnI step- getText = Exc.catch+enumHandle h = checkContinue0 $ \loop k -> do+ let getText = Exc.catch (Just `fmap` TIO.hGetLine h) (\err -> if isEOFError err then return Nothing else Exc.throwIO err)+ + maybeText <- tryIO getText+ case maybeText of+ Nothing -> continue k+ Just text -> k (Chunks [text]) >>== loop+ : :d text IO
src/list-analogues.anansi view
@@ -65,67 +65,55 @@ :d element-oriented list analogues |apidoc Data.Enumerator.List.unfold| unfold :: Monad m => (s -> Maybe (a, s)) -> s -> Enumerator a m b-unfold f = loop where- loop s (Continue k) = case f s of- Nothing -> continue k- Just (a, s') -> k (Chunks [a]) >>== loop s'- loop _ step = returnI step+unfold f = checkContinue1 $ \loop s k -> case f s of+ Nothing -> continue k+ Just (a, s') -> k (Chunks [a]) >>== loop s' : :d byte-oriented list analogues |apidoc Data.Enumerator.Binary.unfold| unfold :: Monad m => (s -> Maybe (Word8, s)) -> s -> Enumerator B.ByteString m b-unfold f = loop where- loop s (Continue k) = case f s of- Nothing -> continue k- Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s'- loop _ step = returnI step+unfold f = checkContinue1 $ \loop s k -> case f s of+ Nothing -> continue k+ Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s' : :d text-oriented list analogues |apidoc Data.Enumerator.Text.unfold| unfold :: Monad m => (s -> Maybe (Char, s)) -> s -> Enumerator T.Text m b-unfold f = loop where- loop s (Continue k) = case f s of- Nothing -> continue k- Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s'- loop _ step = returnI step+unfold f = checkContinue1 $ \loop s k -> case f s of+ Nothing -> continue k+ Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s' : :d element-oriented list analogues |apidoc Data.Enumerator.List.unfoldM| unfoldM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Enumerator a m b-unfoldM f = loop where- loop s (Continue k) = do- fs <- lift (f s)- case fs of- Nothing -> continue k- Just (a, s') -> k (Chunks [a]) >>== loop s'- loop _ step = returnI step+unfoldM f = checkContinue1 $ \loop s k -> do+ fs <- lift (f s)+ case fs of+ Nothing -> continue k+ Just (a, s') -> k (Chunks [a]) >>== loop s' : :d byte-oriented list analogues |apidoc Data.Enumerator.Binary.unfoldM| unfoldM :: Monad m => (s -> m (Maybe (Word8, s))) -> s -> Enumerator B.ByteString m b-unfoldM f = loop where- loop s (Continue k) = do- fs <- lift (f s)- case fs of- Nothing -> continue k- Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s'- loop _ step = returnI step+unfoldM f = checkContinue1 $ \loop s k -> do+ fs <- lift (f s)+ case fs of+ Nothing -> continue k+ Just (b, s') -> k (Chunks [B.singleton b]) >>== loop s' : :d text-oriented list analogues |apidoc Data.Enumerator.Text.unfoldM| unfoldM :: Monad m => (s -> m (Maybe (Char, s))) -> s -> Enumerator T.Text m b-unfoldM f = loop where- loop s (Continue k) = do- fs <- lift (f s)- case fs of- Nothing -> continue k- Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s'- loop _ step = returnI step+unfoldM f = checkContinue1 $ \loop s k -> do+ fs <- lift (f s)+ case fs of+ Nothing -> continue k+ Just (c, s') -> k (Chunks [T.singleton c]) >>== loop s' : \subsection{Maps}@@ -226,6 +214,91 @@ checkDoneEx (Chunks [T.pack xs]) (\k' -> loop k' xs) : +\subsection{Accumulating maps}++:d element-oriented list analogues+|apidoc Data.Enumerator.List.mapAccum|+mapAccum :: Monad m => (s -> ao -> (s, ai)) -> s -> Enumeratee ao ai m b+mapAccum f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case f s x of+ (s', ai) -> k (Chunks [ai]) >>==+ checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)++|apidoc Data.Enumerator.List.mapAccumM|+mapAccumM :: Monad m => (s -> ao -> m (s, ai)) -> s -> Enumeratee ao ai m b+mapAccumM f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = do+ (s', ai) <- lift (f s x)+ k (Chunks [ai]) >>==+ checkDoneEx (Chunks xs) (\k' -> loop s' k' xs)+:++:d byte-oriented list analogues+|apidoc Data.Enumerator.Binary.mapAccum|+mapAccum :: Monad m => (s -> Word8 -> (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b+mapAccum f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case B.uncons x of+ Nothing -> loop s k xs+ Just (b, x') -> case f s b of+ (s', ai) -> k (Chunks [B.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))++|apidoc Data.Enumerator.Binary.mapAccumM|+mapAccumM :: Monad m => (s -> Word8 -> m (s, Word8)) -> s -> Enumeratee B.ByteString B.ByteString m b+mapAccumM f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case B.uncons x of+ Nothing -> loop s k xs+ Just (b, x') -> do+ (s', ai) <- lift (f s b)+ k (Chunks [B.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))+:++:d text-oriented list analogues+|apidoc Data.Enumerator.Text.mapAccum|+mapAccum :: Monad m => (s -> Char -> (s, Char)) -> s -> Enumeratee T.Text T.Text m b+mapAccum f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case T.uncons x of+ Nothing -> loop s k xs+ Just (c, x') -> case f s c of+ (s', ai) -> k (Chunks [T.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))++|apidoc Data.Enumerator.Text.mapAccumM|+mapAccumM :: Monad m => (s -> Char -> m (s, Char)) -> s -> Enumeratee T.Text T.Text m b+mapAccumM f s0 = checkDone (continue . step s0) where+ step _ k EOF = yield (Continue k) EOF+ step s k (Chunks xs) = loop s k xs+ + loop s k [] = continue (step s k)+ loop s k (x:xs) = case T.uncons x of+ Nothing -> loop s k xs+ Just (c, x') -> do+ (s', ai) <- lift (f s c)+ k (Chunks [T.singleton ai]) >>==+ checkDoneEx (Chunks (x':xs)) (\k' -> loop s' k' (x':xs))+:+ \subsection{Infinite streams} {\tt iterate} and {\tt iterateM} apply a function repeatedly to the base@@ -234,56 +307,47 @@ :d element-oriented list analogues |apidoc Data.Enumerator.List.iterate| iterate :: Monad m => (a -> a) -> a -> Enumerator a m b-iterate f = loop where- loop a (Continue k) = k (Chunks [a]) >>== loop (f a)- loop _ step = returnI step+iterate f = checkContinue1 $ \loop s k -> k (Chunks [s]) >>== loop (f s) : :d byte-oriented list analogues |apidoc Data.Enumerator.Binary.iterate| iterate :: Monad m => (Word8 -> Word8) -> Word8 -> Enumerator B.ByteString m b-iterate f = loop where- loop byte (Continue k) = k (Chunks [B.singleton byte]) >>== loop (f byte)- loop _ step = returnI step+iterate f = checkContinue1 $ \loop s k -> k (Chunks [B.singleton s]) >>== loop (f s) : :d text-oriented list analogues |apidoc Data.Enumerator.Text.iterate| iterate :: Monad m => (Char -> Char) -> Char -> Enumerator T.Text m b-iterate f = loop where- loop char (Continue k) = k (Chunks [T.singleton char]) >>== loop (f char)- loop _ step = returnI step+iterate f = checkContinue1 $ \loop s k -> k (Chunks [T.singleton s]) >>== loop (f s) : :d element-oriented list analogues |apidoc Data.Enumerator.List.iterateM| iterateM :: Monad m => (a -> m a) -> a -> Enumerator a m b-iterateM f base = loop (return base) where- loop m_a (Continue k) = do+iterateM f base = worker (return base) where+ worker = checkContinue1 $ \loop m_a k -> do a <- lift m_a k (Chunks [a]) >>== loop (f a)- loop _ step = returnI step : :d byte-oriented list analogues |apidoc Data.Enumerator.Binary.iterateM| iterateM :: Monad m => (Word8 -> m Word8) -> Word8 -> Enumerator B.ByteString m b-iterateM f base = loop (return base) where- loop m_byte (Continue k) = do+iterateM f base = worker (return base) where+ worker = checkContinue1 $ \loop m_byte k -> do byte <- lift m_byte k (Chunks [B.singleton byte]) >>== loop (f byte)- loop _ step = returnI step : :d text-oriented list analogues |apidoc Data.Enumerator.Text.iterateM| iterateM :: Monad m => (Char -> m Char) -> Char -> Enumerator T.Text m b-iterateM f base = loop (return base) where- loop m_char (Continue k) = do+iterateM f base = worker (return base) where+ worker = checkContinue1 $ \loop m_char k -> do char <- lift m_char k (Chunks [T.singleton char]) >>== loop (f char)- loop _ step = returnI step : {\tt repeat} and {\tt repeatM} create infinite streams, where each input@@ -292,7 +356,7 @@ :d element-oriented list analogues |apidoc Data.Enumerator.List.repeat| repeat :: Monad m => a -> Enumerator a m b-repeat a = Data.Enumerator.List.iterate (const a) a+repeat a = checkContinue0 $ \loop k -> k (Chunks [a]) >>== loop : :d element-oriented list analogues@@ -378,13 +442,11 @@ |apidoc Data.Enumerator.List.generateM| generateM :: Monad m => m (Maybe a) -> Enumerator a m b-generateM getNext = loop where- loop (Continue k) = do- next <- lift getNext- case next of- Nothing -> continue k- Just x -> k (Chunks [x]) >>== loop- loop step = returnI step+generateM getNext = checkContinue0 $ \loop k -> do+ next <- lift getNext+ case next of+ Nothing -> continue k+ Just x -> k (Chunks [x]) >>== loop : :d byte-oriented list analogues
src/public-interface.anansi view
@@ -70,11 +70,9 @@ import Data.Enumerator hiding ( head, drop, iterateM, repeatM, replicateM , generateM, filterM, consume, foldM , concatMapM)-import Data.Enumerator.Util (tryIO) import Control.Monad.IO.Class (MonadIO) import qualified Data.ByteString as B import qualified System.IO as IO-import Data.Function (fix) import qualified Control.Exception as Exc import System.IO.Error (isEOFError) import Data.Word (Word8)@@ -101,7 +99,7 @@ import Data.Enumerator hiding ( head, drop, generateM, filterM, consume , concatMapM, iterateM, repeatM, replicateM , foldM)-import Data.Enumerator.Util (tryIO, tSpanBy, tlSpanBy, reprWord, reprChar, textToStrict)+import Data.Enumerator.Util (tSpanBy, tlSpanBy, reprWord, reprChar, textToStrict) import Control.Monad.IO.Class (MonadIO) import qualified Control.Exception as Exc import Control.Arrow (first)@@ -143,6 +141,8 @@ , ($$) , (>==>) , (<==<)+, (=$)+, ($=) -- ** Running iteratees , run@@ -158,9 +158,12 @@ , joinE , Data.Enumerator.sequence , enumEOF+, checkContinue0+, checkContinue1 , checkDoneEx , checkDone , isEOF+, tryIO -- ** Testing and debugging , printChunks@@ -223,6 +226,10 @@ , Data.Enumerator.Binary.concatMap , concatMapM +-- ** Accumulating maps+, mapAccum+, mapAccumM+ -- ** Infinite streams , Data.Enumerator.Binary.iterate , iterateM@@ -268,6 +275,10 @@ , Data.Enumerator.List.concatMap , concatMapM +-- ** Accumulating maps+, mapAccum+, mapAccumM+ -- ** Infinite streams , Data.Enumerator.List.iterate , iterateM@@ -316,6 +327,10 @@ , Data.Enumerator.Text.mapM , Data.Enumerator.Text.concatMap , concatMapM++-- ** Accumulating maps+, mapAccum+, mapAccumM -- ** Infinite streams , Data.Enumerator.Text.iterate
src/utilities.anansi view
@@ -29,6 +29,14 @@ check (Error e) = throwError e : +:d unsorted utilities+infixr 0 =$++|apidoc Data.Enumerator.(=$)|+(=$) :: Monad m => Enumeratee ao ai m b -> Iteratee ai m b -> Iteratee ao m b+enum =$ iter = joinI (enum $$ iter)+:+ {\tt joinE} is similar, except it flattens an enumerator/enumeratee pair into a single enumerator. @@ -46,6 +54,17 @@ Continue _ -> error "joinE: divergent iteratee" : +:d unsorted utilities+infixr 0 $=++|apidoc Data.Enumerator.($=)|+($=) :: Monad m+ => Enumerator ao m (Step ai m b)+ -> Enumeratee ao ai m b+ -> Enumerator ai m b+($=) = joinE+:+ {\tt sequence} repeatedly runs its parameter to transform the stream. :d unsorted utilities@@ -104,31 +123,65 @@ _ -> yield False s : +When an enumerator has to interact with the outside world, it usually+catches any exceptions that arise, and propagate them as {\tt Error} steps+instead. {\tt tryIO} encapsulates that pattern.++:d unsorted utilities+|apidoc Data.Enumerator.tryIO|+tryIO :: MonadIO m => IO b -> Iteratee a m b+tryIO io = Iteratee $ do+ tried <- liftIO (Exc.try io)+ return $ case tried of+ Right b -> Yield b (Chunks [])+ Left err -> Error err+:++Another enumerator pattern that pops up often is a loop that ignores any+non-{\tt Continue} steps. This is especially useful when implementing+most enumerators. It's sort of an analogue to {\tt checkDone}, so I+called it {\tt checkContinue}. It's actually implemented by various+functions ({\tt checkContinue0}, {\tt checkContinue1}, etc), as most+enumerators have some sort of state to pass around.++:d unsorted utilities+|apidoc Data.Enumerator.checkContinue0|+checkContinue0 :: Monad m+ => (Enumerator a m b+ -> (Stream a -> Iteratee a m b)+ -> Iteratee a m b)+ -> Enumerator a m b+checkContinue0 inner = loop where+ loop (Continue k) = inner loop k+ loop step = returnI step+:++:d unsorted utilities+|apidoc Data.Enumerator.checkContinue1|+checkContinue1 :: Monad m+ => ((s1 -> Enumerator a m b)+ -> s1+ -> (Stream a -> Iteratee a m b)+ -> Iteratee a m b)+ -> s1+ -> Enumerator a m b+checkContinue1 inner = loop where+ loop s (Continue k) = inner loop s k+ loop _ step = returnI step+:+ {\tt Data.Enumerator.Util} is a hidden module for functions used by several public modules, but not logically part of the {\tt enumerator} API. :f Data/Enumerator/Util.hs {-# LANGUAGE CPP #-} module Data.Enumerator.Util where-import Data.Enumerator import Data.Char (toUpper, intToDigit, ord) import Data.Word (Word8) import qualified Data.Text as T import qualified Data.Text.Lazy as TL--import Control.Monad.IO.Class (MonadIO, liftIO)-import qualified Control.Exception as Exc import Numeric (showIntAtBase)-:--:f Data/Enumerator/Util.hs-tryIO :: MonadIO m => IO b -> Iteratee a m b-tryIO io = Iteratee $ do- tried <- liftIO (Exc.try io)- return $ case tried of- Right b -> Yield b (Chunks [])- Left err -> Error err : :f Data/Enumerator/Util.hs
tests/Properties.hs view
@@ -34,7 +34,6 @@ tests :: [F.Test] tests = [ test_StreamInstances- , test_Primitives , test_Text , test_ListAnalogues , test_Other@@ -132,38 +131,6 @@ -- }}} --- Primitives {{{--test_Primitives :: F.Test-test_Primitives = F.testGroup "Primitives"- [ test_Map- , test_ConcatMap- , test_MapM- , test_ConcatMapM- , test_Filter- , test_FilterM- ]--test_Map :: F.Test-test_Map = test_Enumeratee "map" (EL.map id)--test_ConcatMap :: F.Test-test_ConcatMap = test_Enumeratee "concatMap" (EL.concatMap (:[]))--test_MapM :: F.Test-test_MapM = test_Enumeratee "mapM" (EL.mapM return)--test_ConcatMapM :: F.Test-test_ConcatMapM = test_Enumeratee "concatMapM" (EL.concatMapM (\x -> return [x]))--test_Filter :: F.Test-test_Filter = test_Enumeratee "filter" (EL.filter (\_ -> True))--test_FilterM :: F.Test-test_FilterM = test_Enumeratee "filterM" (EL.filterM (\_ -> return True))---- }}}- -- Text encoding / decoding {{{ test_Text :: F.Test@@ -435,6 +402,14 @@ , test_Require , test_Isolate , test_SplitWhen+ , test_Map+ , test_ConcatMap+ , test_MapM+ , test_ConcatMapM+ , test_MapAccum+ , test_MapAccumM+ , test_Filter+ , test_FilterM ] check :: Eq b => E.Iteratee a Identity b -> ([a] -> Either Exc.ErrorCall b) -> [a] -> Bool@@ -616,6 +591,79 @@ split = LS.split . LS.dropFinalBlank . LS.dropDelims . LS.whenElt words = BL.unpack bytes in Right (map B.pack (split (== x) words), []))++test_Map :: F.Test+test_Map = test_Enumeratee "map" (EL.map id)++test_ConcatMap :: F.Test+test_ConcatMap = test_Enumeratee "concatMap" (EL.concatMap (:[]))++test_MapM :: F.Test+test_MapM = test_Enumeratee "mapM" (EL.mapM return)++test_ConcatMapM :: F.Test+test_ConcatMapM = test_Enumeratee "concatMapM" (EL.concatMapM (\x -> return [x]))++test_MapAccum :: F.Test+test_MapAccum = testListAnalogue "mapAccum"+ (do+ let enee = EL.mapAccum (\s ao -> (s+1, (s, ao))) 10+ a <- E.joinI (enee $$ EL.head)+ b <- EL.consume+ return (a, b))+ (\xs -> Right $ case xs of+ [] -> (Nothing, [])+ (x:xs') -> (Just (10, x), xs'))+ (do+ let enee = ET.mapAccum (\s ao -> (s+1, succ ao)) 10+ a <- E.joinI (enee $$ EL.head)+ b <- ET.consume+ return (a, b))+ (\text -> Right $ case TL.uncons text of+ Nothing -> (Nothing, TL.empty)+ Just (c, text') -> (Just (T.singleton (succ c)), text'))+ (do+ let enee = EB.mapAccum (\s ao -> (s+1, ao + s)) 10+ a <- E.joinI (enee $$ EL.head)+ b <- EB.consume+ return (a, b))+ (\bytes -> Right $ case BL.uncons bytes of+ Nothing -> (Nothing, BL.empty)+ Just (b, bytes') -> (Just (B.singleton (b + 10)), bytes'))+++test_MapAccumM :: F.Test+test_MapAccumM = testListAnalogue "mapAccumM"+ (do+ let enee = EL.mapAccumM (\s ao -> return (s+1, (s, ao))) 10+ a <- E.joinI (enee $$ EL.head)+ b <- EL.consume+ return (a, b))+ (\xs -> Right $ case xs of+ [] -> (Nothing, [])+ (x:xs') -> (Just (10, x), xs'))+ (do+ let enee = ET.mapAccumM (\s ao -> return (s+1, succ ao)) 10+ a <- E.joinI (enee $$ EL.head)+ b <- ET.consume+ return (a, b))+ (\text -> Right $ case TL.uncons text of+ Nothing -> (Nothing, TL.empty)+ Just (c, text') -> (Just (T.singleton (succ c)), text'))+ (do+ let enee = EB.mapAccumM (\s ao -> return (s+1, ao + s)) 10+ a <- E.joinI (enee $$ EL.head)+ b <- EB.consume+ return (a, b))+ (\bytes -> Right $ case BL.uncons bytes of+ Nothing -> (Nothing, BL.empty)+ Just (b, bytes') -> (Just (B.singleton (b + 10)), bytes'))++test_Filter :: F.Test+test_Filter = test_Enumeratee "filter" (EL.filter (\_ -> True))++test_FilterM :: F.Test+test_FilterM = test_Enumeratee "filterM" (EL.filterM (\_ -> return True)) -- }}}