conduit-combinators 0.2.0.1 → 0.2.1
raw patch · 5 files changed
+256/−3 lines, 5 filesdep +mwc-randomdep +void
Dependencies added: mwc-random, void
Files
- Data/Conduit/Combinators.hs +65/−1
- Data/Conduit/Combinators/Internal.hs +87/−0
- Data/Conduit/Combinators/Unqualified.hs +65/−1
- conduit-combinators.cabal +5/−1
- test/Spec.hs +34/−0
Data/Conduit/Combinators.hs view
@@ -39,6 +39,12 @@ , sourceIOHandle , stdin + -- ** Random numbers+ , sourceRandom+ , sourceRandomN+ , sourceRandomGen+ , sourceRandomNGen+ -- * Consumers -- ** Pure , drop@@ -78,6 +84,8 @@ , lastE , length , lengthE+ , lengthIf+ , lengthIfE , maximum , maximumE , minimum@@ -160,7 +168,7 @@ import Control.Monad (unless, when, (>=>), liftM, forever) import Control.Monad.Base (MonadBase (liftBase)) import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Primitive (PrimMonad)+import Control.Monad.Primitive (PrimMonad, PrimState) import Control.Monad.Trans.Class (lift) import Data.Conduit import qualified Data.Conduit.List as CL@@ -185,6 +193,8 @@ import qualified Data.Conduit.Text as CT import Data.ByteString (ByteString) import Data.Text (Text)+import qualified System.Random.MWC as MWC+import Data.Conduit.Combinators.Internal -- END IMPORTS @@ -340,6 +350,45 @@ stdin :: (MonadIO m, IOData a) => Producer m a stdin = sourceHandle SIO.stdin +-- | Create an infinite stream of random values, seeding from the system random+-- number.+--+-- Since 1.0.0+sourceRandom :: (MWC.Variate a, MonadIO m) => Producer m a+sourceRandom = initRepeat (liftIO MWC.createSystemRandom) (liftIO . MWC.uniform)+{-# INLINE sourceRandom #-}++-- | Create a stream of random values of length n, seeding from the system+-- random number.+--+-- 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 #-}++-- | Create an infinite stream of random values, using the given random number+-- generator.+--+-- 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 #-}++-- | Create a stream of random values of length n, seeding from the system+-- random number.+--+-- 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 #-}+ -- | Ignore a certain number of values in the stream. -- -- Since 1.0.0@@ -774,6 +823,21 @@ lengthE :: (Monad m, Num len, MonoFoldable mono) => Consumer mono m len lengthE = foldl (\x y -> x + fromIntegral (olength y)) 0 {-# INLINE lengthE #-}++-- | Count how many values in the stream pass the given predicate.+--+-- 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 #-}++-- | Count how many elements in the chunked stream pass the given predicate.+--+-- 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 #-} -- | Get the largest value in the stream, if present. --
+ Data/Conduit/Combinators/Internal.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE RankNTypes #-}+-- | Internal helper functions, usually used for rewrite rules.+module Data.Conduit.Combinators.Internal+ ( initReplicate+ , initReplicateConnect+ , initRepeat+ , initRepeatConnect+ ) where++import Data.Conduit+import Data.Conduit.Internal (ConduitM (..), Pipe (..), injectLeftovers)+import Data.Void (absurd)+import Control.Monad.Trans.Class (lift)+import Control.Monad (replicateM_, forever)++-- | Acquire the seed value and perform the given action with it n times,+-- yielding each result.+--+-- Since 0.2.1+initReplicate :: Monad m => m seed -> (seed -> m a) -> Int -> Producer m a+initReplicate mseed f cnt = do+ seed <- lift mseed+ replicateM_ cnt (lift (f seed) >>= yield)+{-# INLINE [1] initReplicate #-}++-- | Optimized version of initReplicate for the special case of connecting with+-- a @Sink@.+--+-- Since 0.2.1+initReplicateConnect :: Monad m+ => m seed+ -> (seed -> m a)+ -> Int+ -> Sink a m b+ -> m b+initReplicateConnect mseed f cnt0 (ConduitM sink0) = do+ seed <- mseed+ let loop cnt sink | cnt <= 0 = finish sink+ loop _ (Done r) = return r+ loop cnt (NeedInput p _) = f seed >>= loop (pred cnt) . p+ loop _ (HaveOutput _ _ o) = absurd o+ loop cnt (PipeM mp) = mp >>= loop cnt+ loop _ (Leftover _ i) = absurd i++ loop cnt0 (injectLeftovers sink0)+ where+ finish (Done r) = return r+ finish (HaveOutput _ _ o) = absurd o+ finish (NeedInput _ p) = finish (p ())+ finish (PipeM mp) = mp >>= finish+ finish (Leftover _ i) = absurd i+{-# RULES "initReplicateConnect" forall mseed f cnt sink.+ initReplicate mseed f cnt $$ sink+ = initReplicateConnect mseed f cnt sink+ #-}++-- | Acquire the seed value and perform the given action with it forever,+-- yielding each result.+--+-- Since 0.2.1+initRepeat :: Monad m => m seed -> (seed -> m a) -> Producer m a+initRepeat mseed f = do+ seed <- lift mseed+ forever $ lift (f seed) >>= yield++-- | Optimized version of initRepeat for the special case of connecting with+-- a @Sink@.+--+-- Since 0.2.1+initRepeatConnect :: Monad m+ => m seed+ -> (seed -> m a)+ -> Sink a m b+ -> m b+initRepeatConnect mseed f (ConduitM sink0) = do+ seed <- mseed+ let loop (Done r) = return r+ loop (NeedInput p _) = f seed >>= loop . p+ loop (HaveOutput _ _ o) = absurd o+ loop (PipeM mp) = mp >>= loop+ loop (Leftover _ i) = absurd i++ loop (injectLeftovers sink0)+{-# RULES "initRepeatConnect" forall mseed f sink.+ initRepeat mseed f $$ sink+ = initRepeatConnect mseed f sink+ #-}
Data/Conduit/Combinators/Unqualified.hs view
@@ -28,6 +28,12 @@ , sourceIOHandle , stdinC + -- *** Random numbers+ , sourceRandom+ , sourceRandomN+ , sourceRandomGen+ , sourceRandomNGen+ -- ** Consumers -- *** Pure , dropC@@ -67,6 +73,8 @@ , lastCE , lengthC , lengthCE+ , lengthIfC+ , lengthIfCE , maximumC , maximumCE , minimumC@@ -152,7 +160,7 @@ import Control.Monad (unless, when, (>=>), liftM, forever) import Control.Monad.Base (MonadBase (liftBase)) import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.Primitive (PrimMonad)+import Control.Monad.Primitive (PrimMonad, PrimState) import Control.Monad.Trans.Class (lift) import Data.Conduit import qualified Data.Conduit.List as CL@@ -177,6 +185,8 @@ import qualified Data.Conduit.Text as CT import Data.ByteString (ByteString) import Data.Text (Text)+import qualified System.Random.MWC as MWC+import Data.Conduit.Combinators.Internal -- END IMPORTS@@ -314,6 +324,45 @@ stdinC = CC.stdin {-# INLINE stdinC #-} +-- | Create an infinite stream of random values, seeding from the system random+-- number.+--+-- Since 1.0.0+sourceRandom :: (MWC.Variate a, MonadIO m) => Producer m a+sourceRandom = CC.sourceRandom+{-# INLINE sourceRandom #-}++-- | Create a stream of random values of length n, seeding from the system+-- random number.+--+-- Since 1.0.0+sourceRandomN :: (MWC.Variate a, MonadIO m)+ => Int -- ^ count+ -> Producer m a+sourceRandomN = CC.sourceRandomN+{-# INLINE sourceRandomN #-}++-- | Create an infinite stream of random values, using the given random number+-- generator.+--+-- Since 1.0.0+sourceRandomGen :: (MWC.Variate a, MonadBase base m, PrimMonad base)+ => MWC.Gen (PrimState base)+ -> Producer m a+sourceRandomGen = CC.sourceRandomGen+{-# INLINE sourceRandomGen #-}++-- | Create a stream of random values of length n, seeding from the system+-- random number.+--+-- Since 1.0.0+sourceRandomNGen :: (MWC.Variate a, MonadBase base m, PrimMonad base)+ => MWC.Gen (PrimState base)+ -> Int -- ^ count+ -> Producer m a+sourceRandomNGen = CC.sourceRandomNGen+{-# INLINE sourceRandomNGen #-}+ -- | Ignore a certain number of values in the stream. -- -- Since 1.0.0@@ -662,6 +711,21 @@ lengthCE :: (Monad m, Num len, MonoFoldable mono) => Consumer mono m len lengthCE = CC.lengthE {-# INLINE lengthCE #-}++-- | Count how many values in the stream pass the given predicate.+--+-- Since 1.0.0+lengthIfC :: (Monad m, Num len) => (a -> Bool) -> Consumer a m len+lengthIfC = CC.lengthIf+{-# INLINE lengthIfC #-}++-- | Count how many elements in the chunked stream pass the given predicate.+--+-- Since 1.0.0+lengthIfCE :: (Monad m, Num len, MonoFoldable mono)+ => (Element mono -> Bool) -> Consumer mono m len+lengthIfCE = CC.lengthIfE+{-# INLINE lengthIfCE #-} -- | Get the largest value in the stream, if present. --
conduit-combinators.cabal view
@@ -1,5 +1,5 @@ name: conduit-combinators-version: 0.2.0.1+version: 0.2.1 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@@ -14,6 +14,7 @@ library exposed-modules: Conduit Data.Conduit.Combinators+ Data.Conduit.Combinators.Internal other-modules: Data.Conduit.Combinators.Unqualified build-depends: base >= 4 && < 5 , chunked-data@@ -27,6 +28,8 @@ , system-filepath , text , bytestring+ , void+ , mwc-random test-suite test hs-source-dirs: test@@ -44,6 +47,7 @@ , mono-traversable , silently , bytestring+ , mwc-random ghc-options: -Wall source-repository head
test/Spec.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-type-defaults #-} import Conduit+import Data.Conduit.Combinators.Internal import Test.Hspec import Test.Hspec.QuickCheck import BasicPrelude hiding (encodeUtf8)@@ -23,6 +24,7 @@ import GHC.IO.Handle (hDuplicateTo) import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as S8+import System.Random.MWC (createSystemRandom) main :: IO () main = hspec $ do@@ -107,6 +109,20 @@ hDuplicateTo h IO.stdin x <- stdinC $$ foldC x `shouldBe` content+ it "sourceRandom" $ do+ x <- sourceRandom $$ takeC 100 =$ sumC :: IO Double+ x `shouldSatisfy` (\y -> y > 10 && y < 90)+ it "sourceRandomN" $ do+ x <- sourceRandomN 100 $$ sumC :: IO Double+ x `shouldSatisfy` (\y -> y > 10 && y < 90)+ it "sourceRandomGen" $ do+ gen <- createSystemRandom+ x <- sourceRandomGen gen $$ takeC 100 =$ sumC :: IO Double+ x `shouldSatisfy` (\y -> y > 10 && y < 90)+ it "sourceRandomNGen" $ do+ gen <- createSystemRandom+ x <- sourceRandomNGen gen 100 $$ sumC :: IO Double+ x `shouldSatisfy` (\y -> y > 10 && y < 90) prop "drop" $ \(T.pack -> input) count -> runIdentity (yieldMany input $$ (dropC count >>= \() -> sinkList)) `shouldBe` T.unpack (T.drop count input)@@ -215,6 +231,12 @@ prop "lengthE" $ \xs -> runIdentity (yieldMany xs $$ lengthCE) `shouldBe` length (concat xs :: [Int])+ prop "lengthIf" $ \x xs ->+ runIdentity (yieldMany xs $$ lengthIfC (< x))+ `shouldBe` length (filter (< x) xs :: [Int])+ prop "lengthIfE" $ \x xs ->+ runIdentity (yieldMany xs $$ lengthIfCE (< x))+ `shouldBe` length (filter (< x) (concat xs) :: [Int]) prop "maximum" $ \xs -> runIdentity (yieldMany xs $$ maximumC) `shouldBe` (if null (xs :: [Int]) then Nothing else Just (maximum xs))@@ -491,6 +513,18 @@ prop "linesUnboundedAscii" $ \(map S.pack -> input) -> runIdentity (yieldMany input $$ (linesUnboundedAsciiC >>= \() -> mempty) =$ sinkList) `shouldBe` S8.lines (S.concat input)+ prop "initReplicate" $ \seed delta (min 50 . abs -> cnt) -> do+ let sink = sumC+ res1 <- initReplicate (return seed) (return . (+ delta)) cnt $$ sink+ res1 `shouldBe` cnt * (seed + delta)+ res2 <- initReplicateConnect (return seed) (return . (+ delta)) cnt sink+ res2 `shouldBe` res1+ prop "initReplicate" $ \seed delta (min 50 . abs -> cnt) -> do+ let sink = takeC cnt =$ sumC+ res1 <- initRepeat (return seed) (return . (+ delta)) $$ sink+ res1 `shouldBe` cnt * (seed + delta)+ res2 <- initRepeatConnect (return seed) (return . (+ delta)) sink+ res2 `shouldBe` res1 evenInt :: Int -> Bool evenInt = even