packages feed

iteratee 0.8.6.1 → 0.8.6.2

raw patch · 3 files changed

+70/−27 lines, 3 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

iteratee.cabal view
@@ -1,5 +1,5 @@ name:          iteratee-version:       0.8.6.1+version:       0.8.6.2 synopsis:      Iteratee-based I/O description:   The Iteratee monad provides strict, safe, and functional I/O. In addition
src/Data/Iteratee/ListLike.hs view
@@ -506,27 +506,71 @@   :: (LL.ListLike s el, Monad m, Nullable s)   => (el -> el -> Bool)   -> Enumeratee s [s] m a-groupBy same iinit = liftI $ go iinit LL.empty-    where go icurr pfx (Chunk s) = case gsplit (pfx `LL.append` s) of-                                          (full, partial)-                                              | LL.null full -> liftI $ go icurr partial-                                              | otherwise -> do inext <- lift . enumPure1Chunk full $ icurr-                                                                liftI $ go inext partial-          go icurr pfx (EOF mex) -            | LL.null pfx = lift . enumChunk (EOF mex) $ icurr-            | otherwise = do inext <- lift . enumPure1Chunk (LL.singleton pfx) $ icurr-                             lift . enumChunk (EOF mex) $ inext-          gsplit ll | LL.null ll = (LL.empty, LL.empty)-                    | otherwise = let groups = llGroupBy same ll-                                      full = LL.init groups-                                      partial = LL.last groups-                                  in full `seq` partial `seq` (full, partial)-          llGroupBy eq l -- Copied from Data.ListLike, avoid spurious (Eq el) constraint-              | LL.null l = LL.empty-              | otherwise = LL.cons (LL.cons x ys) (llGroupBy eq zs)-              where (ys, zs) = LL.span (eq x) xs-                    x = LL.head l-                    xs = LL.tail l+groupBy same iinit = liftI $ go iinit (const True, id)+  where +    -- As in group, need to handle grouping efficiently when we're fed+    -- many small chunks.+    -- +    -- Move the accumulation of groups by chunks into an accumulator+    -- that runs through gsplit, which is pfx / partial here. When we+    -- get a chunk, use gsplit to retrieve any full chunks and get the+    -- carried accumulator.+    -- +    -- At the end, "finish" the accumulator and handle the last chunk,+    -- unless the stream was entirely empty and there is no+    -- accumulator.+    go icurr pfx (Chunk s) = case gsplit pfx s of+      ([], partial)   -> liftI $ go icurr partial+      (full, partial) -> do inext <- lift $ enumPure1Chunk full icurr+                            liftI $ go inext partial+    go icurr (_inpfx, pfxd) (EOF mex) = case pfxd [] of+      [] -> lift . enumChunk (EOF mex) $ icurr+      rest -> do inext <- lift . enumPure1Chunk [mconcat rest] $ icurr+                 lift . enumChunk (EOF mex) $ inext+    -- Here, gsplit carries an accumulator consisting of a predicate+    -- "inpfx" that indicates whether a new element belongs in the+    -- growing group, and a difference list to ultimately generate the+    -- group.+    --+    -- The initial accumulator is a group that can accept anything and+    -- is empty.+    -- +    -- New chunks are split into groups. The cases are ++    --   0. Trivially, empty chunk++    --   1. One chunk, in the currently growing group: continue the+    --   current prefix (and generate a new predicate, in case we had+    --   the initial predicate+    +    --   2. One chunk, but not in the current group: finish the+    --   current group and return a new accumulator for the+    --   newly-started gorup+    +    --   3. Multiple chunks, the first of which completes the+    --   currently growing group+    +    --   4. Multiple chunks, the first of which is a new group+    --   separate from the currently-growing group+    gsplit (inpfx, pfxd) curr = case llGroupBy same curr of+      [] -> ([], (inpfx, pfxd))+      [g0] | inpfx (LL.head g0) -> ([], (same $ LL.head g0, pfxd . (g0 :)))+           | otherwise          -> ([mconcat $ pfxd []], (same $ LL.head g0, pfxd . (g0 :)))+      (g0:grest@(_:_)) | inpfx (LL.head g0) -> let glast = Prelude.last grest+                                                   gfirst = mconcat $ (pfxd . (g0 :)) []+                                                   gdone = gfirst : Prelude.init grest+                                               in ( gdone, (same (LL.head glast), (glast :)) )+                       | otherwise -> let glast = Prelude.last grest+                                          gfirst = mconcat $ pfxd []+                                          gdone = gfirst : Prelude.init grest+                                      in ( gdone, (same (LL.head glast), (glast :)) )+    llGroupBy eq l -- Copied from Data.ListLike, avoid spurious (Eq el) constraint+      | LL.null l = []+      | otherwise = (LL.cons x ys):(llGroupBy eq zs)+        where (ys, zs) = LL.span (eq x) xs+              x = LL.head l+              xs = LL.tail l+ {-# INLINE groupBy #-}  -- | @merge@ offers another way to nest iteratees: as a monad stack.
tests/testIteratee.hs view
@@ -21,7 +21,6 @@ import           Control.Monad as CM import           Control.Monad.Writer - instance Show (a -> b) where   show _ = "<<function>>" @@ -319,11 +318,11 @@           where groupOne [] = Nothing                 groupOne elts@(_:_) = Just . splitAt n $ elts                            -prop_groupBy xs m = m > 0 ==>-                  runner2 (enumPure1Chunk xs $ Iter.groupBy pred stream2list)-                  == runner1 (enumPure1Chunk (List.groupBy pred xs) stream2list)+prop_groupBy xs = forAll (choose (2,5)) $ \m ->+  let pred z1 z2 = (z1 `mod` m == z2 `mod` m)+  in runner2 (enumPure1Chunk xs $ Iter.groupBy pred stream2list)+       == runner1 (enumPure1Chunk (List.groupBy pred xs) stream2list)   where types = xs :: [Int]-        pred z1 z2 = (z1 `mod` m == z2 `mod` m)  prop_mapM_ xs n = n > 0 ==>  runWriter ((enumSpecial xs n (Iter.mapM_ f)) >>= run)