conduit-combinators 0.2.8.3 → 0.3.0
raw patch · 9 files changed
+1473/−397 lines, 9 filesdep +QuickCheckdep +containersdep +directorydep ~basedep ~chunked-datadep ~conduitPVP ok
version bump matches the API change (PVP)
Dependencies added: QuickCheck, containers, directory, mtl, safe
Dependency ranges changed: base, chunked-data, conduit, conduit-extra, mono-traversable
API changes (from Hackage documentation)
+ Data.Conduit.Combinators: foldl1 :: Monad m => (a -> a -> a) -> Consumer a m (Maybe a)
+ Data.Conduit.Combinators: splitOnUnboundedE :: (Monad m, IsSequence seq) => (Element seq -> Bool) -> Conduit seq m seq
+ Data.Conduit.Combinators: takeExactlyUntilE :: (Monad m, IsSequence seq) => (Element seq -> Bool) -> ConduitM seq o m r -> ConduitM seq o m r
+ Data.Conduit.Combinators.Stream: allS :: Monad m => (a -> Bool) -> StreamConsumer a m Bool
+ Data.Conduit.Combinators.Stream: anyS :: Monad m => (a -> Bool) -> StreamConsumer a m Bool
+ Data.Conduit.Combinators.Stream: concatMapMS :: (Monad m, MonoFoldable mono) => (a -> m mono) -> StreamConduit a m (Element mono)
+ Data.Conduit.Combinators.Stream: concatMapS :: (Monad m, MonoFoldable mono) => (a -> mono) -> StreamConduit a m (Element mono)
+ Data.Conduit.Combinators.Stream: concatS :: (Monad m, MonoFoldable mono) => StreamConduit mono m (Element mono)
+ Data.Conduit.Combinators.Stream: filterMS :: Monad m => (a -> m Bool) -> StreamConduit a m a
+ Data.Conduit.Combinators.Stream: findS :: Monad m => (a -> Bool) -> StreamConsumer a m (Maybe a)
+ Data.Conduit.Combinators.Stream: foldl1S :: Monad m => (a -> a -> a) -> StreamConsumer a m (Maybe a)
+ Data.Conduit.Combinators.Stream: initRepeatS :: Monad m => m seed -> (seed -> m a) -> StreamProducer m a
+ Data.Conduit.Combinators.Stream: initReplicateS :: Monad m => m seed -> (seed -> m a) -> Int -> StreamProducer m a
+ Data.Conduit.Combinators.Stream: intersperseS :: Monad m => a -> StreamConduit a m a
+ Data.Conduit.Combinators.Stream: lastES :: (Monad m, IsSequence seq) => StreamConsumer seq m (Maybe (Element seq))
+ Data.Conduit.Combinators.Stream: lastS :: Monad m => StreamConsumer a m (Maybe a)
+ Data.Conduit.Combinators.Stream: repeatMS :: Monad m => m a -> StreamProducer m a
+ Data.Conduit.Combinators.Stream: repeatWhileMS :: Monad m => m a -> (a -> Bool) -> StreamProducer m a
+ Data.Conduit.Combinators.Stream: scanlMS :: Monad m => (a -> b -> m a) -> a -> StreamConduit b m a
+ Data.Conduit.Combinators.Stream: scanlS :: Monad m => (a -> b -> a) -> a -> StreamConduit b m a
+ Data.Conduit.Combinators.Stream: sinkLazyBuilderS :: (Monad m, Monoid builder, ToBuilder a builder, Builder builder lazy) => StreamConsumer a m lazy
+ Data.Conduit.Combinators.Stream: sinkLazyS :: (Monad m, LazySequence lazy strict) => StreamConsumer strict m lazy
+ Data.Conduit.Combinators.Stream: sinkVectorNS :: (MonadBase base m, Vector v a, PrimMonad base) => Int -> StreamConsumer a m (v a)
+ Data.Conduit.Combinators.Stream: sinkVectorS :: (MonadBase base m, Vector v a, PrimMonad base) => StreamConsumer a m (v a)
+ Data.Conduit.Combinators.Stream: slidingWindowS :: (Monad m, IsSequence seq, Element seq ~ a) => Int -> StreamConduit a m seq
+ Data.Conduit.Combinators.Stream: sourceHandleS :: (MonadIO m, IOData a) => Handle -> StreamProducer m a
+ Data.Conduit.Combinators.Stream: splitOnUnboundedES :: (Monad m, IsSequence seq) => (Element seq -> Bool) -> StreamConduit seq m seq
+ Data.Conduit.Combinators.Stream: yieldManyS :: (Monad m, MonoFoldable mono) => mono -> StreamProducer m (Element mono)
Files
- ChangeLog.md +1/−0
- Data/Conduit/Combinators.hs +484/−372
- Data/Conduit/Combinators/Internal.hs +17/−15
- Data/Conduit/Combinators/Stream.hs +451/−0
- Data/Conduit/Combinators/Unqualified.hs +0/−5
- conduit-combinators.cabal +13/−3
- fusion-macros.h +23/−0
- test/Spec.hs +4/−2
- test/StreamSpec.hs +480/−0
+ ChangeLog.md view
@@ -0,0 +1,1 @@+__0.3.0__ Stream fusion enabled, drop compatibility with older conduit
Data/Conduit/Combinators.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE GADTs #-}@@ -60,6 +61,7 @@ , fold , foldE , foldl+ , foldl1 , foldlE , foldMap , foldMapE@@ -170,8 +172,10 @@ , lineAscii , unlines , unlinesAscii+ , takeExactlyUntilE , linesUnbounded , linesUnboundedAscii+ , splitOnUnboundedE -- * Special , vectorBuilder@@ -196,9 +200,11 @@ import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Resource (MonadResource, MonadThrow) import Data.Conduit+import qualified Data.Conduit.Filesystem as CF import Data.Conduit.Internal (ConduitM (..), Pipe (..)) import qualified Data.Conduit.List as CL import Data.IOData+import Data.Maybe (isNothing, isJust) import Data.Monoid (Monoid (..)) import Data.MonoTraversable import qualified Data.Sequences as Seq@@ -225,6 +231,8 @@ import Data.Text (Text) import qualified System.Random.MWC as MWC import Data.Conduit.Combinators.Internal+import Data.Conduit.Combinators.Stream+import Data.Conduit.Internal.Fusion import qualified System.PosixCompat.Files as PosixC import Data.Primitive.MutVar (MutVar, newMutVar, readMutVar, writeMutVar)@@ -233,32 +241,74 @@ import qualified System.Posix.Directory as Dir #endif -#if MIN_VERSION_conduit(1,1,0)-import qualified Data.Conduit.Filesystem as CF-#endif+-- Defines INLINE_RULE0, INLINE_RULE, STREAMING0, and STREAMING.+#include "fusion-macros.h" -- END IMPORTS +-- TODO:+--+-- * The functions sourceRandom* are based on, initReplicate and+-- initRepeat have specialized versions for when they're used with+-- ($$). How does this interact with stream fusion?+--+-- * Is it possible to implement fusion for vectorBuilder? Since it+-- takes a Sink yielding function as an input, the rewrite rule+-- would need to trigger when that parameter looks something like+-- (\x -> unstream (...)). I don't see anything preventing doing+-- this, but it would be quite a bit of code.++-- NOTE: Fusion isn't possible for the following operations:+--+-- * Due to a lack of leftovers:+-- - dropE, dropWhile, dropWhileE+-- - headE+-- - peek, peekE+-- - null, nullE+-- - takeE, takeWhile, takeWhileE+-- - mapWhile+-- - codeWith+-- - line+-- - lineAscii+--+-- * Due to a use of leftover in a dependency:+-- - Due to "codeWith": encodeBase64, decodeBase64, encodeBase64URL, decodeBase64URL, decodeBase16+-- - due to "CT.decode": decodeUtf8, decodeUtf8Lenient+--+-- * Due to lack of resource cleanup (e.g. bracketP):+-- - sourceDirectory+-- - sourceDirectoryDeep+-- - sourceFile+--+-- * takeExactly / takeExactlyE - no monadic bind. Another way to+-- look at this is that subsequent streams drive stream evaluation,+-- so there's no way for the conduit to guarantee a certain amount+-- of demand from the upstream.+ -- | Yield each of the values contained by the given @MonoFoldable@. -- -- This will work on many data structures, including lists, @ByteString@s, and @Vector@s. --+-- Subject to fusion+-- -- Since 1.0.0-yieldMany :: (Monad m, MonoFoldable mono)- => mono- -> Producer m (Element mono)-yieldMany = ofoldMap yield-{-# INLINE yieldMany #-}+yieldMany, yieldManyC :: (Monad m, MonoFoldable mono)+ => mono+ -> Producer m (Element mono)+yieldManyC = ofoldMap yield+{-# INLINE yieldManyC #-}+STREAMING(yieldMany, x) -- | Generate a producer from a seed value. --+-- Subject to fusion+-- -- Since 1.0.0 unfold :: Monad m => (b -> Maybe (a, b)) -> b -> Producer m a-unfold = CL.unfold-{-# INLINE unfold #-}+INLINE_RULE(unfold, f x, CL.unfold f x) -- | Enumerate from a value to a final value, inclusive, via 'succ'. --@@ -266,88 +316,91 @@ -- combining with @sourceList@ since this avoids any intermediate data -- structures. --+-- Subject to fusion+-- -- Since 1.0.0 enumFromTo :: (Monad m, Enum a, Ord a) => a -> a -> Producer m a-enumFromTo = CL.enumFromTo+INLINE_RULE(enumFromTo, f t, CL.enumFromTo f t) -- | Produces an infinite stream of repeated applications of f to x. --+-- Subject to fusion+-- -- Since 1.0.0 iterate :: Monad m => (a -> a) -> a -> Producer m a-iterate = CL.iterate-{-# INLINE iterate #-}+INLINE_RULE(iterate, f t, CL.iterate f t) -- | Produce an infinite stream consisting entirely of the given value. --+-- Subject to fusion+-- -- Since 1.0.0 repeat :: Monad m => a -> Producer m a-repeat = iterate id-{-# INLINE repeat #-}+INLINE_RULE(repeat, x, iterate id x) -- | Produce a finite stream consisting of n copies of the given value. --+-- Subject to fusion+-- -- Since 1.0.0 replicate :: Monad m => Int -> a -> Producer m a-replicate count0 a =- loop count0- where- loop count = if count <= 0- then return ()- else yield a >> loop (count - 1)-{-# INLINE replicate #-}+INLINE_RULE(replicate, n x, CL.replicate n x) -- | Generate a producer by yielding each of the strict chunks in a @LazySequence@. -- -- For more information, see 'toChunks'. --+-- Subject to fusion+-- -- Since 1.0.0 sourceLazy :: (Monad m, LazySequence lazy strict) => lazy -> Producer m strict-sourceLazy = yieldMany . toChunks-{-# INLINE sourceLazy #-}+INLINE_RULE(sourceLazy, x, yieldMany (toChunks x)) -- | Repeatedly run the given action and yield all values it produces. --+-- Subject to fusion+-- -- Since 1.0.0-repeatM :: Monad m- => m a- -> Producer m a-repeatM m = forever $ lift m >>= yield-{-# INLINE repeatM #-}+repeatM, repeatMC :: Monad m+ => m a+ -> Producer m a+repeatMC m = forever $ lift m >>= yield+{-# INLINE repeatMC #-}+STREAMING(repeatM, m) -- | Repeatedly run the given action and yield all values it produces, until -- the provided predicate returns @False@. --+-- Subject to fusion+-- -- Since 1.0.0-repeatWhileM :: Monad m- => m a- -> (a -> Bool)- -> Producer m a-repeatWhileM m f =+repeatWhileM, repeatWhileMC :: Monad m+ => m a+ -> (a -> Bool)+ -> Producer m a+repeatWhileMC m f = loop where loop = do x <- lift m when (f x) $ yield x >> loop+STREAMING(repeatWhileM, m f) -- | Perform the given action n times, yielding each result. --+-- Subject to fusion+-- -- Since 1.0.0 replicateM :: Monad m => Int -> m a -> Producer m a-replicateM count0 m =- loop count0- where- loop count = if count <= 0- then return ()- else lift m >>= yield >> loop (count - 1)-{-# INLINE replicateM #-}+INLINE_RULE(replicateM, n m, CL.replicateM n m) -- | Read all data from the given file. --@@ -364,9 +417,11 @@ -- -- Does not close the @Handle@ at any point. --+-- Subject to fusion+-- -- Since 1.0.0-sourceHandle :: (MonadIO m, IOData a) => Handle -> Producer m a-sourceHandle h =+sourceHandle, sourceHandleC :: (MonadIO m, IOData a) => Handle -> Producer m a+sourceHandleC h = loop where loop = do@@ -374,7 +429,8 @@ if onull x then return () else yield x >> loop-{-# INLINEABLE sourceHandle #-}+{-# INLINEABLE sourceHandleC #-}+STREAMING(sourceHandle, h) -- | Open a @Handle@ using the given function and stream data from it. --@@ -387,48 +443,54 @@ -- | @sourceHandle@ applied to @stdin@. --+-- Subject to fusion+-- -- Since 1.0.0 stdin :: (MonadIO m, IOData a) => Producer m a-stdin = sourceHandle SIO.stdin+INLINE_RULE0(stdin, sourceHandle SIO.stdin) -- | Create an infinite stream of random values, seeding from the system random -- number. --+-- Subject to fusion+-- -- Since 1.0.0 sourceRandom :: (MWC.Variate a, MonadIO m) => Producer m a-sourceRandom = initRepeat (liftIO MWC.createSystemRandom) (liftIO . MWC.uniform)-{-# INLINE sourceRandom #-}+INLINE_RULE0(sourceRandom, initRepeat (liftIO MWC.createSystemRandom) (liftIO . MWC.uniform)) -- | Create a stream of random values of length n, seeding from the system -- random number. --+-- Subject to fusion+-- -- Since 1.0.0 sourceRandomN :: (MWC.Variate a, MonadIO m) => Int -- ^ count -> Producer m a-sourceRandomN = initReplicate (liftIO MWC.createSystemRandom) (liftIO . MWC.uniform)-{-# INLINE [0] sourceRandomN #-}+INLINE_RULE(sourceRandomN, cnt, initReplicate (liftIO MWC.createSystemRandom) (liftIO . MWC.uniform) cnt) -- | Create an infinite stream of random values, using the given random number -- generator. --+-- Subject to fusion+-- -- Since 1.0.0 sourceRandomGen :: (MWC.Variate a, MonadBase base m, PrimMonad base) => MWC.Gen (PrimState base) -> Producer m a-sourceRandomGen gen = initRepeat (return gen) (liftBase . MWC.uniform)-{-# INLINE sourceRandomGen #-}+INLINE_RULE(sourceRandomGen, gen, initRepeat (return gen) (liftBase . MWC.uniform)) -- | Create a stream of random values of length n, seeding from the system -- random number. --+-- Subject to fusion+-- -- Since 1.0.0 sourceRandomNGen :: (MWC.Variate a, MonadBase base m, PrimMonad base) => MWC.Gen (PrimState base) -> Int -- ^ count -> Producer m a-sourceRandomNGen gen = initReplicate (return gen) (liftBase . MWC.uniform)-{-# INLINE sourceRandomNGen #-}+INLINE_RULE(sourceRandomNGen, gen cnt, initReplicate (return gen) (liftBase . MWC.uniform) cnt) -- | Stream the contents of the given directory, without traversing deeply. --@@ -442,26 +504,8 @@ -- -- Since 1.0.0 sourceDirectory :: MonadResource m => FilePath -> Producer m FilePath-#if MIN_VERSION_conduit(1,1,0) sourceDirectory = mapOutput decodeString . CF.sourceDirectory . encodeString-#else -#ifdef WINDOWS-sourceDirectory = (liftIO . F.listDirectory) >=> yieldMany-#else-sourceDirectory dir =- bracketP (Dir.openDirStream $ encodeString dir) Dir.closeDirStream loop- where- loop ds = do- fp <- liftIO $ Dir.readDirStream ds- unless (Prelude.null fp) $ do- unless (fp == "." || fp == "..")- $ yield $ dir </> decodeString fp- loop ds-#endif--#endif- -- | Deeply stream the contents of the given directory. -- -- This works the same as @sourceDirectory@, but will not return directories at@@ -473,46 +517,15 @@ => Bool -- ^ Follow directory symlinks -> FilePath -- ^ Root directory -> Producer m FilePath-#if MIN_VERSION_conduit(1,1,0) sourceDirectoryDeep follow = mapOutput decodeString . CF.sourceDirectoryDeep follow . encodeString-#else -sourceDirectoryDeep followSymlinks =- start- where- start :: MonadResource m => FilePath -> Producer m FilePath- start dir = sourceDirectory dir =$= awaitForever go-- go :: MonadResource m => FilePath -> Producer m FilePath- go fp = do- isFile' <- liftIO $ F.isFile fp- if isFile'- then yield fp- else do- follow' <- liftIO $ follow fp- when follow' (start fp)-- follow :: FilePath -> Prelude.IO Bool- follow p = do- let path = encodeString p- stat <- if followSymlinks- then PosixC.getFileStatus path- else PosixC.getSymbolicLinkStatus path- return (PosixC.isDirectory stat)-#endif- -- | Ignore a certain number of values in the stream. -- -- Since 1.0.0 drop :: Monad m => Int -> Consumer a m ()-drop =- loop- where- loop i | i <= 0 = return ()- loop count = await >>= maybe (return ()) (\_ -> loop (count - 1))-{-# INLINE drop #-}+INLINE_RULE(drop, n, CL.drop n) -- | Drop a certain number of elements from a chunked stream. --@@ -567,184 +580,241 @@ -- | Monoidally combine all values in the stream. --+-- Subject to fusion+-- -- Since 1.0.0 fold :: (Monad m, Monoid a) => Consumer a m a-fold = CL.foldMap id-{-# INLINE fold #-}+INLINE_RULE0(fold, CL.foldMap id) -- | Monoidally combine all elements in the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 foldE :: (Monad m, MonoFoldable mono, Monoid (Element mono)) => Consumer mono m (Element mono)-foldE = CL.fold (\accum mono -> accum `mappend` ofoldMap id mono) mempty-{-# INLINE foldE #-}+INLINE_RULE0(foldE, CL.fold (\accum mono -> accum `mappend` ofoldMap id mono) mempty) -- | A strict left fold. --+-- Subject to fusion+-- -- Since 1.0.0 foldl :: Monad m => (a -> b -> a) -> a -> Consumer b m a-foldl = CL.fold-{-# INLINE foldl #-}+INLINE_RULE(foldl, f x, CL.fold f x) -- | A strict left fold on a chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 foldlE :: (Monad m, MonoFoldable mono) => (a -> Element mono -> a) -> a -> Consumer mono m a-foldlE f = CL.fold (ofoldl' f)-{-# INLINE foldlE #-}+INLINE_RULE(foldlE, f x, CL.fold (ofoldlPrime f) x) +-- Work around CPP not supporting identifiers with primes...+ofoldlPrime :: MonoFoldable mono => (a -> Element mono -> a) -> a -> mono -> a+ofoldlPrime = ofoldl'+ -- | Apply the provided mapping function and monoidal combine all values. --+-- Subject to fusion+-- -- Since 1.0.0 foldMap :: (Monad m, Monoid b) => (a -> b) -> Consumer a m b-foldMap = CL.foldMap-{-# INLINE foldMap #-}+INLINE_RULE(foldMap, f, CL.foldMap f) -- | Apply the provided mapping function and monoidal combine all elements of the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 foldMapE :: (Monad m, MonoFoldable mono, Monoid w) => (Element mono -> w) -> Consumer mono m w-foldMapE = CL.foldMap . ofoldMap-{-# INLINE foldMapE #-}+INLINE_RULE(foldMapE, f, CL.foldMap (ofoldMap f)) +-- | A strict left fold with no starting value. Returns 'Nothing'+-- when the stream is empty.+--+-- Subject to fusion+foldl1, foldl1C :: Monad m => (a -> a -> a) -> Consumer a m (Maybe a)+foldl1C f =+ await >>= maybe (return Nothing) loop+ where+ loop prev = await >>= maybe (return $ Just prev) (loop . f prev)+STREAMING(foldl1, f)++-- | A strict left fold on a chunked stream, with no starting value.+-- Returns 'Nothing' when the stream is empty.+--+-- Subject to fusion+--+-- Since 1.0.0+foldl1E :: (Monad m, MonoFoldable mono, a ~ Element mono)+ => (a -> a -> a)+ -> Consumer mono m (Maybe a)+INLINE_RULE(foldl1E, f, foldl (foldMaybeNull f) Nothing)++-- Helper for foldl1E+foldMaybeNull :: (MonoFoldable mono, e ~ Element mono)+ => (e -> e -> e)+ -> Maybe e+ -> mono+ -> Maybe e+foldMaybeNull f macc mono =+ case (macc, NonNull.fromNullable mono) of+ (Just acc, Just nn) -> Just $ ofoldl' f acc nn+ (Nothing, Just nn) -> Just $ NonNull.ofoldl1' f nn+ _ -> macc+{-# INLINE foldMaybeNull #-}+ -- | Check that all values in the stream return True. -- -- Subject to shortcut logic: at the first False, consumption of the stream -- will stop. --+-- Subject to fusion+-- -- Since 1.0.0-all :: Monad m- => (a -> Bool)- -> Consumer a m Bool-all f =- loop- where- loop = await >>= maybe (return True) go- go x = if f x then loop else return False-{-# INLINE all #-}+all, allC :: Monad m+ => (a -> Bool)+ -> Consumer a m Bool+allC f = fmap isNothing $ find (Prelude.not . f)+{-# INLINE allC #-}+STREAMING(all, f) -- | Check that all elements in the chunked stream return True. -- -- Subject to shortcut logic: at the first False, consumption of the stream -- will stop. --+-- Subject to fusion+-- -- Since 1.0.0 allE :: (Monad m, MonoFoldable mono) => (Element mono -> Bool) -> Consumer mono m Bool-allE = all . oall+INLINE_RULE(allE, f, all (oall f)) -- | Check that at least one value in the stream returns True. -- -- Subject to shortcut logic: at the first True, consumption of the stream -- will stop. --+-- Subject to fusion+-- -- Since 1.0.0-any :: Monad m- => (a -> Bool)- -> Consumer a m Bool-any f =- loop- where- loop = await >>= maybe (return False) go- go x = if f x then return True else loop-{-# INLINE any #-}+any, anyC :: Monad m+ => (a -> Bool)+ -> Consumer a m Bool+anyC = fmap isJust . find+{-# INLINE anyC #-}+STREAMING(any, f) -- | Check that at least one element in the chunked stream returns True. -- -- Subject to shortcut logic: at the first True, consumption of the stream -- will stop. --+-- Subject to fusion+-- -- Since 1.0.0 anyE :: (Monad m, MonoFoldable mono) => (Element mono -> Bool) -> Consumer mono m Bool-anyE = any . oany+INLINE_RULE(anyE, f, any (oany f)) -- | Are all values in the stream True? -- -- Consumption stops once the first False is encountered. --+-- Subject to fusion+-- -- Since 1.0.0 and :: Monad m => Consumer Bool m Bool-and = all id-{-# INLINE and #-}+INLINE_RULE0(and, all id) -- | Are all elements in the chunked stream True? -- -- Consumption stops once the first False is encountered. --+-- Subject to fusion+-- -- Since 1.0.0 andE :: (Monad m, MonoFoldable mono, Element mono ~ Bool) => Consumer mono m Bool-andE = allE id-{-# INLINE andE #-}+INLINE_RULE0(andE, allE id) -- | Are any values in the stream True? -- -- Consumption stops once the first True is encountered. --+-- Subject to fusion+-- -- Since 1.0.0 or :: Monad m => Consumer Bool m Bool-or = any id-{-# INLINE or #-}+INLINE_RULE0(or, any id) -- | Are any elements in the chunked stream True? -- -- Consumption stops once the first True is encountered. --+-- Subject to fusion+-- -- Since 1.0.0 orE :: (Monad m, MonoFoldable mono, Element mono ~ Bool) => Consumer mono m Bool-orE = anyE id-{-# INLINE orE #-}+INLINE_RULE0(orE, anyE id) -- | Are any values in the stream equal to the given value? -- -- Stops consuming as soon as a match is found. --+-- Subject to fusion+-- -- Since 1.0.0 elem :: (Monad m, Eq a) => a -> Consumer a m Bool-elem x = any (== x)-{-# INLINE elem #-}+INLINE_RULE(elem, x, any (== x)) -- | Are any elements in the chunked stream equal to the given element? -- -- Stops consuming as soon as a match is found. --+-- Subject to fusion+-- -- Since 1.0.0 elemE :: (Monad m, Seq.EqSequence seq) => Element seq -> Consumer seq m Bool-elemE = any . Seq.elem+INLINE_RULE(elemE, f, any (Seq.elem f)) -- | Are no values in the stream equal to the given value? -- -- Stops consuming as soon as a match is found. --+-- Subject to fusion+-- -- Since 1.0.0 notElem :: (Monad m, Eq a) => a -> Consumer a m Bool-notElem x = all (/= x)-{-# INLINE notElem #-}+INLINE_RULE(notElem, x, all (/= x)) -- | Are no elements in the chunked stream equal to the given element? -- -- Stops consuming as soon as a match is found. --+-- Subject to fusion+-- -- Since 1.0.0 notElemE :: (Monad m, Seq.EqSequence seq) => Element seq -> Consumer seq m Bool-notElemE = all . Seq.notElem+INLINE_RULE(notElemE, x, all (Seq.notElem x)) -- | Consume all incoming strict chunks into a lazy sequence. -- Note that the entirety of the sequence will be resident at memory.@@ -752,19 +822,23 @@ -- This can be used to consume a stream of strict ByteStrings into a lazy -- ByteString, for example. --+-- Subject to fusion+-- -- Since 1.0.0-sinkLazy :: (Monad m, LazySequence lazy strict)- => Consumer strict m lazy-sinkLazy = (fromChunks . ($ [])) <$> CL.fold (\front next -> front . (next:)) id-{-# INLINE sinkLazy #-}+sinkLazy, sinkLazyC :: (Monad m, LazySequence lazy strict)+ => Consumer strict m lazy+sinkLazyC = (fromChunks . ($ [])) <$> CL.fold (\front next -> front . (next:)) id+{-# INLINE sinkLazyC #-}+STREAMING0(sinkLazy) -- | Consume all values from the stream and return as a list. Note that this -- will pull all values into memory. --+-- Subject to fusion+-- -- Since 1.0.0 sinkList :: Monad m => Consumer a m [a]-sinkList = CL.consume-{-# INLINE sinkList #-}+INLINE_RULE0(sinkList, CL.consume) -- | Sink incoming values into a vector, growing the vector as necessary to fit -- more elements.@@ -772,10 +846,12 @@ -- Note that using this function is more memory efficient than @sinkList@ and -- then converting to a @Vector@, as it avoids intermediate list constructors. --+-- Subject to fusion+-- -- Since 1.0.0-sinkVector :: (MonadBase base m, V.Vector v a, PrimMonad base)- => Consumer a m (v a)-sinkVector = do+sinkVector, sinkVectorC :: (MonadBase base m, V.Vector v a, PrimMonad base)+ => Consumer a m (v a)+sinkVectorC = do let initSize = 10 mv0 <- liftBase $ VM.new initSize let go maxSize i mv | i >= maxSize = do@@ -790,7 +866,8 @@ liftBase $ VM.write mv i x go maxSize (i + 1) mv go initSize 0 mv0-{-# INLINEABLE sinkVector #-}+{-# INLINEABLE sinkVectorC #-}+STREAMING0(sinkVector) -- | Sink incoming values into a vector, up until size @maxSize@. Subsequent -- values will be left in the stream. If there are less than @maxSize@ values@@ -799,11 +876,13 @@ -- Note that using this function is more memory efficient than @sinkList@ and -- then converting to a @Vector@, as it avoids intermediate list constructors. --+-- Subject to fusion+-- -- Since 1.0.0-sinkVectorN :: (MonadBase base m, V.Vector v a, PrimMonad base)- => Int -- ^ maximum allowed size- -> Consumer a m (v a)-sinkVectorN maxSize = do+sinkVectorN, sinkVectorNC :: (MonadBase base m, V.Vector v a, PrimMonad base)+ => Int -- ^ maximum allowed size+ -> Consumer a m (v a)+sinkVectorNC maxSize = do mv <- liftBase $ VM.new maxSize let go i | i >= maxSize = liftBase $ V.unsafeFreeze mv go i = do@@ -814,17 +893,19 @@ liftBase $ VM.write mv i x go (i + 1) go 0-{-# INLINEABLE sinkVectorN #-}+{-# INLINEABLE sinkVectorNC #-}+STREAMING(sinkVectorN, maxSize) -- | Convert incoming values to a builder and fold together all builder values. -- -- Defined as: @foldMap toBuilder@. --+-- Subject to fusion+-- -- Since 1.0.0 sinkBuilder :: (Monad m, Monoid builder, ToBuilder a builder) => Consumer a m builder-sinkBuilder = foldMap toBuilder-{-# INLINE sinkBuilder #-}+INLINE_RULE0(sinkBuilder, foldMap toBuilder) -- | Same as @sinkBuilder@, but afterwards convert the builder to its lazy -- representation.@@ -837,18 +918,22 @@ -- -- * Some buffer copying may occur in this version. --+-- Subject to fusion+-- -- Since 1.0.0-sinkLazyBuilder :: (Monad m, Monoid builder, ToBuilder a builder, Builder builder lazy)- => Consumer a m lazy-sinkLazyBuilder = fmap builderToLazy sinkBuilder-{-# INLINE sinkLazyBuilder #-}+sinkLazyBuilder, sinkLazyBuilderC :: (Monad m, Monoid builder, ToBuilder a builder, Builder builder lazy)+ => Consumer a m lazy+sinkLazyBuilderC = fmap builderToLazy sinkBuilder+{-# INLINE sinkLazyBuilderC #-}+STREAMING0(sinkLazyBuilder) -- | Consume and discard all remaining values in the stream. --+-- Subject to fusion+-- -- Since 1.0.0 sinkNull :: Monad m => Consumer a m ()-sinkNull = CL.sinkNull-{-# INLINE sinkNull #-}+INLINE_RULE0(sinkNull, CL.sinkNull) -- | Same as @await@, but discards any leading 'onull' values. --@@ -903,103 +988,92 @@ -- | Retrieve the last value in the stream, if present. --+-- Subject to fusion+-- -- Since 1.0.0-last :: Monad m => Consumer a m (Maybe a)-last =+last, lastC :: Monad m => Consumer a m (Maybe a)+lastC = await >>= maybe (return Nothing) loop where loop prev = await >>= maybe (return $ Just prev) loop-{-# INLINE last #-}+STREAMING0(last) -- | Retrieve the last element in the chunked stream, if present. --+-- Subject to fusion+-- -- Since 1.0.0-lastE :: (Monad m, Seq.IsSequence seq) => Consumer seq m (Maybe (Element seq))-lastE =+lastE, lastEC :: (Monad m, Seq.IsSequence seq) => Consumer seq m (Maybe (Element seq))+lastEC = awaitNonNull >>= maybe (return Nothing) (loop . NonNull.last) where- loop prev = awaitNonNull >>= maybe (return $ Just prev) (loop . NonNull.last)-{-# INLINE lastE #-}+STREAMING0(lastE) -- | Count how many values are in the stream. --+-- Subject to fusion+-- -- Since 1.0.0 length :: (Monad m, Num len) => Consumer a m len-length = foldl (\x _ -> x + 1) 0-{-# INLINE length #-}+INLINE_RULE0(length, foldl (\x _ -> x + 1) 0) -- | Count how many elements are in the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 lengthE :: (Monad m, Num len, MonoFoldable mono) => Consumer mono m len-lengthE = foldl (\x y -> x + fromIntegral (olength y)) 0-{-# INLINE lengthE #-}+INLINE_RULE0(lengthE, foldl (\x y -> x + fromIntegral (olength y)) 0) -- | Count how many values in the stream pass the given predicate. --+-- Subject to fusion+-- -- Since 1.0.0 lengthIf :: (Monad m, Num len) => (a -> Bool) -> Consumer a m len-lengthIf f = foldl (\cnt a -> if f a then (cnt + 1) else cnt) 0-{-# INLINE lengthIf #-}+INLINE_RULE(lengthIf, f, foldl (\cnt a -> if f a then (cnt + 1) else cnt) 0) -- | Count how many elements in the chunked stream pass the given predicate. --+-- Subject to fusion+-- -- Since 1.0.0 lengthIfE :: (Monad m, Num len, MonoFoldable mono) => (Element mono -> Bool) -> Consumer mono m len-lengthIfE f = foldlE (\cnt a -> if f a then (cnt + 1) else cnt) 0-{-# INLINE lengthIfE #-}+INLINE_RULE(lengthIfE, f, foldlE (\cnt a -> if f a then (cnt + 1) else cnt) 0) -- | Get the largest value in the stream, if present. --+-- Subject to fusion+-- -- Since 1.0.0 maximum :: (Monad m, Ord a) => Consumer a m (Maybe a)-maximum =- await >>= maybe (return Nothing) loop- where- loop prev = await >>= maybe (return $ Just prev) (loop . max prev)-{-# INLINE maximum #-}+INLINE_RULE0(maximum, foldl1 max) -- | Get the largest element in the chunked stream, if present. --+-- Subject to fusion+-- -- Since 1.0.0 maximumE :: (Monad m, Seq.OrdSequence seq) => Consumer seq m (Maybe (Element seq))-maximumE =- start- where- start = await >>= maybe (return Nothing) start'- start' x =- case NonNull.fromNullable x of- Nothing -> start- Just y -> loop $ NonNull.maximum y- loop prev = await >>= maybe (return $ Just prev) (loop . ofoldl' max prev)-{-# INLINE maximumE #-}+INLINE_RULE0(maximumE, foldl1E max) -- | Get the smallest value in the stream, if present. --+-- Subject to fusion+-- -- Since 1.0.0 minimum :: (Monad m, Ord a) => Consumer a m (Maybe a)-minimum =- await >>= maybe (return Nothing) loop- where- loop prev = await >>= maybe (return $ Just prev) (loop . min prev)-{-# INLINE minimum #-}+INLINE_RULE0(minimum, foldl1 min) -- | Get the smallest element in the chunked stream, if present. --+-- Subject to fusion+-- -- Since 1.0.0 minimumE :: (Monad m, Seq.OrdSequence seq) => Consumer seq m (Maybe (Element seq))-minimumE =- start- where- start = await >>= maybe (return Nothing) start'- start' x =- case NonNull.fromNullable x of- Nothing -> start- Just y -> loop $ NonNull.minimum y- loop prev = await >>= maybe (return $ Just prev) (loop . ofoldl' min prev)-{-# INLINE minimumE #-}+INLINE_RULE0(minimumE, foldl1E min) -- | True if there are no values in the stream. --@@ -1027,92 +1101,104 @@ -- | Get the sum of all values in the stream. --+-- Subject to fusion+-- -- Since 1.0.0 sum :: (Monad m, Num a) => Consumer a m a-sum = foldl (+) 0-{-# INLINE sum #-}+INLINE_RULE0(sum, foldl (+) 0) -- | Get the sum of all elements in the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 sumE :: (Monad m, MonoFoldable mono, Num (Element mono)) => Consumer mono m (Element mono)-sumE = foldlE (+) 0-{-# INLINE sumE #-}+INLINE_RULE0(sumE, foldlE (+) 0) -- | Get the product of all values in the stream. --+-- Subject to fusion+-- -- Since 1.0.0 product :: (Monad m, Num a) => Consumer a m a-product = foldl (*) 1-{-# INLINE product #-}+INLINE_RULE0(product, foldl (*) 1) -- | Get the product of all elements in the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 productE :: (Monad m, MonoFoldable mono, Num (Element mono)) => Consumer mono m (Element mono)-productE = foldlE (*) 1-{-# INLINE productE #-}+INLINE_RULE0(productE, foldlE (*) 1) -- | Find the first matching value. --+-- Subject to fusion+-- -- Since 1.0.0-find :: Monad m => (a -> Bool) -> Consumer a m (Maybe a)-find f =+find, findC :: Monad m => (a -> Bool) -> Consumer a m (Maybe a)+findC f = loop where loop = await >>= maybe (return Nothing) go go x = if f x then return (Just x) else loop+{-# INLINE findC #-}+STREAMING(find, f) -- | Apply the action to all values in the stream. --+-- Subject to fusion+-- -- Since 1.0.0 mapM_ :: Monad m => (a -> m ()) -> Consumer a m ()-mapM_ = CL.mapM_-{-# INLINE mapM_ #-}+INLINE_RULE(mapM_, f, CL.mapM_ f) -- | Apply the action to all elements in the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 mapM_E :: (Monad m, MonoFoldable mono) => (Element mono -> m ()) -> Consumer mono m ()-mapM_E = CL.mapM_ . omapM_-{-# INLINE mapM_E #-}+INLINE_RULE(mapM_E, f, CL.mapM_ (omapM_ f)) -- | A monadic strict left fold. --+-- Subject to fusion+-- -- Since 1.0.0 foldM :: Monad m => (a -> b -> m a) -> a -> Consumer b m a-foldM = CL.foldM-{-# INLINE foldM #-}+INLINE_RULE(foldM, f x, CL.foldM f x) -- | A monadic strict left fold on a chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 foldME :: (Monad m, MonoFoldable mono) => (a -> Element mono -> m a) -> a -> Consumer mono m a-foldME f = foldM (ofoldlM f)-{-# INLINE foldME #-}+INLINE_RULE(foldME, f x, foldM (ofoldlM f) x) -- | Apply the provided monadic mapping function and monoidal combine all values. --+-- Subject to fusion+-- -- Since 1.0.0 foldMapM :: (Monad m, Monoid w) => (a -> m w) -> Consumer a m w-foldMapM = CL.foldMapM-{-# INLINE foldMapM #-}+INLINE_RULE(foldMapM, f, CL.foldMapM f) -- | Apply the provided monadic mapping function and monoidal combine all -- elements in the chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 foldMapME :: (Monad m, MonoFoldable mono, Monoid w) => (Element mono -> m w) -> Consumer mono m w-foldMapME f =- CL.foldM go mempty- where- go = ofoldlM (\accum e -> mappend accum `liftM` f e)-{-# INLINE foldMapME #-}+INLINE_RULE(foldMapME, f,+ CL.foldM (ofoldlM (\accum e -> mappend accum `liftM` f e)) mempty) -- | Write all data to the given file. --@@ -1127,30 +1213,37 @@ -- | Print all incoming values to stdout. --+-- Subject to fusion+-- -- Since 1.0.0 print :: (Show a, MonadIO m) => Consumer a m ()-print = mapM_ (liftIO . Prelude.print)+INLINE_RULE0(print, mapM_ (liftIO . Prelude.print)) -- | @sinkHandle@ applied to @stdout@. --+-- Subject to fusion+-- -- Since 1.0.0 stdout :: (MonadIO m, IOData a) => Consumer a m ()-stdout = sinkHandle SIO.stdout+INLINE_RULE0(stdout, sinkHandle SIO.stdout) -- | @sinkHandle@ applied to @stderr@. --+-- Subject to fusion+-- -- Since 1.0.0 stderr :: (MonadIO m, IOData a) => Consumer a m ()-stderr = sinkHandle SIO.stderr+INLINE_RULE0(stderr, sinkHandle SIO.stderr) -- | Write all data to the given @Handle@. -- -- Does not close the @Handle@ at any point. --+-- Subject to fusion+-- -- Since 1.0.0 sinkHandle :: (MonadIO m, IOData a) => Handle -> Consumer a m ()-sinkHandle = CL.mapM_ . hPut-{-# INLINE sinkHandle #-}+INLINE_RULE(sinkHandle, h, CL.mapM_ (hPut h)) -- | Open a @Handle@ using the given function and stream data to it. --@@ -1163,27 +1256,30 @@ -- | Apply a transformation to all values in a stream. --+-- Subject to fusion+-- -- Since 1.0.0 map :: Monad m => (a -> b) -> Conduit a m b-map = CL.map-{-# INLINE map #-}+INLINE_RULE(map, f, CL.map f) -- | Apply a transformation to all elements in a chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 mapE :: (Monad m, Functor f) => (a -> b) -> Conduit (f a) m (f b)-mapE = CL.map . fmap-{-# INLINE mapE #-}+INLINE_RULE(mapE, f, CL.map (fmap f)) -- | Apply a monomorphic transformation to all elements in a chunked stream. -- -- Unlike @mapE@, this will work on types like @ByteString@ and @Text@ which -- are @MonoFunctor@ but not @Functor@. --+-- Subject to fusion+-- -- Since 1.0.0 omapE :: (Monad m, MonoFunctor mono) => (Element mono -> Element mono) -> Conduit mono m mono-omapE = CL.map . omap-{-# INLINE omapE #-}+INLINE_RULE(omapE, f, CL.map (omap f)) -- | Apply the function to each value in the stream, resulting in a foldable -- value (e.g., a list). Then yield each of the individual values in that@@ -1191,12 +1287,15 @@ -- -- Generalizes concatMap, mapMaybe, and mapFoldable. --+-- Subject to fusion+-- -- Since 1.0.0-concatMap :: (Monad m, MonoFoldable mono)- => (a -> mono)- -> Conduit a m (Element mono)-concatMap f = awaitForever (yieldMany . f)-{-# INLINE concatMap #-}+concatMap, concatMapC :: (Monad m, MonoFoldable mono)+ => (a -> mono)+ -> Conduit a m (Element mono)+concatMapC f = awaitForever (yieldMany . f)+{-# INLINE concatMapC #-}+STREAMING(concatMap, f) -- | Apply the function to each element in the chunked stream, resulting in a -- foldable value (e.g., a list). Then yield each of the individual values in@@ -1204,12 +1303,13 @@ -- -- Generalizes concatMap, mapMaybe, and mapFoldable. --+-- Subject to fusion+-- -- Since 1.0.0 concatMapE :: (Monad m, MonoFoldable mono, Monoid w) => (Element mono -> w) -> Conduit mono m w-concatMapE = CL.map . ofoldMap-{-# INLINE concatMapE #-}+INLINE_RULE(concatMapE, f, CL.map (ofoldMap f)) -- | Stream up to n number of values downstream. --@@ -1217,15 +1317,11 @@ -- If you want to force /exactly/ the given number of values to be consumed, -- see 'takeExactly'. --+-- Subject to fusion+-- -- Since 1.0.0 take :: Monad m => Int -> Conduit a m a-take =- loop- where- loop count = if count <= 0- then return ()- else await >>= maybe (return ()) (\i -> yield i >> loop (count - 1))-{-# INLINE take #-}+INLINE_RULE(take, n, CL.isolate n) -- | Stream up to n number of elements downstream in a chunked stream. --@@ -1313,7 +1409,6 @@ r <- inner CL.sinkNull return r-{-# INLINE takeExactly #-} -- | Same as 'takeExactly', but for chunked streams. --@@ -1331,25 +1426,29 @@ -- | Flatten out a stream by yielding the values contained in an incoming -- @MonoFoldable@ as individually yielded values. --+-- Subject to fusion+-- -- Since 1.0.0-concat :: (Monad m, MonoFoldable mono)- => Conduit mono m (Element mono)-concat = awaitForever yieldMany-{-# INLINE concat #-}+concat, concatC :: (Monad m, MonoFoldable mono)+ => Conduit mono m (Element mono)+concatC = awaitForever yieldMany+STREAMING0(concat) -- | Keep only values in the stream passing a given predicate. --+-- Subject to fusion+-- -- Since 1.0.0 filter :: Monad m => (a -> Bool) -> Conduit a m a-filter = CL.filter-{-# INLINE filter #-}+INLINE_RULE(filter, f, CL.filter f) -- | Keep only elements in the chunked stream passing a given predicate. --+-- Subject to fusion+-- -- Since 1.0.0 filterE :: (Seq.IsSequence seq, Monad m) => (Element seq -> Bool) -> Conduit seq m seq-filterE = CL.map . Seq.filter-{-# INLINE filterE #-}+INLINE_RULE(filterE, f, CL.map (Seq.filter f)) -- | Map values as long as the result is @Just@. --@@ -1385,9 +1484,11 @@ -- | Analog of 'Prelude.scanl' for lists. --+-- Subject to fusion+-- -- Since 1.0.6-scanl :: Monad m => (a -> b -> a) -> a -> Conduit b m a-scanl f =+scanl, scanlC :: Monad m => (a -> b -> a) -> a -> Conduit b m a+scanlC f = loop where loop seed =@@ -1397,24 +1498,27 @@ let seed' = f seed b seed' `seq` yield seed loop seed'-{-# INLINE scanl #-}+STREAMING(scanl, f x) -- | 'concatMap' with an accumulator. --+-- Subject to fusion+-- -- Since 1.0.0 concatMapAccum :: Monad m => (a -> accum -> (accum, [b])) -> accum -> Conduit a m b-concatMapAccum = CL.concatMapAccum-{-# INLINE concatMapAccum #-}+INLINE_RULE0(concatMapAccum, CL.concatMapAccum) -- | Insert the given value between each two values in the stream. --+-- Subject to fusion+-- -- Since 1.0.0-intersperse :: Monad m => a -> Conduit a m a-intersperse x =+intersperse, intersperseC :: Monad m => a -> Conduit a m a+intersperseC x = await >>= omapM_ go where go y = yield y >> concatMap (\z -> [x, z])-{-# INLINE intersperse #-}+STREAMING(intersperse, x) -- | Sliding window of values -- 1,2,3,4,5 with window size 2 gives@@ -1422,9 +1526,11 @@ -- -- Best used with structures that support O(1) snoc. --+-- Subject to fusion+-- -- Since 1.0.0-slidingWindow :: (Monad m, Seq.IsSequence seq, Element seq ~ a) => Int -> Conduit a m seq-slidingWindow sz = go (if sz <= 0 then 1 else sz) mempty+slidingWindow, slidingWindowC :: (Monad m, Seq.IsSequence seq, Element seq ~ a) => Int -> Conduit a m seq+slidingWindowC sz = go (max 1 sz) mempty where goContinue st = await >>= maybe (return ()) (\x -> do@@ -1436,6 +1542,7 @@ case m of Nothing -> yield st Just x -> go (n-1) (Seq.snoc st x)+STREAMING(slidingWindow, sz) codeWith :: Monad m => Int@@ -1512,10 +1619,11 @@ -- | Apply base16-encoding to the stream. --+-- Subject to fusion+-- -- Since 1.0.0 encodeBase16 :: Monad m => Conduit ByteString m ByteString-encodeBase16 = map B16.encode-{-# INLINE encodeBase16 #-}+INLINE_RULE0(encodeBase16, map B16.encode) -- | Apply base16-decoding to the stream. Will stop decoding on the first -- invalid chunk.@@ -1537,29 +1645,32 @@ -- If you do not need the transformed values, and instead just want the monadic -- side-effects of running the action, see 'mapM_'. --+-- Subject to fusion+-- -- Since 1.0.0 mapM :: Monad m => (a -> m b) -> Conduit a m b-mapM = CL.mapM-{-# INLINE mapM #-}+INLINE_RULE(mapM, f, CL.mapM f) -- | Apply a monadic transformation to all elements in a chunked stream. --+-- Subject to fusion+-- -- Since 1.0.0 mapME :: (Monad m, Data.Traversable.Traversable f) => (a -> m b) -> Conduit (f a) m (f b)-mapME = CL.mapM . Data.Traversable.mapM-{-# INLINE mapME #-}+INLINE_RULE(mapME, f, CL.mapM (Data.Traversable.mapM f)) -- | Apply a monadic monomorphic transformation to all elements in a chunked stream. -- -- Unlike @mapME@, this will work on types like @ByteString@ and @Text@ which -- are @MonoFunctor@ but not @Functor@. --+-- Subject to fusion+-- -- Since 1.0.0 omapME :: (Monad m, MonoTraversable mono) => (Element mono -> m (Element mono)) -> Conduit mono m mono-omapME = CL.mapM . omapM-{-# INLINE omapME #-}+INLINE_RULE(omapME, f, CL.mapM (omapM f)) -- | Apply the monadic function to each value in the stream, resulting in a -- foldable value (e.g., a list). Then yield each of the individual values in@@ -1567,33 +1678,38 @@ -- -- Generalizes concatMapM, mapMaybeM, and mapFoldableM. --+-- Subject to fusion+-- -- Since 1.0.0-concatMapM :: (Monad m, MonoFoldable mono)- => (a -> m mono)- -> Conduit a m (Element mono)-concatMapM f = awaitForever (lift . f >=> yieldMany)-{-# INLINE concatMapM #-}+concatMapM, concatMapMC :: (Monad m, MonoFoldable mono)+ => (a -> m mono)+ -> Conduit a m (Element mono)+concatMapMC f = awaitForever (lift . f >=> yieldMany)+STREAMING(concatMapM, f) -- | Keep only values in the stream passing a given monadic predicate. --+-- Subject to fusion+-- -- Since 1.0.0-filterM :: Monad m- => (a -> m Bool)- -> Conduit a m a-filterM f =+filterM, filterMC :: Monad m+ => (a -> m Bool)+ -> Conduit a m a+filterMC f = awaitForever go where go x = do b <- lift $ f x when b $ yield x-{-# INLINE filterM #-}+STREAMING(filterM, f) -- | Keep only elements in the chunked stream passing a given monadic predicate. --+-- Subject to fusion+-- -- Since 1.0.0 filterME :: (Monad m, Seq.IsSequence seq) => (Element seq -> m Bool) -> Conduit seq m seq-filterME = CL.mapM . Seq.filterM-{-# INLINE filterME #-}+INLINE_RULE(filterME, f, CL.mapM (Seq.filterM f)) -- | Apply a monadic action on all values in a stream. --@@ -1602,15 +1718,19 @@ -- -- > iterM f = mapM (\a -> f a >>= \() -> return a) --+-- Subject to fusion+-- -- Since 1.0.0 iterM :: Monad m => (a -> m ()) -> Conduit a m a-iterM = CL.iterM+INLINE_RULE(iterM, f, CL.iterM f) -- | Analog of 'Prelude.scanl' for lists, monadic. --+-- Subject to fusion+-- -- Since 1.0.6-scanlM :: Monad m => (a -> b -> m a) -> a -> Conduit b m a-scanlM f =+scanlM, scanlMC :: Monad m => (a -> b -> m a) -> a -> Conduit b m a+scanlMC f = loop where loop seed =@@ -1620,20 +1740,23 @@ seed' <- lift $ f seed b seed' `seq` yield seed loop seed'-{-# INLINE scanlM #-}+STREAMING(scanlM, f x) -- | 'concatMapM' with an accumulator. --+-- Subject to fusion+-- -- Since 1.0.0 concatMapAccumM :: Monad m => (a -> accum -> m (accum, [b])) -> accum -> Conduit a m b-concatMapAccumM = CL.concatMapAccumM-{-# INLINE concatMapAccumM #-}+INLINE_RULE(concatMapAccumM, f x, CL.concatMapAccumM f x) -- | Encode a stream of text as UTF8. --+-- Subject to fusion+-- -- Since 1.0.0 encodeUtf8 :: (Monad m, DTE.Utf8 text binary) => Conduit text m binary-encodeUtf8 = map DTE.encodeUtf8+INLINE_RULE0(encodeUtf8, map DTE.encodeUtf8) -- | Decode a stream of binary data as UTF8. --@@ -1657,22 +1780,7 @@ line :: (Monad m, Seq.IsSequence seq, Element seq ~ Char) => ConduitM seq o m r -> ConduitM seq o m r-line inner = do- loop =$= do- x <- inner- sinkNull- return x- where- loop = await >>= omapM_ go- go t =- if onull y- then yield x >> loop- else do- unless (onull x) $ yield x- let y' = Seq.drop 1 y- unless (onull y') $ leftover y'- where- (x, y) = Seq.break (== '\n') t+line = takeExactlyUntilE (== '\n') {-# INLINE line #-} -- | Same as 'line', but operates on ASCII/binary data.@@ -1681,7 +1789,18 @@ lineAscii :: (Monad m, Seq.IsSequence seq, Element seq ~ Word8) => ConduitM seq o m r -> ConduitM seq o m r-lineAscii inner =+lineAscii = takeExactlyUntilE (== 10)+{-# INLINE lineAscii #-}++-- | Stream in the chunked input until an element matches a predicate.+--+-- Like @takeExactly@, this will consume the entirety of the prefix+-- regardless of the behavior of the inner Conduit.+takeExactlyUntilE :: (Monad m, Seq.IsSequence seq)+ => (Element seq -> Bool)+ -> ConduitM seq o m r+ -> ConduitM seq o m r+takeExactlyUntilE f inner = loop =$= do x <- inner sinkNull@@ -1696,32 +1815,35 @@ let y' = Seq.drop 1 y unless (onull y') $ leftover y' where- (x, y) = Seq.break (== 10) t-{-# INLINE lineAscii #-}+ (x, y) = Seq.break f t+{-# INLINE takeExactlyUntilE #-} -- | Insert a newline character after each incoming chunk of data. --+-- Subject to fusion+-- -- Since 1.0.0 unlines :: (Monad m, Seq.IsSequence seq, Element seq ~ Char) => Conduit seq m seq-unlines = concatMap (:[Seq.singleton '\n'])-{-# INLINE unlines #-}+INLINE_RULE0(unlines, concatMap (:[Seq.singleton '\n'])) -- | Same as 'unlines', but operates on ASCII/binary data. --+-- Subject to fusion+-- -- Since 1.0.0 unlinesAscii :: (Monad m, Seq.IsSequence seq, Element seq ~ Word8) => Conduit seq m seq-unlinesAscii = concatMap (:[Seq.singleton 10])-{-# INLINE unlinesAscii #-}+INLINE_RULE0(unlinesAscii, concatMap (:[Seq.singleton 10])) --- | Convert a stream of arbitrarily-chunked textual data into a stream of data--- where each chunk represents a single line. Note that, if you have--- unknown/untrusted input, this function is /unsafe/, since it would allow an--- attacker to form lines of massive length and exhaust memory.------ Since 1.0.0-linesUnbounded :: (Monad m, Seq.IsSequence seq, Element seq ~ Char)- => Conduit seq m seq-linesUnbounded =+-- | Split a stream of arbitrarily-chunked data, based on a predicate+-- on elements. Elements that satisfy the predicate will cause chunks+-- to be split, and aren't included in these output chunks. Note+-- that, if you have unknown/untrusted input, this function is+-- /unsafe/, since it would allow an attacker to form chunks of+-- massive length and exhaust memory.+splitOnUnboundedE, splitOnUnboundedEC+ :: (Monad m, Seq.IsSequence seq)+ => (Element seq -> Bool) -> Conduit seq m seq+splitOnUnboundedEC f = start where start = await >>= maybe (return ()) loop@@ -1735,28 +1857,29 @@ Just t' -> loop (t `mappend` t') else yield x >> loop (Seq.drop 1 y) where- (x, y) = Seq.break (== '\n') t+ (x, y) = Seq.break f t+STREAMING(splitOnUnboundedE, f) +-- | Convert a stream of arbitrarily-chunked textual data into a stream of data+-- where each chunk represents a single line. Note that, if you have+-- unknown/untrusted input, this function is /unsafe/, since it would allow an+-- attacker to form lines of massive length and exhaust memory.+--+-- Subject to fusion+--+-- Since 1.0.0+linesUnbounded :: (Monad m, Seq.IsSequence seq, Element seq ~ Char)+ => Conduit seq m seq+INLINE_RULE0(linesUnbounded, splitOnUnboundedE (== '\n'))+ -- | Same as 'linesUnbounded', but for ASCII/binary data. --+-- Subject to fusion+-- -- Since 1.0.0 linesUnboundedAscii :: (Monad m, Seq.IsSequence seq, Element seq ~ Word8) => Conduit seq m seq-linesUnboundedAscii =- start- where- start = await >>= maybe (return ()) loop-- loop t =- if onull y- then do- mt <- await- case mt of- Nothing -> unless (onull t) $ yield t- Just t' -> loop (t `mappend` t')- else yield x >> loop (Seq.drop 1 y)- where- (x, y) = Seq.break (== 10) t+INLINE_RULE0(linesUnboundedAscii, splitOnUnboundedE (== 10)) -- | Generally speaking, yielding values from inside a Conduit requires -- some allocation for constructors. This can introduce an overhead,@@ -1807,7 +1930,6 @@ => ConduitM i o m () -> Sink i m r -> ConduitM i o m r-#if MIN_VERSION_conduit(1, 2, 0) onAwait (ConduitM callback) (ConduitM sink0) = ConduitM $ \rest -> let go (Done r) = rest r go (HaveOutput _ _ o) = absurd o@@ -1815,16 +1937,6 @@ go (PipeM mp) = PipeM (liftM go mp) go (Leftover f i) = Leftover (go f) i in go (sink0 Done)-#else-onAwait (ConduitM callback) =- ConduitM . go . unConduitM- where- go (Done r) = Done r- go (HaveOutput _ _ o) = absurd o- go (NeedInput f g) = callback >> NeedInput (go . f) (go . g)- go (PipeM mp) = PipeM (liftM go mp)- go (Leftover f i) = Leftover (go f) i-#endif {-# INLINE onAwait #-} yieldS :: (PrimMonad base, MonadBase base m)
Data/Conduit/Combinators/Internal.hs view
@@ -13,16 +13,24 @@ import Data.Void (absurd) import Control.Monad.Trans.Class (lift) import Control.Monad (replicateM_, forever)+import Data.Conduit.Combinators.Stream+import Data.Conduit.Internal.Fusion +-- Defines INLINE_RULE0, INLINE_RULE, STREAMING0, and STREAMING.+#include "fusion-macros.h"+ -- | Acquire the seed value and perform the given action with it n times, -- yielding each result. --+-- Subject to fusion+-- -- Since 0.2.1-initReplicate :: Monad m => m seed -> (seed -> m a) -> Int -> Producer m a-initReplicate mseed f cnt = do+initReplicate, initReplicateC :: Monad m => m seed -> (seed -> m a) -> Int -> Producer m a+initReplicateC mseed f cnt = do seed <- lift mseed replicateM_ cnt (lift (f seed) >>= yield)-{-# INLINE [1] initReplicate #-}+{-# INLINE [1] initReplicateC #-}+STREAMING(initReplicate, mseed f cnt) -- | Optimized version of initReplicate for the special case of connecting with -- a @Sink@.@@ -42,12 +50,7 @@ loop _ (HaveOutput _ _ o) = absurd o loop cnt (PipeM mp) = mp >>= loop cnt loop _ (Leftover _ i) = absurd i--#if MIN_VERSION_conduit(1, 2, 0) loop cnt0 (injectLeftovers $ sink0 Done)-#else- loop cnt0 (injectLeftovers sink0)-#endif where finish (Done r) = return r finish (HaveOutput _ _ o) = absurd o@@ -62,11 +65,15 @@ -- | Acquire the seed value and perform the given action with it forever, -- yielding each result. --+-- Subject to fusion+-- -- Since 0.2.1-initRepeat :: Monad m => m seed -> (seed -> m a) -> Producer m a-initRepeat mseed f = do+initRepeat, initRepeatC :: Monad m => m seed -> (seed -> m a) -> Producer m a+initRepeatC mseed f = do seed <- lift mseed forever $ lift (f seed) >>= yield+{-# INLINE [1] initRepeatC #-}+STREAMING(initRepeat, mseed f) -- | Optimized version of initRepeat for the special case of connecting with -- a @Sink@.@@ -84,12 +91,7 @@ loop (HaveOutput _ _ o) = absurd o loop (PipeM mp) = mp >>= loop loop (Leftover _ i) = absurd i--#if MIN_VERSION_conduit(1, 2, 0) loop (injectLeftovers (sink0 Done))-#else- loop (injectLeftovers sink0)-#endif {-# RULES "initRepeatConnect" forall mseed f sink. initRepeat mseed f $$ sink = initRepeatConnect mseed f sink
+ Data/Conduit/Combinators/Stream.hs view
@@ -0,0 +1,451 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TypeFamilies #-}+-- | These are stream fusion versions of some of the functions in+-- "Data.Conduit.Combinators". Many functions don't have stream+-- versions here because instead they have @RULES@ which inline a+-- definition that fuses.+module Data.Conduit.Combinators.Stream+ ( yieldManyS+ , repeatMS+ , repeatWhileMS+ , sourceHandleS+ , foldl1S+ , allS+ , anyS+ , sinkLazyS+ , sinkVectorS+ , sinkVectorNS+ , sinkLazyBuilderS+ , lastS+ , lastES+ , findS+ , concatMapS+ , concatMapMS+ , concatS+ , scanlS+ , scanlMS+ , intersperseS+ , slidingWindowS+ , filterMS+ , splitOnUnboundedES+ , initReplicateS+ , initRepeatS+ )+ where++-- BEGIN IMPORTS++import Control.Monad (liftM)+import Control.Monad.Base (MonadBase (liftBase))+import Control.Monad.IO.Class (MonadIO (..))+import Control.Monad.Primitive (PrimMonad)+import Data.Builder+import Data.Conduit.Internal.Fusion+import Data.Conduit.Internal.List.Stream (foldS)+import Data.IOData+import Data.Maybe (isNothing, isJust)+import Data.MonoTraversable+import Data.Monoid (Monoid (..))+import qualified Data.NonNull as NonNull+import qualified Data.Sequences as Seq+import Data.Sequences.Lazy+import qualified Data.Vector.Generic as V+import qualified Data.Vector.Generic.Mutable as VM+import Prelude+import System.IO (Handle)++-- END IMPORTS++yieldManyS :: (Monad m, MonoFoldable mono)+ => mono+ -> StreamProducer m (Element mono)+yieldManyS mono _ =+ Stream (return . step) (return (otoList mono))+ where+ step [] = Stop ()+ step (x:xs) = Emit xs x+{-# INLINE yieldManyS #-}++repeatMS :: Monad m+ => m a+ -> StreamProducer m a+repeatMS m _ =+ Stream step (return ())+ where+ step _ = liftM (Emit ()) m+{-# INLINE repeatMS #-}++repeatWhileMS :: Monad m+ => m a+ -> (a -> Bool)+ -> StreamProducer m a+repeatWhileMS m f _ =+ Stream step (return ())+ where+ step _ = do+ x <- m+ return $ if f x+ then Emit () x+ else Stop ()+{-# INLINE repeatWhileMS #-}++sourceHandleS :: (MonadIO m, IOData a) => Handle -> StreamProducer m a+sourceHandleS h _ =+ Stream step (return ())+ where+ step () = do+ x <- liftIO (hGetChunk h)+ return $ if onull x+ then Stop ()+ else Emit () x+{-# INLINE sourceHandleS #-}++foldl1S :: Monad m+ => (a -> a -> a)+ -> StreamConsumer a m (Maybe a)+foldl1S f (Stream step ms0) =+ Stream step' (liftM (Nothing, ) ms0)+ where+ step' (mprev, s) = do+ res <- step s+ return $ case res of+ Stop () -> Stop mprev+ Skip s' -> Skip (mprev, s')+ Emit s' a -> Skip (Just $ maybe a (`f` a) mprev, s')+{-# INLINE foldl1S #-}++allS :: Monad m+ => (a -> Bool)+ -> StreamConsumer a m Bool+allS f = fmapS isNothing (findS (Prelude.not . f))+{-# INLINE allS #-}++anyS :: Monad m+ => (a -> Bool)+ -> StreamConsumer a m Bool+anyS f = fmapS isJust (findS f)+{-# INLINE anyS #-}++--TODO: use a definition like+-- fmapS (fromChunks . ($ [])) <$> CL.fold (\front next -> front . (next:)) id++sinkLazyS :: (Monad m, LazySequence lazy strict)+ => StreamConsumer strict m lazy+sinkLazyS = fmapS (fromChunks . ($ [])) $ foldS (\front next -> front . (next:)) id+{-# INLINE sinkLazyS #-}++sinkVectorS :: (MonadBase base m, V.Vector v a, PrimMonad base)+ => StreamConsumer a m (v a)+sinkVectorS (Stream step ms0) = do+ Stream step' $ do+ s0 <- ms0+ mv0 <- liftBase $ VM.new initSize+ return (initSize, 0, mv0, s0)+ where+ initSize = 10+ step' (maxSize, i, mv, s) = do+ res <- step s+ case res of+ Stop () -> liftM (Stop . V.slice 0 i) $ liftBase (V.unsafeFreeze mv)+ Skip s' -> return $ Skip (maxSize, i, mv, s')+ Emit s' x -> do+ liftBase $ VM.write mv i x+ let i' = i + 1+ if i' >= maxSize+ then do+ let newMax = maxSize * 2+ mv' <- liftBase $ VM.grow mv maxSize+ return $ Skip (newMax, i', mv', s')+ else return $ Skip (maxSize, i', mv, s')+{-# INLINE sinkVectorS #-}++sinkVectorNS :: (MonadBase base m, V.Vector v a, PrimMonad base)+ => Int -- ^ maximum allowed size+ -> StreamConsumer a m (v a)+sinkVectorNS maxSize (Stream step ms0) = do+ Stream step' $ do+ s0 <- ms0+ mv0 <- liftBase $ VM.new maxSize+ return (0, mv0, s0)+ where+ step' (i, mv, _) | i >= maxSize = liftM Stop $ liftBase $ V.unsafeFreeze mv+ step' (i, mv, s) = do+ res <- step s+ case res of+ Stop () -> liftM (Stop . V.slice 0 i) $ liftBase (V.unsafeFreeze mv)+ Skip s' -> return $ Skip (i, mv, s')+ Emit s' x -> do+ liftBase $ VM.write mv i x+ let i' = i + 1+ return $ Skip (i', mv, s')+{-# INLINE sinkVectorNS #-}++sinkLazyBuilderS :: (Monad m, Monoid builder, ToBuilder a builder, Builder builder lazy)+ => StreamConsumer a m lazy+sinkLazyBuilderS = fmapS builderToLazy (foldS combiner mempty)+ where+ combiner accum = mappend accum . toBuilder+{-# INLINE sinkLazyBuilderS #-}++lastS :: Monad m+ => StreamConsumer a m (Maybe a)+lastS (Stream step ms0) =+ Stream step' (liftM (Nothing,) ms0)+ where+ step' (mlast, s) = do+ res <- step s+ return $ case res of+ Stop () -> Stop mlast+ Skip s' -> Skip (mlast, s')+ Emit s' x -> Skip (Just x, s')+{-# INLINE lastS #-}++lastES :: (Monad m, Seq.IsSequence seq)+ => StreamConsumer seq m (Maybe (Element seq))+lastES (Stream step ms0) =+ Stream step' (liftM (Nothing, ) ms0)+ where+ step' (mlast, s) = do+ res <- step s+ return $ case res of+ Stop () -> Stop (fmap NonNull.last mlast)+ Skip s' -> Skip (mlast, s')+ Emit s' (NonNull.fromNullable -> mlast'@(Just _)) -> Skip (mlast', s')+ Emit s' _ -> Skip (mlast, s')+{-# INLINE lastES #-}++findS :: Monad m+ => (a -> Bool) -> StreamConsumer a m (Maybe a)+findS f (Stream step ms0) =+ Stream step' ms0+ where+ step' s = do+ res <- step s+ return $ case res of+ Stop () -> Stop Nothing+ Skip s' -> Skip s'+ Emit s' x ->+ if f x+ then Stop (Just x)+ else Skip s'+{-# INLINE findS #-}++concatMapS :: (Monad m, MonoFoldable mono)+ => (a -> mono)+ -> StreamConduit a m (Element mono)+concatMapS f (Stream step ms0) =+ Stream step' (liftM ([], ) ms0)+ where+ step' ([], s) = do+ res <- step s+ return $ case res of+ Stop () -> Stop ()+ Skip s' -> Skip ([], s')+ Emit s' x -> Skip (otoList (f x), s')+ step' ((x:xs), s) = return (Emit (xs, s) x)+{-# INLINE concatMapS #-}++concatMapMS :: (Monad m, MonoFoldable mono)+ => (a -> m mono)+ -> StreamConduit a m (Element mono)+concatMapMS f (Stream step ms0) =+ Stream step' (liftM ([], ) ms0)+ where+ step' ([], s) = do+ res <- step s+ case res of+ Stop () -> return $ Stop ()+ Skip s' -> return $ Skip ([], s')+ Emit s' x -> do+ o <- f x+ return $ Skip (otoList o, s')+ step' ((x:xs), s) = return (Emit (xs, s) x)+{-# INLINE concatMapMS #-}++concatS :: (Monad m, MonoFoldable mono)+ => StreamConduit mono m (Element mono)+concatS = concatMapS id+{-# INLINE concatS #-}++data ScanState a s+ = ScanEnded+ | ScanContinues a s++scanlS :: Monad m => (a -> b -> a) -> a -> StreamConduit b m a+scanlS f seed0 (Stream step ms0) =+ Stream step' (liftM (ScanContinues seed0) ms0)+ where+ step' ScanEnded = return $ Stop ()+ step' (ScanContinues seed s) = do+ res <- step s+ return $ case res of+ Stop () -> Emit ScanEnded seed+ Skip s' -> Skip (ScanContinues seed s')+ Emit s' x -> Emit (ScanContinues seed' s') seed+ where+ !seed' = f seed x+{-# INLINE scanlS #-}++scanlMS :: Monad m => (a -> b -> m a) -> a -> StreamConduit b m a+scanlMS f seed0 (Stream step ms0) =+ Stream step' (liftM (ScanContinues seed0) ms0)+ where+ step' ScanEnded = return $ Stop ()+ step' (ScanContinues seed s) = do+ res <- step s+ case res of+ Stop () -> return $ Emit ScanEnded seed+ Skip s' -> return $ Skip (ScanContinues seed s')+ Emit s' x -> do+ !seed' <- f seed x+ return $ Emit (ScanContinues seed' s') seed+{-# INLINE scanlMS #-}++data IntersperseState a s+ = IFirstValue s+ | IGotValue s a+ | IEmitValue s a++intersperseS :: Monad m => a -> StreamConduit a m a+intersperseS sep (Stream step ms0) =+ Stream step' (liftM IFirstValue ms0)+ where+ step' (IFirstValue s) = do+ res <- step s+ return $ case res of+ Stop () -> Stop ()+ Skip s' -> Skip (IFirstValue s')+ Emit s' x -> Emit (IGotValue s' x) x+ -- Emit the separator once we know it's not the end of the list.+ step' (IGotValue s x) = do+ res <- step s+ return $ case res of+ Stop () -> Stop ()+ Skip s' -> Skip (IGotValue s' x)+ Emit s' x' -> Emit (IEmitValue s' x') sep+ -- We emitted a separator, now emit the value that comes after.+ step' (IEmitValue s x) = return $ Emit (IGotValue s x) x+{-# INLINE intersperseS #-}++data SlidingWindowState seq s+ = SWInitial Int seq s+ | SWSliding seq s+ | SWEarlyExit++slidingWindowS :: (Monad m, Seq.IsSequence seq, Element seq ~ a) => Int -> StreamConduit a m seq+slidingWindowS sz (Stream step ms0) =+ Stream step' (liftM (SWInitial (max 1 sz) mempty) ms0)+ where+ step' (SWInitial n st s) = do+ res <- step s+ return $ case res of+ Stop () -> Emit SWEarlyExit st+ Skip s' -> Skip (SWInitial n st s')+ Emit s' x ->+ if n == 1+ then Emit (SWSliding (Seq.unsafeTail st') s') st'+ else Skip (SWInitial (n - 1) st' s')+ where+ st' = Seq.snoc st x+ -- After collecting the initial window, each upstream element+ -- causes an additional window to be yielded.+ step' (SWSliding st s) = do+ res <- step s+ return $ case res of+ Stop () -> Stop ()+ Skip s' -> Skip (SWSliding st s')+ Emit s' x -> Emit (SWSliding (Seq.unsafeTail st') s') st'+ where+ st' = Seq.snoc st x+ step' SWEarlyExit = return $ Stop ()++{-# INLINE slidingWindowS #-}++filterMS :: Monad m+ => (a -> m Bool)+ -> StreamConduit a m a+filterMS f (Stream step ms0) = do+ Stream step' ms0+ where+ step' s = do+ res <- step s+ case res of+ Stop () -> return $ Stop ()+ Skip s' -> return $ Skip s'+ Emit s' x -> do+ r <- f x+ return $+ if r+ then Emit s' x+ else Skip s'+{-# INLINE filterMS #-}++data SplitState seq s+ = SplitDone+ -- When no element of seq passes the predicate. This allows+ -- 'splitOnUnboundedES' to not run 'Seq.break' multiple times due+ -- to 'Skip's being sent by the upstream.+ | SplitNoSep seq s+ | SplitState seq s++splitOnUnboundedES :: (Monad m, Seq.IsSequence seq)+ => (Element seq -> Bool) -> StreamConduit seq m seq+splitOnUnboundedES f (Stream step ms0) =+ Stream step' (liftM (SplitState mempty) ms0)+ where+ step' SplitDone = return $ Stop ()+ step' (SplitNoSep t s) = do+ res <- step s+ return $ case res of+ Stop () | not (onull t) -> Emit SplitDone t+ | otherwise -> Stop ()+ Skip s' -> Skip (SplitNoSep t s')+ Emit s' t' -> Skip (SplitState (t `mappend` t') s')+ step' (SplitState t s) = do+ if onull y+ then do+ res <- step s+ return $ case res of+ Stop () | not (onull t) -> Emit SplitDone t+ | otherwise -> Stop ()+ Skip s' -> Skip (SplitNoSep t s')+ Emit s' t' -> Skip (SplitState (t `mappend` t') s')+ else return $ Emit (SplitState (Seq.drop 1 y) s) x+ where+ (x, y) = Seq.break f t+{-# INLINE splitOnUnboundedES #-}++-- | Streaming versions of @Data.Conduit.Combinators.Internal.initReplicate@+initReplicateS :: Monad m => m seed -> (seed -> m a) -> Int -> StreamProducer m a+initReplicateS mseed f cnt _ =+ Stream step (liftM (cnt, ) mseed)+ where+ step (ix, _) | ix <= 0 = return $ Stop ()+ step (ix, seed) = do+ x <- f seed+ return $ Emit (ix - 1, seed) x+{-# INLINE initReplicateS #-}++-- | Streaming versions of @Data.Conduit.Combinators.Internal.initRepeat@+initRepeatS :: Monad m => m seed -> (seed -> m a) -> StreamProducer m a+initRepeatS mseed f _ =+ Stream step mseed+ where+ step seed = do+ x <- f seed+ return $ Emit seed x+{-# INLINE initRepeatS #-}++-- | Utility function+fmapS :: Monad m+ => (a -> b)+ -> StreamConduitM i o m a+ -> StreamConduitM i o m b+fmapS f s inp =+ case s inp of+ Stream step ms0 -> Stream (fmap (liftM (fmap f)) step) ms0+{-# INLINE fmapS #-}
Data/Conduit/Combinators/Unqualified.hs view
@@ -225,11 +225,6 @@ import qualified System.Posix.Directory as Dir #endif -#if MIN_VERSION_conduit(1,1,0)-import qualified Data.Conduit.Filesystem as CF-#endif-- -- END IMPORTS -- | Yield each of the values contained by the given @MonoFoldable@.
conduit-combinators.cabal view
@@ -1,5 +1,5 @@ name: conduit-combinators-version: 0.2.8.3+version: 0.3.0 synopsis: Commonly used conduit functions, for both chunked and unchunked data description: Provides a replacement for Data.Conduit.List, as well as a convenient Conduit module. homepage: https://github.com/fpco/conduit-combinators@@ -10,16 +10,17 @@ category: Data, Conduit build-type: Simple cabal-version: >=1.8-extra-source-files: test/subdir/dummyfile.txt+extra-source-files: test/subdir/dummyfile.txt fusion-macros.h ChangeLog.md library exposed-modules: Conduit Data.Conduit.Combinators Data.Conduit.Combinators.Internal+ Data.Conduit.Combinators.Stream other-modules: Data.Conduit.Combinators.Unqualified build-depends: base >= 4 && < 5 , chunked-data- , conduit >= 1.0.12+ , conduit >= 1.2.2 , conduit-extra >= 1.1.1 , transformers , transformers-base@@ -41,10 +42,13 @@ cpp-options: -DWINDOWS else build-depends: unix+ include-dirs: .+ ghc-options: -Wall -O2 -rtsopts test-suite test hs-source-dirs: test main-is: Spec.hs+ other-modules: StreamSpec type: exitcode-stdio-1.0 cpp-options: -DTEST build-depends: conduit-combinators@@ -61,6 +65,12 @@ , base16-bytestring , base64-bytestring , system-filepath+ , mtl+ , conduit+ , containers+ , safe+ , QuickCheck+ , directory ghc-options: -Wall source-repository head
+ fusion-macros.h view
@@ -0,0 +1,23 @@+#define INLINE_RULE0(new,old) ;\+ new = old ;\+ {-# INLINE [0] new #-} ;\+ {-# RULES "inline new" new = old #-}++#define INLINE_RULE(new,vars,body) ;\+ new vars = body ;\+ {-# INLINE [0] new #-} ;\+ {-# RULES "inline new" forall vars. new vars = body #-}++#define STREAMING0(name) ;\+ name = name/**/C ;\+ {-# INLINE [0] name #-} ;\+ {-# RULES "unstream name" \+ name = unstream (streamConduit name/**/C name/**/S) \+ #-}++#define STREAMING(name,vars) ;\+ name = name/**/C ;\+ {-# INLINE [0] name #-} ;\+ {-# RULES "unstream name" forall vars. \+ name vars = unstream (streamConduit (name/**/C vars) (name/**/S vars)) \+ #-}
test/Spec.hs view
@@ -39,6 +39,7 @@ import qualified Data.ByteString.Base64.URL.Lazy as B64LU import qualified Data.ByteString.Base64.URL as B64U import Control.Monad.ST (runST)+import qualified StreamSpec main :: IO () main = hspec $ do@@ -140,13 +141,13 @@ it "sourceDirectory" $ do res <- runResourceT $ sourceDirectory "test" $$ filterC (not . flip hasExtension "swp") =$ sinkList- sort res `shouldBe` ["test/Spec.hs", "test/subdir"]+ sort res `shouldBe` ["test/Spec.hs", "test/StreamSpec.hs", "test/subdir"] it "sourceDirectoryDeep" $ do res1 <- runResourceT $ sourceDirectoryDeep False "test" $$ filterC (not . flip hasExtension "swp") =$ sinkList res2 <- runResourceT $ sourceDirectoryDeep True "test" $$ filterC (not . flip hasExtension "swp") =$ sinkList- sort res1 `shouldBe` ["test/Spec.hs", "test/subdir/dummyfile.txt"]+ sort res1 `shouldBe` ["test/Spec.hs", "test/StreamSpec.hs", "test/subdir/dummyfile.txt"] sort res1 `shouldBe` sort res2 prop "drop" $ \(T.pack -> input) count -> runIdentity (yieldMany input $$ (dropC count >>= \() -> sinkList))@@ -625,6 +626,7 @@ where (y, z) = splitAt size x res `shouldBe` expected+ StreamSpec.spec evenInt :: Int -> Bool evenInt = even
+ test/StreamSpec.hs view
@@ -0,0 +1,480 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TypeFamilies #-}+module StreamSpec where++import Control.Applicative+import qualified Control.Monad+import Control.Monad (liftM)+import Control.Monad.Identity (Identity, runIdentity)+import Control.Monad.State (StateT(..), get, put)+import Data.Conduit+import Data.Conduit.Combinators+import Data.Conduit.Combinators.Internal+import Data.Conduit.Combinators.Stream+import Data.Conduit.Internal.Fusion+import Data.Conduit.Internal.List.Stream (takeS, sourceListS, mapS)+import Data.Conduit.List (consume, isolate, sourceList)+import qualified Data.List+import Data.MonoTraversable+import Data.Monoid (Monoid(..))+import qualified Data.NonNull as NonNull+import Data.Sequence (Seq)+import qualified Data.Sequences as Seq+import qualified Data.Text.Lazy as TL+import Data.Vector (Vector)+import qualified Prelude+import Prelude+ ((.), ($), (=<<), return, id, Maybe(..), Monad, Bool(..), Int,+ Eq, Show, String, Functor, fst, snd)+import qualified Safe+import System.Directory (removeFile)+import qualified System.IO as IO+import System.IO.Unsafe+import Test.Hspec+import Test.QuickCheck++spec :: Spec+spec = do+ it "sourceHandleS works" $ do+ let contents = Prelude.concat $ Prelude.replicate 10000 $ "this is some content\n"+ fp = "tmp"+ IO.writeFile fp contents+ (res, ()) <- IO.withBinaryFile "tmp" IO.ReadMode $ \h ->+ evalStream $ sourceHandleS h emptyStream+ (TL.concat res) `shouldBe` TL.pack contents+ removeFile "tmp"+ describe "Comparing list function to" $ do+ qit "yieldMany" $+ \(mono :: Seq Int) ->+ yieldMany mono `checkProducer`+ otoList mono+ qit "yieldManyS" $+ \(mono :: Seq Int) ->+ yieldManyS mono `checkStreamProducer`+ otoList mono+ qit "repeatM" $+ \(getBlind -> (f :: M Int)) ->+ repeatM f `checkInfiniteProducerM`+ repeatML f+ qit "repeatMS" $+ \(getBlind -> (f :: M Int)) ->+ repeatMS f `checkInfiniteStreamProducerM`+ repeatML f+ qit "repeatWhileM" $+ \(getBlind -> (f :: M Int), getBlind -> g) ->+ repeatWhileM f g `checkInfiniteProducerM`+ repeatWhileML f g+ qit "repeatWhileMS" $+ \(getBlind -> (f :: M Int), getBlind -> g) ->+ repeatWhileMS f g `checkInfiniteStreamProducerM`+ repeatWhileML f g+ qit "foldl1" $+ \(getBlind -> f) ->+ foldl1 f `checkConsumer`+ foldl1L f+ qit "foldl1S" $+ \(getBlind -> f) ->+ foldl1S f `checkStreamConsumer`+ foldl1L f+ qit "all" $+ \(getBlind -> f) ->+ all f `checkConsumer`+ Prelude.all f+ qit "allS" $+ \(getBlind -> f) ->+ allS f `checkStreamConsumer`+ Prelude.all f+ qit "any" $+ \(getBlind -> f) ->+ any f `checkConsumer`+ Prelude.any f+ qit "anyS" $+ \(getBlind -> f) ->+ anyS f `checkStreamConsumer`+ Prelude.any f+ qit "last" $+ \() ->+ last `checkConsumer`+ Safe.lastMay+ qit "lastS" $+ \() ->+ lastS `checkStreamConsumer`+ Safe.lastMay+ qit "lastE" $+ \(getBlind -> f) ->+ let g x = Seq.replicate (Prelude.abs (f x)) x :: Seq Int+ in (map g =$= lastE) `checkConsumer`+ (lastEL . Prelude.map g :: [Int] -> Maybe Int)+ qit "lastES" $+ \(getBlind -> f) ->+ let g x = Seq.replicate (Prelude.abs (f x)) x :: Seq Int+ in (lastES . mapS g) `checkStreamConsumer`+ (lastEL . Prelude.map g :: [Int] -> Maybe Int)+ qit "find" $+ \(getBlind -> f) ->+ find f `checkConsumer`+ Data.List.find f+ qit "findS" $+ \(getBlind -> f) ->+ findS f `checkStreamConsumer`+ Data.List.find f+ qit "concatMap" $+ \(getBlind -> (f :: Int -> Seq Int)) ->+ concatMap f `checkConduit`+ concatMapL f+ qit "concatMapS" $+ \(getBlind -> (f :: Int -> Seq Int)) ->+ concatMapS f `checkStreamConduit`+ concatMapL f+ qit "concatMapM" $+ \(getBlind -> (f :: Int -> M (Seq Int))) ->+ concatMapM f `checkConduitM`+ concatMapML f+ qit "concatMapMS" $+ \(getBlind -> (f :: Int -> M (Seq Int))) ->+ concatMapMS f `checkStreamConduitM`+ concatMapML f+ qit "concat" $+ \() ->+ concat `checkConduit`+ (concatL :: [Seq Int] -> [Int])+ qit "concatS" $+ \() ->+ concatS `checkStreamConduit`+ (concatL :: [Seq Int] -> [Int])+ qit "scanl" $+ \(getBlind -> (f :: Int -> Int -> Int), initial) ->+ scanl f initial `checkConduit`+ Prelude.scanl f initial+ qit "scanlS" $+ \(getBlind -> (f :: Int -> Int -> Int), initial) ->+ scanlS f initial `checkStreamConduit`+ Prelude.scanl f initial+ qit "scanlM" $+ \(getBlind -> (f :: Int -> Int -> M Int), initial) ->+ scanlM f initial `checkConduitM`+ scanlML f initial+ qit "scanlMS" $+ \(getBlind -> (f :: Int -> Int -> M Int), initial) ->+ scanlMS f initial `checkStreamConduitM`+ scanlML f initial+ qit "intersperse" $+ \(sep :: Int) ->+ intersperse sep `checkConduit`+ Data.List.intersperse sep+ qit "intersperseS" $+ \(sep :: Int) ->+ intersperseS sep `checkStreamConduit`+ Data.List.intersperse sep+ qit "filterM" $+ \(getBlind -> (f :: Int -> M Bool)) ->+ filterM f `checkConduitM`+ Control.Monad.filterM f+ qit "filterMS" $+ \(getBlind -> (f :: Int -> M Bool)) ->+ filterMS f `checkStreamConduitM`+ Control.Monad.filterM f+ describe "comparing normal conduit function to" $ do+ qit "slidingWindowS" $+ \(getSmall -> n) ->+ slidingWindowS n `checkStreamConduit`+ (\xs -> runIdentity $+ sourceList xs $= preventFusion (slidingWindow n) $$ consume+ :: [Seq Int])+ qit "splitOnUnboundedES" $+ \(getBlind -> (f :: Int -> Bool)) ->+ splitOnUnboundedES f `checkStreamConduit`+ (\xs -> runIdentity $+ sourceList xs $= preventFusion (splitOnUnboundedE f) $$ consume+ :: [Seq Int])+ qit "initReplicateS" $+ \(getBlind -> (mseed :: M Int), getBlind -> (f :: Int -> M Int), getSmall -> cnt) ->+ initReplicateS mseed f cnt `checkStreamProducerM`+ (preventFusion (initReplicate mseed f cnt) $$ consume)+ qit "initRepeatS" $+ \(getBlind -> (mseed :: M Int), getBlind -> (f :: Int -> M Int)) ->+ initRepeatS mseed f `checkInfiniteStreamProducerM`+ (preventFusion (initRepeat mseed f) $= take 10 $$ consume)+ qit "sinkVectorS" $+ \() -> checkStreamConsumerM'+ unsafePerformIO+ (sinkVectorS :: forall o. StreamConduitM Int o IO.IO (Vector Int))+ (\xs -> sourceList xs $$ preventFusion sinkVector)+ qit "sinkVectorNS" $+ \(getSmall . getNonNegative -> n) -> checkStreamConsumerM'+ unsafePerformIO+ (sinkVectorNS n :: forall o. StreamConduitM Int o IO.IO (Vector Int))+ (\xs -> sourceList xs $$ preventFusion (sinkVectorN n))++instance Arbitrary a => Arbitrary (Seq a) where+ arbitrary = Seq.fromList <$> arbitrary++repeatML :: Monad m => m a -> m [a]+repeatML = Prelude.sequence . Prelude.repeat++repeatWhileML :: Monad m => m a -> (a -> Bool) -> m [a]+repeatWhileML m f = go+ where+ go = do+ x <- m+ if f x+ then liftM (x:) go+ else return []++foldl1L :: (a -> a -> a) -> [a] -> Maybe a+foldl1L _ [] = Nothing+foldl1L f xs = Just $ Prelude.foldl1 f xs++lastEL :: Seq.IsSequence seq+ => [seq] -> Maybe (Element seq)+lastEL = Prelude.foldl go Nothing+ where+ go _ (NonNull.fromNullable -> Just l) = Just (NonNull.last l)+ go mlast _ = mlast++concatMapL :: MonoFoldable mono+ => (a -> mono) -> [a] -> [Element mono]+concatMapL f = Prelude.concatMap (otoList . f)++concatMapML :: (Monad m, MonoFoldable mono)+ => (a -> m mono) -> [a] -> m [Element mono]+concatMapML f = liftM (Prelude.concatMap otoList) . Prelude.mapM f++concatL :: MonoFoldable mono+ => [mono] -> [Element mono]+concatL = Prelude.concatMap otoList++scanlML :: Monad m => (a -> b -> m a) -> a -> [b] -> m [a]+scanlML f = go+ where+ go l [] = return [l]+ go l (r:rs) = do+ l' <- f l r+ liftM (l:) (go l' rs)++--FIXME: the following code is directly copied from the conduit test+--suite. How to share this code??++qit :: (Arbitrary a, Testable prop, Show a)+ => String -> (a -> prop) -> Spec+qit n f = it n $ property $ forAll arbitrary f++--------------------------------------------------------------------------------+-- Quickcheck utilities for pure conduits / streams++checkProducer :: (Show a, Eq a) => Source Identity a -> [a] -> Property+checkProducer c l = checkProducerM' runIdentity c (return l)++checkStreamProducer :: (Show a, Eq a) => StreamSource Identity a -> [a] -> Property+checkStreamProducer s l = checkStreamProducerM' runIdentity s (return l)++checkInfiniteProducer :: (Show a, Eq a) => Source Identity a -> [a] -> Property+checkInfiniteProducer c l = checkInfiniteProducerM' runIdentity c (return l)++checkInfiniteStreamProducer :: (Show a, Eq a) => StreamSource Identity a -> [a] -> Property+checkInfiniteStreamProducer s l = checkInfiniteStreamProducerM' runIdentity s (return l)++checkConsumer :: (Show b, Eq b) => Consumer Int Identity b -> ([Int] -> b) -> Property+checkConsumer c l = checkConsumerM' runIdentity c (return . l)++checkStreamConsumer :: (Show b, Eq b) => StreamConsumer Int Identity b -> ([Int] -> b) -> Property+checkStreamConsumer c l = checkStreamConsumerM' runIdentity c (return . l)++checkConduit :: (Show a, Arbitrary a, Show b, Eq b) => Conduit a Identity b -> ([a] -> [b]) -> Property+checkConduit c l = checkConduitM' runIdentity c (return . l)++checkStreamConduit :: (Show a, Arbitrary a, Show b, Eq b) => StreamConduit a Identity b -> ([a] -> [b]) -> Property+checkStreamConduit c l = checkStreamConduitM' runIdentity c (return . l)++-- checkConduitResult :: (Show a, Arbitrary a, Show b, Eq b, Show r, Eq r) => ConduitM a b Identity r -> ([a] -> ([b], r)) -> Property+-- checkConduitResult c l = checkConduitResultM' runIdentity c (return . l)++checkStreamConduitResult :: (Show a, Arbitrary a, Show b, Eq b, Show r, Eq r) => StreamConduitM a b Identity r -> ([a] -> ([b], r)) -> Property+checkStreamConduitResult c l = checkStreamConduitResultM' runIdentity c (return . l)++--------------------------------------------------------------------------------+-- Quickcheck utilities for conduits / streams in the M monad.++checkProducerM :: (Show a, Eq a) => Source M a -> M [a] -> Property+checkProducerM = checkProducerM' runM++checkStreamProducerM :: (Show a, Eq a) => StreamSource M a -> M [a] -> Property+checkStreamProducerM = checkStreamProducerM' runM++checkInfiniteProducerM :: (Show a, Eq a) => Source M a -> M [a] -> Property+checkInfiniteProducerM = checkInfiniteProducerM' (fst . runM)++checkInfiniteStreamProducerM :: (Show a, Eq a) => StreamSource M a -> M [a] -> Property+checkInfiniteStreamProducerM = checkInfiniteStreamProducerM' (fst . runM)++checkConsumerM :: (Show b, Eq b) => Consumer Int M b -> ([Int] -> M b) -> Property+checkConsumerM = checkConsumerM' runM++checkStreamConsumerM :: (Show b, Eq b) => StreamConsumer Int M b -> ([Int] -> M b) -> Property+checkStreamConsumerM = checkStreamConsumerM' runM++checkConduitM :: (Show a, Arbitrary a, Show b, Eq b) => Conduit a M b -> ([a] -> M [b]) -> Property+checkConduitM = checkConduitM' runM++checkStreamConduitM :: (Show a, Arbitrary a, Show b, Eq b) => StreamConduit a M b -> ([a] -> M [b]) -> Property+checkStreamConduitM = checkStreamConduitM' runM++-- checkConduitResultM :: (Show a, Arbitrary a, Show b, Eq b, Show r, Eq r) => ConduitM a b M r -> ([a] -> M ([b], r)) -> Property+-- checkConduitResultM = checkConduitResultM' runM++checkStreamConduitResultM :: (Show a, Arbitrary a, Show b, Eq b, Show r, Eq r) => StreamConduitM a b M r -> ([a] -> M ([b], r)) -> Property+checkStreamConduitResultM = checkStreamConduitResultM' runM++--------------------------------------------------------------------------------+-- Quickcheck utilities for monadic streams / conduits+-- These are polymorphic in which Monad is used.++checkProducerM' :: (Show a, Monad m, Show b, Eq b)+ => (m [a] -> b)+ -> Source m a+ -> m [a]+ -> Property+checkProducerM' f c l =+ f (preventFusion c $$ consume)+ ===+ f l++checkStreamProducerM' :: (Show a, Monad m, Show b, Eq b)+ => (m [a] -> b)+ -> StreamSource m a+ -> m [a]+ -> Property+checkStreamProducerM' f s l =+ f (liftM fst $ evalStream $ s emptyStream)+ ===+ f l++checkInfiniteProducerM' :: (Show a, Monad m, Show b, Eq b)+ => (m [a] -> b)+ -> Source m a+ -> m [a]+ -> Property+checkInfiniteProducerM' f s l =+ checkProducerM' f+ (preventFusion s $= isolate 10)+ (liftM (Prelude.take 10) l)++checkInfiniteStreamProducerM' :: (Show a, Monad m, Show b, Eq b)+ => (m [a] -> b)+ -> StreamSource m a+ -> m [a]+ -> Property+checkInfiniteStreamProducerM' f s l =+ f (liftM snd $ evalStream $ takeS 10 $ s emptyStream)+ ===+ f (liftM (Prelude.take 10) l)++checkConsumerM' :: (Show a, Monad m, Show b, Eq b)+ => (m a -> b)+ -> Consumer Int m a+ -> ([Int] -> m a)+ -> Property+checkConsumerM' f c l = forAll arbitrary $ \xs ->+ f (sourceList xs $$ preventFusion c)+ ===+ f (l xs)++checkStreamConsumerM' :: (Show a, Monad m, Show b, Eq b)+ => (m a -> b)+ -> StreamConsumer Int m a+ -> ([Int] -> m a)+ -> Property+checkStreamConsumerM' f s l = forAll arbitrary $ \xs ->+ f (liftM snd $ evalStream $ s $ sourceListS xs emptyStream)+ ===+ f (l xs)++checkConduitM' :: (Show a, Arbitrary a, Monad m, Show c, Eq c)+ => (m [b] -> c)+ -> Conduit a m b+ -> ([a] -> m [b])+ -> Property+checkConduitM' f c l = forAll arbitrary $ \xs ->+ f (sourceList xs $= preventFusion c $$ consume)+ ===+ f (l xs)++checkStreamConduitM' :: (Show a, Arbitrary a, Monad m, Show c, Eq c)+ => (m [b] -> c)+ -> StreamConduit a m b+ -> ([a] -> m [b])+ -> Property+checkStreamConduitM' f s l = forAll arbitrary $ \xs ->+ f (liftM fst $ evalStream $ s $ sourceListS xs emptyStream)+ ===+ f (l xs)++-- TODO: Fixing this would allow comparing conduit consumers against+-- their list versions.+--+-- checkConduitResultM' :: (Show a, Arbitrary a, Monad m, Show c, Eq c)+-- => (m ([b], r) -> c)+-- -> ConduitM a b m r+-- -> ([a] -> m ([b], r))+-- -> Property+-- checkConduitResultM' f c l = FIXME forAll arbitrary $ \xs ->+-- f (sourceList xs $= preventFusion c $$ consume)+-- ===+-- f (l xs)++checkStreamConduitResultM' :: (Show a, Arbitrary a, Monad m, Show c, Eq c)+ => (m ([b], r) -> c)+ -> StreamConduitM a b m r+ -> ([a] -> m ([b], r))+ -> Property+checkStreamConduitResultM' f s l = forAll arbitrary $ \xs ->+ f (evalStream $ s $ sourceListS xs emptyStream)+ ===+ f (l xs)++emptyStream :: Monad m => Stream m () ()+emptyStream = Stream (\_ -> return $ Stop ()) (return ())++evalStream :: Monad m => Stream m o r -> m ([o], r)+evalStream (Stream step s0) = go =<< s0+ where+ go s = do+ res <- step s+ case res of+ Stop r -> return ([], r)+ Skip s' -> go s'+ Emit s' x -> liftM (\(l, r) -> (x:l, r)) (go s')++--------------------------------------------------------------------------------+-- Misc utilities++-- Prefer this to creating an orphan instance for Data.Monoid.Sum:++newtype Sum a = Sum a+ deriving (Eq, Show, Arbitrary)++instance Prelude.Num a => Monoid (Sum a) where+ mempty = Sum 0+ mappend (Sum x) (Sum y) = Sum $ x Prelude.+ y++preventFusion :: a -> a+preventFusion = id+{-# INLINE [0] preventFusion #-}++newtype M a = M (StateT Int Identity a)+ deriving (Functor, Applicative, Monad)++instance Arbitrary a => Arbitrary (M a) where+ arbitrary = do+ f <- arbitrary+ return $ do+ s <- M get+ let (x, s') = f s+ M (put s')+ return x++runM :: M a -> (a, Int)+runM (M m) = runIdentity $ runStateT m 0