enumerator 0.1 → 0.1.1
raw patch · 9 files changed
+184/−28 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Enumerator: ($$) :: (Monad m) => (Step e a m b -> Iteratee e a' m b') -> Iteratee e a m b -> Iteratee e a' m b'
+ Data.Enumerator: (<==<) :: (Monad m) => (Step e a m b -> Iteratee e a' m b') -> Enumerator e a m b -> Step e a m b -> Iteratee e a' m b'
+ Data.Enumerator: (>==>) :: (Monad m) => Enumerator e a m b -> (Step e a m b -> Iteratee e a' m b') -> Step e a m b -> Iteratee e a' m b'
+ Data.Enumerator: catchError :: (Monad m) => Iteratee e a m b -> (e -> Iteratee e a m b) -> Iteratee e a m b
+ Data.Enumerator: liftFoldL :: (Monad m) => (b -> a -> b) -> b -> Iteratee e a m b
+ Data.Enumerator: liftFoldL' :: (Monad m) => (b -> a -> b) -> b -> Iteratee e a m b
+ Data.Enumerator: liftFoldM :: (Monad m) => (b -> a -> m b) -> b -> Iteratee e a m b
+ Data.Enumerator: liftTrans :: (Monad m, MonadTrans t, Monad (t m)) => Iteratee e a m b -> Iteratee e a (t m) b
Files
- Examples/cat.hs +3/−5
- Examples/enumerator-wcl.hs +22/−0
- Examples/iteratee-wcl.hs +24/−0
- Examples/lazy-bytestring-wcl.hs +16/−0
- Examples/strict-bytestring-wcl.hs +21/−0
- Examples/wc.hs +8/−7
- enumerator.cabal +1/−1
- hs/Data/Enumerator.hs +81/−5
- hs/Data/Enumerator/IO.hs +8/−10
Examples/cat.hs view
@@ -105,11 +105,9 @@ then enumHandle 1 stdin else concatEnums (Prelude.map enumFile args) - -- This line looks a bit weird, because the (>>==) causes a visual- -- "flow" from the iteratee to the enumerator. However, data is- -- actually being sent from the enumerator to the iteratee. It's- -- better to think of it in terms of continuation passing.- res <- run (iterHandle stdout >>== enum)+ -- 'run' sends an EOF to an iteratee and returns its output, which+ -- is either a 'Yield' or an 'Error'.+ res <- run (enum $$ iterHandle stdout) -- Finally, 'run' has returned either an error or the iteratee's -- result. 'iterHandle' doesn't return a useful result, so as long
+ Examples/enumerator-wcl.hs view
@@ -0,0 +1,22 @@+module Main (main) where+import Data.Enumerator+import Data.Enumerator.IO+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import System.IO+import System.Environment++iterLines :: Monad m => Iteratee e B.ByteString m Integer+iterLines = continue (step 0) where+ step acc EOF = yield acc EOF+ step acc (Chunks xs) = continue $ step $! foldl foldStep acc xs+ foldStep acc bytes = acc + countChar '\n' bytes++countChar :: Char -> B.ByteString -> Integer+countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0++main :: IO ()+main = do+ filename:_ <- getArgs+ h <- openBinaryFile filename ReadMode+ run (iterLines >>== enumHandle 4096 h) >>= print
+ Examples/iteratee-wcl.hs view
@@ -0,0 +1,24 @@+module Main (main) where+import Data.Iteratee+import Data.Iteratee.IO+import Data.Iteratee.WrappedByteString+import Data.Word (Word8)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import System.IO+import System.Environment++iterLines :: Monad m => IterateeG WrappedByteString Word8 m Integer+iterLines = IterateeG (step 0) where+ step acc s@(EOF _) = return $ Done acc s+ step acc (Chunk wrapped) = return $ Cont (IterateeG (step $! acc')) Nothing where+ acc' = acc + countChar '\n' (unWrap wrapped)++countChar :: Char -> B.ByteString -> Integer+countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0++main :: IO ()+main = do+ filename:_ <- getArgs+ h <- openBinaryFile filename ReadMode+ enumHandle h iterLines >>= run >>= print
+ Examples/lazy-bytestring-wcl.hs view
@@ -0,0 +1,16 @@+import qualified Data.ByteString.Lazy as B+import qualified Data.ByteString.Lazy.Char8 as B8+import System.IO+import System.Environment++countChar :: Char -> B.ByteString -> Integer+countChar c = B8.foldl' (\acc c' -> if c' == c then acc + 1 else acc) 0++countHandle :: Handle -> IO Integer+countHandle h = fmap (countChar '\n') (B.hGetContents h)++main :: IO ()+main = do+ filename:_ <- getArgs+ h <- openBinaryFile filename ReadMode+ countHandle h >>= print
+ Examples/strict-bytestring-wcl.hs view
@@ -0,0 +1,21 @@+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8+import System.IO+import System.Environment++countChar :: Char -> B.ByteString -> Integer+countChar c = B8.foldl' (\acc c' -> if c' == c then acc + 1 else acc) 0++countHandle :: Handle -> IO Integer+countHandle h = loop 0 where+ loop acc = do+ bytes <- B.hGet h 4096+ if B.null bytes+ then return acc+ else let acc' = acc + countChar '\n' bytes in loop $! acc'++main :: IO ()+main = do+ filename:_ <- getArgs+ h <- openBinaryFile filename ReadMode+ countHandle h >>= print
Examples/wc.hs view
@@ -34,16 +34,17 @@ iterBytes :: Monad m => Iteratee e B.ByteString m Integer iterBytes = continue (step 0) where step acc EOF = yield acc EOF- step acc (Chunks xs) = continue $ step (foldl' foldStep acc xs)+ step acc (Chunks xs) = continue $ step $! foldl' foldStep acc xs foldStep acc bytes = acc + toInteger (B.length bytes) -- iterLines is similar, except it only counts newlines ('\n')+--+-- Because it's basically the same as 'iterBytes', we use it to demonstrate+-- the 'liftFoldL\'' helper function. iterLines :: Monad m => Iteratee e B.ByteString m Integer-iterLines = continue (step 0) where- step acc EOF = yield acc EOF- step acc (Chunks xs) = continue $ step (foldl' foldStep acc xs)- foldStep acc bytes = acc + countChar '\n' bytes+iterLines = liftFoldL' step 0 where+ step acc bytes = acc + countChar '\n' bytes countChar c = B8.foldl (\acc c' -> if c' == c then acc + 1 else acc) 0 -- iterChars is a bit more complicated. It has to decode the input (for now,@@ -58,7 +59,7 @@ step accT chunk = case chunk of EOF -> let (extra, acc) = accT in yield acc (Chunks [extra]) (Chunks xs) -> case foldl' foldStep (Just accT) xs of- Just accT' -> continue $ step accT'+ Just accT' -> continue $ step $! accT' Nothing -> throwError (E.SomeException (E.ErrorCall "Invalid UTF-8")) -- The 'decodeUtf8' function is complicated, and defined later, but@@ -87,7 +88,7 @@ putStr $ filename ++ ": " -- see cat.hs for commented implementation of 'Data.Enumerator.IO.enumFile'- eitherStat <- run (iter >>== enumFile filename)+ eitherStat <- run (enumFile filename $$ iter) putStrLn $ case eitherStat of Left err -> "ERROR: " ++ show err Right stat -> show stat
enumerator.cabal view
@@ -1,5 +1,5 @@ name: enumerator-version: 0.1+version: 0.1.1 synopsis: Implementation of Oleg Kiselyov's left-fold enumerators license: MIT license-file: license.txt
hs/Data/Enumerator.hs view
@@ -25,12 +25,20 @@ , yield , continue , throwError+ , catchError , liftI , (>>==) , (==<<)+ , ($$)+ , (>==>)+ , (<==<) -- ** Iteratees , consume , isEOF+ , liftTrans+ , liftFoldL+ , liftFoldL'+ , liftFoldM -- ** Enumerators , enumEOF , enumList@@ -63,6 +71,8 @@ import Control.Monad (liftM, ap) import qualified Control.Monad.IO.Class as MIO import qualified Control.Monad.Trans.Class as MT+import qualified Data.List as DataList+import Control.Monad (foldM) import qualified Control.Exception as E import Prelude hiding (span) import qualified Prelude as Prelude@@ -165,6 +175,16 @@ instance MIO.MonadIO m => MIO.MonadIO (Iteratee e a m) where liftIO = MT.lift . MIO.liftIO {-# INLINE liftIO #-}+-- | Lift an 'Iteratee' onto a monad transformer, re-wrapping the+-- 'Iteratee'’s inner monadic values.+liftTrans :: (Monad m, MT.MonadTrans t, Monad (t m)) =>+ Iteratee e a m b -> Iteratee e a (t m) b+liftTrans iter = Iteratee $ do+ step <- MT.lift $ runIteratee iter+ return $ case step of+ Yield x cs -> Yield x cs+ Error err -> Error err+ Continue k -> Continue (liftTrans . k) -- | @returnI x = Iteratee (return x)@ returnI :: Monad m => Step e a m b -> Iteratee e a m b returnI = Iteratee . return@@ -189,6 +209,12 @@ liftI :: Monad m => (Stream a -> Step e a m b) -> Iteratee e a m b liftI k = continue $ returnI . k {-# INLINE liftI #-}+catchError :: Monad m => Iteratee e a m b -> (e -> Iteratee e a m b) -> Iteratee e a m b+catchError iter h = Iteratee $ do+ step <- runIteratee iter+ case step of+ Error err -> runIteratee (h err)+ _ -> return step -- | Equivalent to (>>=), but allows 'Iteratee's with different input types -- to be composed. (>>==) :: Monad m =>@@ -205,18 +231,68 @@ Iteratee e a' m b' (==<<) = flip (>>==) {-# INLINE (==<<) #-}++-- | @($$) = (==\<\<)@+--+-- This might be easier to read when passing a chain of iteratees to an+-- enumerator.+($$):: Monad m =>+ (Step e a m b -> Iteratee e a' m b') ->+ Iteratee e a m b ->+ Iteratee e a' m b'+($$) = (==<<)+{-# INLINE ($$) #-}+-- | @(>==>) e1 e2 s = e1 s >>== e2@+(>==>) :: Monad m =>+ Enumerator e a m b ->+ (Step e a m b -> Iteratee e a' m b') ->+ Step e a m b ->+ Iteratee e a' m b'+(>==>) e1 e2 s = e1 s >>== e2+{-# INLINE (>==>) #-}++-- | @(\<==\<) = flip (>==>)@+(<==<) :: Monad m =>+ (Step e a m b -> Iteratee e a' m b') ->+ Enumerator e a m b ->+ Step e a m b ->+ Iteratee e a' m b'+(<==<) = flip (>==>)+{-# INLINE (<==<) #-} -- | Consume all input until 'EOF', then return consumed input as a list. consume :: Monad m => Iteratee e a m [a]-consume = liftI $ step [] where+consume = liftI $ step id where step acc chunk = case chunk of Chunks [] -> Continue $ returnI . step acc- Chunks xs -> Continue $ returnI . (step $ acc ++ xs)- EOF -> Yield acc EOF+ Chunks xs -> Continue $ returnI . (step $ acc . (xs ++))+ EOF -> Yield (acc []) EOF -- | Return 'True' if the next 'Stream' is 'EOF'. isEOF :: Monad m => Iteratee e a m Bool isEOF = liftI $ \c -> case c of EOF -> Yield True c _ -> Yield False c+-- | Lifts a pure left fold into an iteratee.+liftFoldL :: Monad m => (b -> a -> b) -> b -> Iteratee e a m b+liftFoldL f = liftI . step where+ step acc chunk = case chunk of+ Chunks [] -> Continue $ returnI . step acc+ Chunks xs -> Continue $ returnI . step (Prelude.foldl f acc xs)+ EOF -> Yield acc EOF+-- | As 'liftFoldL', but strict in its accumulator.+liftFoldL' :: Monad m => (b -> a -> b) -> b -> Iteratee e a m b+liftFoldL' f = liftI . step where+ fold = DataList.foldl' f+ step acc chunk = case chunk of+ Chunks [] -> Continue $ returnI . step acc+ Chunks xs -> Continue $ returnI . (step $! fold acc xs)+ EOF -> Yield acc EOF+-- | Lifts a monadic left fold into an iteratee.+liftFoldM :: Monad m => (b -> a -> m b) -> b -> Iteratee e a m b+liftFoldM f = continue . step where+ step acc chunk = case chunk of+ Chunks [] -> continue $ step acc+ Chunks xs -> Iteratee $ liftM (Continue . step) (foldM f acc xs)+ EOF -> yield acc EOF -- | The most primitive enumerator; simply sends 'EOF'. The iteratee must -- either yield a value or throw an error continuing receiving 'EOF' will -- not terminate with any useful value.@@ -237,8 +313,8 @@ enumList _ _ step = returnI step -- | Compose a list of 'Enumerator's using '(>>==)' concatEnums :: Monad m => [Enumerator e a m b] -> Enumerator e a m b-concatEnums [] s = returnI s-concatEnums (e:es) s = e s >>== concatEnums es+concatEnums = foldl (>==>) returnI+{-# INLINE concatEnums #-} -- | 'joinI' is used to “flatten” 'Enumeratee's into an -- 'Iteratee'. joinI :: Monad m => Iteratee e a m (Step e a' m b) -> Iteratee e a m b
hs/Data/Enumerator/IO.hs view
@@ -20,7 +20,6 @@ import Control.Monad.IO.Class (liftIO) import qualified Control.Exception as E import qualified Data.ByteString as B-import qualified Foreign as F import qualified System.IO as IO -- | Read bytes (in chunks of the given buffer size) from the handle, and -- stream them to an 'Iteratee'. If an exception occurs during file IO,@@ -29,19 +28,18 @@ enumHandle :: Integer -- ^ Buffer size -> IO.Handle -> Enumerator E.SomeException B.ByteString IO b-enumHandle bufferSize h = Iteratee . F.allocaBytes size' . loop where+enumHandle bufferSize h = Iteratee . loop where size' = fromInteger bufferSize loop (Continue k) = read' k- loop step = const $ return step- read' k p = do- eitherN <- E.try $ IO.hGetBuf h p size'- case eitherN of+ loop step = return step+ read' k = do+ eitherB <- E.try $ B.hGet h size'+ case eitherB of Left err -> return $ Error err- Right 0 -> return $ Continue k- Right n -> do- bytes <- B.packCStringLen (p, n)+ Right bytes | B.null bytes -> return $ Continue k+ Right bytes -> do step <- runIteratee (k (Chunks [bytes]))- loop step p+ loop step -- | Opens a file path in binary mode, and passes the handle to 'enumHandle'. -- The file will be closed when the 'Iteratee' finishes. enumFile :: FilePath -> Enumerator E.SomeException B.ByteString IO b