on master help sign in
Review of #138 (Prelude functions) and other changes

APIs ---- Removed: merge lazy left scans: scanl, scanlM, scanl1, scanl1M Renamed: generate and generateM renamed to fromIndices and fromIndicesM Added: replicate mergeByM, mergeAsyncBy, mergeAsyncByM `intFrom`, `intFromTo`, `intFromThen`, `intFromThenTo`, `intFromStep`, `fracFrom`, `fracFromThen`, `fracFromThenTo`, `numFromStep` Added StreamD version of replicateM and a rewrite rule for replicateMSerial. Added but not exposed: postscanl and prescanl ops Rewrote mergeByS in StreamK, simplified quite a bit and got some perf improvement too. Added @since notations to new APIs. Fixed lines exceeding 80 columns. Tests ----- Added tests for the new enumeration APIs. Improved some tests by generating values randomly using quickcheck forAll. We can improve more tests similarly. Removed some redundant transformOps tests. reorganized test code in groups so as to keep similar functionality together and added header lines so that we can find relevant code easily. Benchmarks ---------- Added benchmarks for enumeration primitives added above. Added benchmarks for scan and fold mixed ops. Added benchmark for concatMap. Fixed foldr and foldrM benchmarks to use a (+) operation instead of a list operation for fair comparision with other folds. Kept only one benchmark each for deleteBy, insertBy, isPrefixOf and isSubsequenceOf. Documentation ------------- Updated documentation, added examples for the new primitives as well as many old ones. Especially the documentation of folds and scans was rewritten. Reordered and re-organized the groups of APIs in the doc. Refactoring ----------- Some related and urelated refactoring. Hlint ----- Fixed some hlint hints introduced recently. TBD --- Some APIs need concurrent versions. I have added "XXX" notes for those. Some more tests have to be added. Some more benchmarks have to be added.

commitdbc1956141b92f40f4575a52f652e689abed0a7f authorHarendra Kumar <harendra.kumar@gmail.com> dateMon 3 Dec 2018, 16:32 (8 years ago) parentde083af
12 files changed+1764−682
Changelog.md+17−5 benchmark/Linear.hs+22−24 benchmark/LinearOps.hs+73−58 src/Streamly/Prelude.hs+1011−247 src/Streamly/Streams/Instances.hs+2−3 src/Streamly/Streams/Prelude.hs+7−3 src/Streamly/Streams/Serial.hs+11−1 src/Streamly/Streams/StreamD.hs+253−29 src/Streamly/Streams/StreamK.hs+67−51 src/Streamly/Streams/Zip.hs+4−4 src/Streamly/Tutorial.hs+4−6 test/Prop.hs+293−251
Changelog.md+17−5
@@ -1,12 +1,24 @@
11## Unreleased
22
3+### Breaking changes
4+
5+* 'Monad' constraint may be needed on some of the existing APIs.
6+
7+### Enhancements
8+
39* Add benchmarks to measure composed and iterated operations
410* Performance improvements
5−* Add `(!!)`, `insertBy`, `mergeBy`, `merge`, `the`, `maximumBy`,
6− `minimumBy`, `scanl1`, `scanl1M`, `scanl1'`, `scanl1M'`, `scanl`, `scanlM`,
7− `deleteBy`, `isPrefixOf`, `isSubsequenceOf`, `stripPrefix`,
8− `replicate`, `generateM`, `generate`, `concatMap`, `concatMapM`,
9− `findM`, `uniq`, `indexed`, `indexedR` to Prelude.
11+* Add the following functions to Prelude:
12+ * Generation: `replicate`, `fromIndices`, `fromIndicesM`
13+ * Enumeration: `intFrom`, `intFromTo`, `intFromThen`, `intFromThenTo`,
14+ `intFromStep`, `fracFrom`, `fracFromThen`, `fracFromThenTo`,
15+ `numFromStep`
16+ * Folds: `(!!)`, `maximumBy`, `minimumBy`, `the`
17+ * Scans: `scanl1'`, `scanl1M'
18+ * Filters: `uniq`, `insertBy`, `deleteBy`, `findM`
19+ * Multi-stream: `eqBy`, `cmpBy`, `mergeBy`, `mergeByM`, `mergeAsyncBy`,
20+ `mergeAsyncByM`, `isPrefixOf`, `isSubsequenceOf`, `stripPrefix`,
21+ `concatMap`, `concatMapM`, `indexed`, `indexedR`
1022
1123## 0.5.2
1224
benchmark/Linear.hs+22−24
@@ -29,12 +29,12 @@ benchIOSink name f = bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source
2929-- | Takes a source, and uses it with a default drain/fold method.
3030{-# INLINE benchIOSrc #-}
3131benchIOSrc
32− :: (t IO Int -> SerialT IO Int)
32+ :: (t IO a -> SerialT IO a)
3333 -> String
34− -> (Int -> t IO Int)
34+ -> (Int -> t IO a)
3535 -> Benchmark
3636benchIOSrc t name f =
37− bench name $ nfIO $ randomRIO (1,1) >>= (Ops.toNull t) . f
37+ bench name $ nfIO $ randomRIO (1,1) >>= Ops.toNull t . f
3838
3939{-# INLINE benchPure #-}
4040benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
@@ -84,6 +84,11 @@ main =
8484 [ -- Most basic, barely stream continuations running
8585 benchIOSrc serially "unfoldr" Ops.sourceUnfoldr
8686 , benchIOSrc serially "unfoldrM" Ops.sourceUnfoldrM
87+ , benchIOSrc serially "intFromTo" Ops.sourceIntFromTo
88+ , benchIOSrc serially "intFromThenTo" Ops.sourceIntFromThenTo
89+ , benchIOSrc serially "integerFromStep" Ops.sourceIntegerFromStep
90+ , benchIOSrc serially "fracFromThenTo" Ops.sourceFracFromThenTo
91+ , benchIOSrc serially "fracFromTo" Ops.sourceFracFromTo
8792 , benchIOSrc serially "fromList" Ops.sourceFromList
8893 , benchIOSrc serially "fromListM" Ops.sourceFromListM
8994 -- These are essentially cons and consM
@@ -127,7 +132,7 @@ main =
127132 ]
128133 , bgroup "transformation"
129134 [ benchIOSink "scan" (Ops.scan 1)
130− , benchIOSink "scanl1" (Ops.scanl1 1)
135+ , benchIOSink "scanl1'" (Ops.scanl1' 1)
131136 , benchIOSink "map" (Ops.map 1)
132137 , benchIOSink "fmap" (Ops.fmap 1)
133138 , benchIOSink "mapM" (Ops.mapM serially 1)
@@ -137,15 +142,10 @@ main =
137142 Ops.sequence serially (Ops.sourceUnfoldrMAction n)
138143 , benchIOSink "findIndices" (Ops.findIndices 1)
139144 , benchIOSink "elemIndices" (Ops.elemIndices 1)
140− -- , benchIOSink "concat" Ops.concat
141− , benchIOSink "insertLarge" (Ops.insertLarge 1)
142− , benchIOSink "insertSmall" (Ops.insertSmall 1)
143− , benchIOSink "deleteLarge" (Ops.deleteLarge 1)
144− , benchIOSink "deleteSmall" (Ops.deleteSmall 1)
145145 ]
146146 , bgroup "transformationX4"
147147 [ benchIOSink "scan" (Ops.scan 4)
148− , benchIOSink "scanl1" (Ops.scanl1 4)
148+ , benchIOSink "scanl1'" (Ops.scanl1' 4)
149149 , benchIOSink "map" (Ops.map 4)
150150 , benchIOSink "fmap" (Ops.fmap 4)
151151 , benchIOSink "mapM" (Ops.mapM serially 4)
@@ -155,11 +155,6 @@ main =
155155 -- Ops.sequence serially (Ops.sourceUnfoldrMAction n)
156156 , benchIOSink "findIndices" (Ops.findIndices 4)
157157 , benchIOSink "elemIndices" (Ops.elemIndices 4)
158− , benchIOSink "insertLarge" (Ops.insertLarge 4)
159− , benchIOSink "insertSmall" (Ops.insertSmall 4)
160− , benchIOSink "deleteLarge" (Ops.deleteLarge 4)
161− , benchIOSink "deleteSmall" (Ops.deleteSmall 4)
162− -- , benchIOSink "concat" Ops.concat
163158 ]
164159 , bgroup "filtering"
165160 [ benchIOSink "filter-even" (Ops.filterEven 1)
@@ -173,6 +168,8 @@ main =
173168 , benchIOSink "dropWhile-true" (Ops.dropWhileTrue 1)
174169 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 1)
175170 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 1)
171+ , benchIOSink "deleteBy" (Ops.deleteBy 1)
172+ , benchIOSink "insertBy" (Ops.insertBy 1)
176173 ]
177174 , bgroup "filteringX4"
178175 [ benchIOSink "filter-even" (Ops.filterEven 4)
@@ -186,23 +183,24 @@ main =
186183 , benchIOSink "dropWhile-true" (Ops.dropWhileTrue 4)
187184 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 4)
188185 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 4)
186+ , benchIOSink "deleteBy" (Ops.deleteBy 4)
187+ , benchIOSink "insertBy" (Ops.insertBy 4)
189188 ]
190− , bgroup "zipping"
189+ , bgroup "multi-stream"
191190 [ benchIOSink "eqBy" Ops.eqBy
192191 , benchIOSink "cmpBy" Ops.cmpBy
193192 , benchIOSink "zip" Ops.zip
194193 , benchIOSink "zipM" Ops.zipM
195− ]
196− , bgroup "merging"
197− [ benchIOSink "merge" Ops.merge
198− ]
199− , bgroup "subsequences"
200− [ benchIOSink "isPrefixOf" Ops.isPrefixOf
201− , benchIOSink "isPrefixAll" Ops.isPrefixAll
194+ , benchIOSink "mergeBy" Ops.mergeBy
195+ , benchIOSink "isPrefixOf" Ops.isPrefixOf
202196 , benchIOSink "isSubsequenceOf" Ops.isSubsequenceOf
203− , benchIOSink "evenSubsequence" Ops.evenSubsequence
204197 , benchIOSink "stripPrefix" Ops.stripPrefix
198+ , benchIOSrc serially "concatMap" Ops.concatMap
205199 ]
200+ , bgroup "mixed"
201+ [ benchIOSink "sum-product-fold" Ops.sumProductFold
202+ , benchIOSink "sum-product-scan" Ops.sumProductScan
203+ ]
206204 , bgroup "mixedX4"
207205 [ benchIOSink "scan-map" (Ops.scanMap 4)
208206 , benchIOSink "drop-map" (Ops.dropMap 4)
benchmark/LinearOps.hs+73−58
@@ -8,6 +8,8 @@
88{-# LANGUAGE CPP #-}
99{-# LANGUAGE FlexibleContexts #-}
1010{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE DeriveAnyClass #-}
12+{-# LANGUAGE DeriveGeneric #-}
1113
1214module LinearOps where
1315
@@ -17,10 +19,12 @@ import Data.Maybe (fromJust)
1719import Prelude
1820 (Monad, Int, (+), ($), (.), return, fmap, even, (>), (<=), (==), (>=),
1921 subtract, undefined, Maybe(..), odd, Bool, not, (>>=), mapM_, curry,
20− maxBound, div, IO, compare)
22+ maxBound, div, IO, compare, Double, fromIntegral, Integer)
2123import qualified Prelude as P
2224import qualified Data.Foldable as F
2325import qualified GHC.Exts as GHC
26+import Control.DeepSeq (NFData)
27+import GHC.Generics (Generic)
2428
2529import qualified Streamly as S
2630import qualified Streamly.Prelude as S
@@ -46,7 +50,27 @@ type Stream m a = S.SerialT m a
4650{-# INLINE source #-}
4751source :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
4852source n = S.serially $ sourceUnfoldrM n
49−-- source n = S.serially $ sourceFromList n
53+
54+{-# INLINE sourceIntFromTo #-}
55+sourceIntFromTo :: (Monad m, S.IsStream t) => Int -> t m Int
56+sourceIntFromTo n = S.intFromTo n (n + value)
57+
58+{-# INLINE sourceIntFromThenTo #-}
59+sourceIntFromThenTo :: (Monad m, S.IsStream t) => Int -> t m Int
60+sourceIntFromThenTo n = S.intFromThenTo n (n + 1) (n + value)
61+
62+{-# INLINE sourceFracFromTo #-}
63+sourceFracFromTo :: (Monad m, S.IsStream t) => Int -> t m Double
64+sourceFracFromTo n = S.fracFromTo (fromIntegral n) (fromIntegral (n + value))
65+
66+{-# INLINE sourceFracFromThenTo #-}
67+sourceFracFromThenTo :: (Monad m, S.IsStream t) => Int -> t m Double
68+sourceFracFromThenTo n = S.fracFromThenTo (fromIntegral n)
69+ (fromIntegral n + 1.0001) (fromIntegral (n + value))
70+
71+{-# INLINE sourceIntegerFromStep #-}
72+sourceIntegerFromStep :: (Monad m, S.IsStream t) => Int -> t m Integer
73+sourceIntegerFromStep n = S.take value $ S.intFromStep (fromIntegral n) 1
5074
5175{-# INLINE sourceFromList #-}
5276sourceFromList :: (Monad m, S.IsStream t) => Int -> t m Int
@@ -131,9 +155,7 @@ runStream :: Monad m => Stream m a -> m ()
131155runStream = S.runStream
132156
133157{-# INLINE toList #-}
134−{-# INLINE foldr #-}
135−{-# INLINE foldrM #-}
136−toList, foldr, foldrM :: Monad m => Stream m Int -> m [Int]
158+toList :: Monad m => Stream m Int -> m [Int]
137159
138160{-# INLINE last #-}
139161{-# INLINE maximum #-}
@@ -154,7 +176,7 @@ minimumBy, maximumBy :: Monad m => Stream m Int -> m (Maybe Int)
154176{-# INLINE length #-}
155177{-# INLINE sum #-}
156178{-# INLINE product #-}
157−foldl', length, sum, product :: Monad m => Stream m Int -> m Int
179+foldl', foldr, foldrM, length, sum, product :: Monad m => Stream m Int -> m Int
158180
159181{-# INLINE all #-}
160182{-# INLINE any #-}
@@ -165,7 +187,7 @@ foldl', length, sum, product :: Monad m => Stream m Int -> m Int
165187elem, notElem, all, any, and, or :: Monad m => Stream m Int -> m Bool
166188
167189{-# INLINE toNull #-}
168−toNull :: Monad m => (t m Int -> S.SerialT m Int) -> t m Int -> m ()
190+toNull :: Monad m => (t m a -> S.SerialT m a) -> t m a -> m ()
169191toNull t = runStream . t
170192
171193{-# INLINE uncons #-}
@@ -197,9 +219,9 @@ mapM_ :: Monad m => Stream m Int -> m ()
197219mapM_ = S.mapM_ (\_ -> return ())
198220
199221toList = S.toList
200−foldr = S.foldr (:) []
222+foldr = S.foldr (+) 0
201223foldr1 = S.foldr1 (+)
202−foldrM = S.foldrM (\a xs -> return (a : xs)) []
224+foldrM = S.foldrM (\a xs -> return $ a + xs) 0
203225foldl' = S.foldl' (+) 0
204226foldl1' = S.foldl1' (+)
205227last = S.last
@@ -254,7 +276,7 @@ composeN' n f =
254276 _ -> undefined
255277
256278{-# INLINE scan #-}
257−{-# INLINE scanl1 #-}
279+{-# INLINE scanl1' #-}
258280{-# INLINE map #-}
259281{-# INLINE fmap #-}
260282{-# INLINE mapMaybe #-}
@@ -272,15 +294,12 @@ composeN' n f =
272294{-# INLINE dropWhileFalse #-}
273295{-# INLINE findIndices #-}
274296{-# INLINE elemIndices #-}
275−{-# INLINE insertLarge #-}
276−{-# INLINE insertSmall #-}
277−{-# INLINE deleteLarge #-}
278−{-# INLINE deleteSmall #-}
279−scan, scanl1, map, fmap, mapMaybe, filterEven, filterAllOut,
297+{-# INLINE insertBy #-}
298+{-# INLINE deleteBy #-}
299+scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
280300 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
281301 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
282− findIndices, elemIndices, insertLarge, insertSmall, deleteLarge,
283− deleteSmall
302+ findIndices, elemIndices, insertBy, deleteBy
284303 :: Monad m
285304 => Int -> Stream m Int -> m ()
286305
@@ -296,7 +315,7 @@ sequence :: (S.IsStream t, S.MonadAsync m)
296315 => (t m Int -> S.SerialT m Int) -> t m (m Int) -> m ()
297316
298317scan n = composeN n $ S.scanl' (+) 0
299−scanl1 n = composeN n $ S.scanl1' (+)
318+scanl1' n = composeN n $ S.scanl1' (+)
300319fmap n = composeN n $ Prelude.fmap (+1)
301320map n = composeN n $ S.map (+1)
302321mapM t n = composeN' n $ t . S.mapM return
@@ -319,11 +338,8 @@ dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
319338dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
320339findIndices n = composeN n $ S.findIndices (== maxValue)
321340elemIndices n = composeN n $ S.elemIndices maxValue
322−insertLarge n = composeN n $ S.insertBy compare maxValue
323−insertSmall n = composeN n $ S.insertBy compare 0
324−deleteSmall n = composeN n $ S.deleteBy (==) 0
325−deleteLarge n = composeN n $ S.deleteBy (>=) maxValue
326−
341+insertBy n = composeN n $ S.insertBy compare maxValue
342+deleteBy n = composeN n $ S.deleteBy (>=) maxValue
327343
328344-------------------------------------------------------------------------------
329345-- Iteration
@@ -373,16 +389,8 @@ iterateDropWhileTrue = iterateSource (S.dropWhile (<= maxValue)) maxIters
373389
374390{-# INLINE zip #-}
375391{-# INLINE zipM #-}
376−{-# INLINE merge #-}
377−{-# INLINE concat #-}
378−{-# INLINE isPrefixOf #-}
379−{-# INLINE isPrefixAll #-}
380−{-# INLINE isSubsequenceOf #-}
381−{-# INLINE evenSubsequence #-}
382−{-# INLINE stripPrefix #-}
383−zip, zipM, concat, merge, isPrefixOf, isPrefixAll, isSubsequenceOf,
384− evenSubsequence, stripPrefix
385− :: Monad m => Stream m Int -> m ()
392+{-# INLINE mergeBy #-}
393+zip, zipM, mergeBy :: Monad m => Stream m Int -> m ()
386394
387395zip src = do
388396 r <- S.tail src
@@ -393,33 +401,22 @@ zipM src = do
393401 let src1 = fromJust r
394402 transform (S.zipWithM (curry return) src src1)
395403
396−merge src = do
404+mergeBy src = do
397405 r <- S.tail src
398406 let src1 = fromJust r
399407 transform (S.mergeBy P.compare src src1)
400408
401−isPrefixOf src = do
402− let src1 = S.take 10 src
403− _ <- S.isPrefixOf src1 src
404− return ()
405−
406−isPrefixAll src = do
407− _ <- S.isPrefixOf src src
408− return ()
409−
410−isSubsequenceOf src = do
411− let src1 = S.take 10 src
412− _ <- S.isSubsequenceOf src1 src
413− return ()
409+{-# INLINE isPrefixOf #-}
410+{-# INLINE isSubsequenceOf #-}
411+isPrefixOf, isSubsequenceOf :: Monad m => Stream m Int -> m Bool
414412
415−evenSubsequence src = do
416− let src1 = S.filter even src
417− _ <- S.isSubsequenceOf src1 src
418− return ()
413+isPrefixOf src = S.isPrefixOf src src
414+isSubsequenceOf src = S.isSubsequenceOf src src
419415
416+{-# INLINE stripPrefix #-}
417+stripPrefix :: Monad m => Stream m Int -> m ()
420418stripPrefix src = do
421− let src1 = S.take 10 src
422− _ <- S.stripPrefix src1 src
419+ _ <- S.stripPrefix src src
423420 return ()
424421
425422{-# INLINE zipAsync #-}
@@ -435,8 +432,6 @@ zipAsyncM src = do
435432 let src1 = fromJust r
436433 transform (S.zipAsyncWithM (curry return) src src1)
437434
438−concat _n = return ()
439−
440435{-# INLINE eqBy #-}
441436eqBy :: (Monad m, P.Eq a) => Stream m a -> m P.Bool
442437eqBy src = S.eqBy (==) src src
@@ -445,6 +440,15 @@ eqBy src = S.eqBy (==) src src
445440cmpBy :: (Monad m, P.Ord a) => Stream m a -> m P.Ordering
446441cmpBy src = S.cmpBy P.compare src src
447442
443+concatStreamLen, maxNested :: Int
444+concatStreamLen = 1
445+maxNested = 100000
446+
447+{-# INLINE concatMap #-}
448+concatMap :: S.MonadAsync m => Int -> Stream m Int
449+concatMap n = S.concatMap (\_ -> sourceUnfoldrMN maxNested n)
450+ (sourceUnfoldrMN concatStreamLen n)
451+
448452-------------------------------------------------------------------------------
449453-- Mixed Composition
450454-------------------------------------------------------------------------------
@@ -476,6 +480,17 @@ filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
476480filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
477481filterMap n = composeN n $ S.map (subtract 1) . S.filter (<= maxValue)
478482
483+data Pair a b = Pair !a !b deriving (Generic, NFData)
484+
485+{-# INLINE sumProductFold #-}
486+sumProductFold :: Monad m => Stream m Int -> m (Int, Int)
487+sumProductFold = S.foldl' (\(s,p) x -> (s + x, p P.* x)) (0,1)
488+
489+{-# INLINE sumProductScan #-}
490+sumProductScan :: Monad m => Stream m Int -> m (Pair Int Int)
491+sumProductScan = S.foldl' (\(Pair _ p) (s0,x) -> Pair s0 (p P.* x)) (Pair 0 1)
492+ . S.scanl' (\(s,_) x -> (s + x,x)) (0,0)
493+
479494-------------------------------------------------------------------------------
480495-- Pure stream operations
481496-------------------------------------------------------------------------------
@@ -498,16 +513,16 @@ ordInstanceMin src = P.min src src
498513
499514{-# INLINE showInstance #-}
500515showInstance :: Stream Identity Int -> P.String
501−showInstance src = P.showsPrec 0 src ""
516+showInstance src = P.show src
502517
503518{-# INLINE showInstanceList #-}
504519showInstanceList :: Stream Identity Int -> P.String
505−showInstanceList src = P.showsPrec 0 (GHC.toList src P.++ [2..value]) ""
520+showInstanceList src = P.show (GHC.toList src P.++ [2..value])
506521
507522{-# INLINE readInstance #-}
508523readInstance :: Stream Identity Int -> Stream Identity Int
509524readInstance src =
510− let r = P.readsPrec 0 ("fromList [1"
525+ let r = P.reads ("fromList [1"
511526 P.++ P.concat (P.replicate value ",1") P.++ "]")
512527 in case r of
513528 [(x,"")] -> src S.<> x
src/Streamly/Prelude.hs+1011−247
@@ -28,6 +28,10 @@
2828-- provided for convenience and for consistency with other pure APIs in the
2929-- @base@ package.
3030--
31+-- In many cases, short definitions of the combinators are provided in the
32+-- documentation for illustration. The actual implementation may differ for
33+-- performance reasons.
34+--
3135-- Functions having a 'MonadAsync' constraint work concurrently when used with
3236-- appropriate stream type combinator. Please be careful to not use 'parallely'
3337-- with infinite streams.
@@ -41,74 +45,128 @@
4145module Streamly.Prelude
4246 (
4347 -- * Construction
44− -- | All other stream construction and generation combinators described
45− -- later, and even more custom combinators can be expressed in terms of
46− -- these primitives. However, the special versions provided in this module
47− -- can be more efficient in some situations.
48−
49− -- ** From Elements
48+ -- ** Primitives
5049 -- | Primitives to construct a stream from pure values or monadic actions.
50+ -- All other stream construction and generation combinators described later
51+ -- can be expressed in terms of these primitives. However, the special
52+ -- versions provided in this module can be much more efficient in most
53+ -- cases. Users can create custom combinators using these primitives.
54+
5155 K.nil
5256 , K.cons
5357 , (K..:)
5458
5559 , consM
5660 , (|:)
61+
62+ -- ** From Values
63+ -- | Generate a monadic stream from a seed value or values.
5764 , yield
5865 , yieldM
66+ , K.repeat
67+ , repeatM
68+ , replicate
69+ , replicateM
5970
60− -- ** From Streams
61− -- | You can construct streams by appending or merging existing streams.
62− -- When constructing streams from streams, '<>' and 'mempty' are the
63− -- intuitive equivalents of 'K.cons' and 'K.nil', respectively. These
64− -- primitives can be very useful when constructing your own custom stream
65− -- combinators. Also see the variants of '<>' defined in the "Streamly"
66− -- module. Note that appending streams is inexpensive, it is much more
67− -- efficient than appending lists.
68−
69− -- * Generation
70− -- ** Unfold and Iterate
71− -- | Note that the generative steps of unfold and iterate are inherently
72− -- serial as the next step depends on the result of the previous step.
73− -- However, consumption of the result from the previous step can happen in
74− -- parallel with the generation by the next step.
71+ -- ** Enumeration
72+ -- | The haskell 'Enum' typeclass provides enumerations for member types.
73+ -- However, the typeclass is tied to lists, it is impossible to use it to
74+ -- write enumeration functions producing something other than a list in a
75+ -- generic way.
76+ -- We can use the 'Enum' facilities to enumerate the type producing a list
77+ -- and then convert it to a stream:
78+ --
79+ -- @
80+ -- 'fromList' $ 'Prelude.enumFromThen' from then
81+ -- @
82+ --
83+ -- However, this is not particularly efficient. Note that using
84+ -- enumeration functions e.g. 'Prelude.enumFromThen' is slightly faster
85+ -- than the idioms like @[from, then..]@.
86+ --
87+ -- For the cases where speed matters, we have provided specific 'Enum' like
88+ -- implementations for enumeration.
89+ --
90+ , intFrom
91+ , intFromTo
92+ , intFromThen
93+ , intFromThenTo
94+ , intFromStep
95+
96+ , fracFrom
97+ , fracFromTo
98+ , fracFromThen
99+ , fracFromThenTo
100+ , numFromStep
101+
102+ -- ** From Generators
103+ -- | Generate a monadic stream from a seed value and a generator function.
75104 , unfoldr
76105 , unfoldrM
77106 , iterate
78107 , iterateM
108+ , fromIndices
109+ , fromIndicesM
79110
80− -- ** Replicate and Repeat
81− -- | Generate a monadic stream from a seed value or function. Note that
82− -- these functions can generate a stream fully concurrently as, unlike
83− -- unfolds, there is no dependency between steps, therefore, an unbounded
84− -- number of steps can run concurrently. All of these can be expressed in
85− -- terms of 'K.cons' and 'K.nil' primitives.
86− , replicateM
87− , replicate
88− , K.repeat
89− , repeatM
90−
91− -- ** Generate From
111+ -- ** From Containers
92112 -- | Convert an input structure, container or source into a stream. All of
93113 -- these can be expressed in terms of primitives.
94− , generate
95− , generateM
96114 , P.fromList
97115 , fromListM
98116 , K.fromFoldable
99117 , fromFoldableM
100− , fromHandle
101118
102− -- * Deconstruction
103− , uncons
119+ -- ** From External Containers
120+ , fromHandle
104121
105122 -- * Elimination
106123
124+ -- ** Primitives
125+ -- | It is easy to express all the folds in terms of the 'uncons' primitive,
126+ -- however the specific implementations provided later are generally more
127+ -- efficient. Folds are inherently serial as each step needs to use the
128+ -- result of the previous step.
129+ , uncons
130+
107131 -- ** General Folds
108− -- | All the folds can be implemented in terms of 'uncons', however the
109− -- specific implementations provided here are generally more efficient.
110− -- Folds are inherently serial as each step needs to use the result of
111− -- the previous step.
132+-- | Right and left folds.
133+-- As a simple rule, always use lazy right fold for construction and strict
134+-- left fold for reduction. By construction we mean using a constructor as the
135+-- outermost operation in the fold function, by reduction we mean using a
136+-- function as the outermost operation in the fold function.
137+--
138+-- +-----------------------------------+--------------------------------------+
139+-- | Right Fold | Left Fold |
140+-- +===================================+======================================+
141+-- | Construction consumes input | Construction consumes all input, |
142+-- | lazily and streams it in FIFO | and constructs in reverse (LIFO) |
143+-- | order | order |
144+-- +-----------------------------------+--------------------------------------+
145+-- | Reduction ends up buffering all | Strict reduction works |
146+-- | input before it can be reduced | incrementally, without buffering. |
147+-- +-----------------------------------+--------------------------------------+
148+--
149+-- Almost always, we need lazy construction and strict reduction, therefore,
150+-- strict @foldr@ and lazy @foldl@ are rarely useful. If needed, strict @foldr@
151+-- and lazy @foldl@ can be expressed in terms of the available versions. For
152+-- example, a lazy @foldl@ can be replaced by a strict @foldl@ to reverse the
153+-- structure followed by a @foldr@.
154+--
155+-- The following equations may help understand the relation between the two
156+-- folds for lists:
157+--
158+-- @
159+-- foldr f z xs = foldl (flip f) z (reverse xs)
160+-- foldl f z xs = foldr (flip f) z (reverse xs)
161+-- @
162+--
163+-- More generally:
164+--
165+-- @
166+-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
167+-- foldr f z xs = foldl g id xs z where g k x = k . f x
168+-- @
169+
112170 , foldr
113171 , foldr1
114172 , foldrM
@@ -118,108 +176,113 @@ module Streamly.Prelude
118176 , foldx
119177 , foldxM
120178
121− -- ** Specialized Folds
122− -- | These folds can be expressed in terms of the general fold routines but
123− -- the special versions here can be more efficient in many cases.
124−
125− -- Filtering folds: extract parts of the stream
179+ -- ** To Elements
180+ -- | Folds that extract selected elements of a stream or their properties.
181+ , (!!)
126182 , head
127− , tail
128183 , last
129− , init
184+ , findM
185+ , find
186+ , lookup
187+ , findIndex
188+ , elemIndex
130189
131− -- Substreams
132− , isPrefixOf
133− , isSubsequenceOf
134− , stripPrefix
190+ -- ** To Parts
191+ -- | Folds that extract selected parts of a stream.
192+ , tail
193+ , init
135194
136− -- Conditional folds: may terminate early based on a condition
195+ -- ** To Boolean
196+ -- | Folds that summarize the stream to a boolean value.
137197 , null
138198 , elem
139− , elemIndex
140199 , notElem
141− , lookup
142− , findM
143− , find
144− , findIndex
145200 , all
146201 , any
147202 , and
148203 , or
149− , eqBy
150− , cmpBy
151− , (!!)
152− , concatMapM
153− , concatMap
154204
155− -- Full folds - need to go through all elements
205+ -- ** To Summary
206+ -- | Folds that summarize the stream to a single value.
156207 , length
157− , maximum
158− , maximumBy
159− , minimum
160− , minimumBy
161208 , sum
162209 , product
163210
164− -- ** Fold To
211+ -- ** To Summary (Maybe)
212+ -- | Folds that summarize a non-empty stream to a 'Just' value and return
213+ -- 'Nothing' for an empty stream.
214+ , maximumBy
215+ , maximum
216+ , minimumBy
217+ , minimum
218+ , the
219+
220+ -- ** To Containers
165221 -- | Convert or divert a stream into an output structure, container or
166222 -- sink.
167223 , toList
168224 , toHandle
169225
170226 -- * Transformation
171− -- | One to one transformations, each element in the input stream is
172− -- transformed into a corresponding element in the output stream.
173− -- Therefore, the length of the stream and the ordering of elements in the
174− -- stream remains unchanged after the transformation.
175227
176228 -- ** Scanning
177− -- | Scan is a transformation by continuously folding the result with the
178− -- next element of the stream. This is the generalized way to transform a
179− -- stream carrying state from previous transformation steps, other forms of
180− -- transformation like map can be expressed in terms of this.
229+ -- | Scans stream all the intermediate reduction steps of the corresponding
230+ -- folds. The following equations hold for lists:
231+ --
232+ -- > scanl f z xs == map (foldl f z) $ inits xs
233+ -- > scanr f z xs == map (foldr f z) $ tails
234+ --
235+ -- We do not provide a right associative scan, it can be recovered from a
236+ -- 'scanl'' as follows:
237+ --
238+ -- > scanr f z xs == reverse $ scanl' (flip f) z (reverse xs)
239+ --
240+ -- Scan is like a stateful map. If we discard the state, we get the map:
241+ --
242+ -- > S.drop 1 $ S.scanl' (\_ x -> f x) z xs == map f xs
243+
244+ -- > S.postscanl' (\_ x -> f x) z xs == map f xs
245+
181246 , scanl'
182247 , scanlM'
183− , scanlM
184− , scanl
185− , scanl1M'
248+ -- , postscanl'
249+ -- , postscanlM'
250+ -- , prescanl'
251+ -- , prescanlM'
186252 , scanl1'
187− , scanl1M
188− , scanl1
253+ , scanl1M'
189254 , scanx
190255
191256 -- ** Mapping
192− -- | Map is a special form of scan where no state is carried from one step
193− -- to the next.
257+ -- | Map is a strictly one-to-one transformation of stream elements. It
258+ -- cannot add or remove elements from the stream, just transforms them.
194259 , Serial.map
195− , mapM
196260
197261 -- ** Flattening
198262 , sequence
199−
200− -- * Filtering and Insertion
201− -- | Adding or removing elements from the stream thus changing the length
202− -- of the stream.
263+ , mapM
203264
204265 -- ** Filtering
266+ -- | Filtering may remove some elements from the stream.
267+
205268 , filter
206269 , filterM
207− , uniq
208270 , take
209271 , takeWhile
210272 , takeWhileM
211273 , drop
212274 , dropWhile
213275 , dropWhileM
276+ , deleteBy
277+ , uniq
214278
215− -- ** Inserting
216− , intersperseM
217− , insertBy
279+ -- ** Insertion
280+ -- | Insertion adds more elements to the stream.
218281
219− -- ** Deleting
220− , deleteBy
282+ , insertBy
283+ , intersperseM
221284
222− -- * Reordering
285+ -- ** Reordering
223286 , reverse
224287
225288 -- * Hybrid Operations
@@ -234,20 +297,51 @@ module Streamly.Prelude
234297 , findIndices
235298 , elemIndices
236299
237− -- * Zipping
238− , indexed
239− , indexedR
300+ -- * Multi-Stream Operations
301+ -- | New streams can be constructed by appending, merging or zipping
302+ -- existing streams.
303+
304+ -- ** Appending
305+ -- | Streams form a 'Semigroup' and a 'Monoid' under the append
306+ -- operation.
307+ --
308+ -- @
309+ -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
310+ -- [1,2,3,4]
311+ -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
312+ -- [1,2,3,4]
313+ -- @
314+
315+ -- ** Merging
316+ -- | Streams form a commutative semigroup under the merge
317+ -- operation.
318+
319+ -- , merge
320+ , mergeBy
321+ , mergeByM
322+ , mergeAsyncBy
323+ , mergeAsyncByM
324+
325+ -- ** Zipping
240326 , zipWith
241327 , zipWithM
242328 , Z.zipAsyncWith
243329 , Z.zipAsyncWithM
244330
245− -- * Merging
246− , mergeBy
247− , merge
331+ -- Special zips
332+ , indexed
333+ , indexedR
248334
249− -- * Transformation comprehensions
250− , the
335+ -- ** Flattening
336+ , concatMapM
337+ , concatMap
338+
339+ -- ** Folds
340+ , eqBy
341+ , cmpBy
342+ , isPrefixOf
343+ , isSubsequenceOf
344+ , stripPrefix
251345
252346 -- * Deprecated
253347 , K.once
@@ -270,6 +364,7 @@ import qualified Prelude
270364import qualified System.IO as IO
271365
272366import Streamly.SVar (MonadAsync, defState, rstState)
367+import Streamly.Streams.Async (mkAsync')
273368import Streamly.Streams.Combinators (maxYields)
274369import Streamly.Streams.Prelude (fromStreamS, toStreamS)
275370import Streamly.Streams.StreamD (fromStreamD, toStreamD)
@@ -306,10 +401,18 @@ uncons m = K.uncons (K.adapt m)
306401-- Generation by Unfolding
307402------------------------------------------------------------------------------
308403
309−-- | Build a stream by unfolding a /pure/ step function starting from a seed.
310−-- The step function returns the next element in the stream and the next seed
311−-- value. When it is done it returns 'Nothing' and the stream ends. For
312−-- example,
404+-- |
405+-- @
406+-- unfoldr step s =
407+-- case step s of
408+-- Nothing -> 'K.nil'
409+-- Just (a, b) -> a \`cons` unfoldr step b
410+-- @
411+--
412+-- Build a stream by unfolding a /pure/ step function @step@ starting from a
413+-- seed @s@. The step function returns the next element in the stream and the
414+-- next seed value. When it is done it returns 'Nothing' and the stream ends.
415+-- For example,
313416--
314417-- @
315418-- let f b =
@@ -322,15 +425,6 @@ uncons m = K.uncons (K.adapt m)
322425-- [0,1,2,3]
323426-- @
324427--
325−-- unfoldr can be expressed in terms of 'yield' and '<>' as follows:
326−--
327−-- @
328−-- unfoldr step s =
329−-- case step s of
330−-- Nothing -> mempty
331−-- Just (a, b) -> 'yield' a '<>' (unfoldr step b)
332−-- @
333−--
334428-- @since 0.1.0
335429{-# INLINE_EARLY unfoldr #-}
336430unfoldr :: (Monad m, IsStream t) => (b -> Maybe (a, b)) -> b -> t m a
@@ -384,20 +478,36 @@ unfoldrMSerial step seed = fromStreamS (S.unfoldrM step seed)
384478
385479-- Faster than yieldM because there is no bind.
386480--
387−-- | Create a singleton stream from a pure value. Same as @a `cons` nil@ but
388−-- slighly more efficient. Note that in monadic streams, 'yield' is the same
389−-- as 'pure' or 'return', however, in Zip applicative streams it is not the
390−-- same as 'pure' because in that case 'pure' is equivalent to 'repeat'
391−-- instead. In all other stream types, 'yield' is the same as @yieldM . pure@
392−-- but more efficient.
481+-- |
482+-- @
483+-- yield a = a \`cons` nil
484+-- @
485+--
486+-- Create a singleton stream from a pure value.
487+--
488+-- The following holds in monadic streams, but not in Zip streams:
489+--
490+-- @
491+-- yield = pure
492+-- yield = yieldM . pure
493+-- @
494+--
495+-- In Zip applicative streams 'yield' is not the same as 'pure' because in that
496+-- case 'pure' is equivalent to 'repeat' instead. 'yield' and 'pure' are
497+-- equally efficient, in other cases 'yield' may be slightly more efficient
498+-- than the other equivalent definitions.
393499--
394500-- @since 0.4.0
395501{-# INLINE yield #-}
396502yield :: IsStream t => a -> t m a
397503yield = K.yield
398504
399−-- | Create a singleton stream from a monadic action. Same as @m \`consM` nil@
400−-- but more efficient.
505+-- |
506+-- @
507+-- yieldM m = m \`consM` nil
508+-- @
509+--
510+-- Create a singleton stream from a monadic action.
401511--
402512-- @
403513-- > toList $ yieldM getLine
@@ -410,24 +520,231 @@ yield = K.yield
410520yieldM :: (Monad m, IsStream t) => m a -> t m a
411521yieldM = K.yieldM
412522
413−-- | Generate a stream of given length, from a given pure
414−-- function of its indices.
415−{-# INLINE generate #-}
416−generate :: (IsStream t, Monad m) => Int -> (Int -> a) -> t m a
417−generate n = fromStreamD . D.generate n
523+-- | @intFromStep from step@ generates an infinite stream whose first element
524+-- is @from@ and the successive elements are in increments of @step@. This does
525+-- not check for overflow or underflow if the 'Integral' type is bounded.
526+--
527+-- @
528+-- > S.toList $ S.take 4 $ S.intFromStep 0 2
529+-- [0,2,4,6]
530+-- > S.toList $ S.take 3 $ S.intFromStep 0 (-2)
531+-- [0,-2,-4]
532+-- @
533+--
534+-- @since 0.6.0
535+{-# INLINE intFromStep #-}
536+intFromStep :: (IsStream t, Monad m, Integral a) => a -> a -> t m a
537+intFromStep from stride = fromStreamD $ D.intFromStep from stride
418538
419−-- | Generate a stream of given length, from a given monadic
420−-- function of its indices.
539+-- | Enumerate an 'Integral' type. @intFrom from@ generates a stream whose
540+-- first element is @from@ and the successive elements are in increments of
541+-- @1@. The stream is bounded by the size of the 'Integral' type.
421542--
422−{-# INLINE generateM #-}
423−generateM :: (IsStream t, Monad m) => Int -> (Int -> m a) -> t m a
424−generateM n = fromStreamD . D.generateM n
543+-- @
544+-- > S.toList $ S.take 4 $ S.intFrom (0 :: Int)
545+-- [0,1,2,3]
546+-- @
547+--
548+-- @since 0.6.0
549+{-# INLINE intFrom #-}
550+intFrom :: (IsStream t, Monad m, Integral a, Bounded a) => a -> t m a
551+intFrom from = fromStreamD $ D.intFrom from
425552
426−-- | Generate a stream by performing a monadic action @n@ times. Can be
427−-- expressed as @stimes n (yieldM m)@.
553+-- | Enumerate an 'Integral' type in steps. @intFromThen from then@ generates a
554+-- stream whose first element is @from@, the second element is @then@ and the
555+-- successive elements are in increments of @then - from@. The stream is
556+-- bounded by the size of the 'Integral' type.
557+--
558+-- @
559+-- > S.toList $ S.take 4 $ S.intFromThen (0 :: Int) 2
560+-- [0,2,4,6]
561+-- > S.toList $ S.take 4 $ S.intFromThen (0 :: Int) (-2)
562+-- [0,-2,-4,-6]
563+-- @
564+--
565+-- @since 0.6.0
566+{-# INLINE intFromThen #-}
567+intFromThen :: (IsStream t, Monad m, Integral a, Bounded a) => a -> a -> t m a
568+intFromThen from next = fromStreamD $ D.intFromThen from next
569+
570+-- | Enumerate an 'Integral' type up to a given limit. @intFromTo from to@
571+-- generates a finite stream whose first element is @from@ and successive
572+-- elements are in increments of @1@ up to @to@.
573+--
574+-- @
575+-- > S.toList $ S.intFromTo 0 4
576+-- [0,1,2,3,4]
577+-- @
578+--
579+-- @since 0.6.0
580+{-# INLINE intFromTo #-}
581+intFromTo :: (IsStream t, Monad m, Integral a) => a -> a -> t m a
582+intFromTo from to = fromStreamD $ D.intFromTo from to
583+
584+-- | Enumerate an 'Integral' type in steps up to a given limit. @intFromThenTo
585+-- from then to@ generates a finite stream whose first element is @from@, the
586+-- second element is @then@ and the successive elements are in increments of
587+-- @then - from@ up to @to@.
588+--
589+-- @
590+-- > S.toList $ S.intFromThenTo 0 2 6
591+-- [0,2,4,6]
592+-- > S.toList $ S.intFromThenTo 0 (-2) (-6)
593+-- [0,-2,-4,-6]
594+-- @
595+--
596+-- @since 0.6.0
597+{-# INLINE intFromThenTo #-}
598+intFromThenTo :: (IsStream t, Monad m, Integral a) => a -> a -> a -> t m a
599+intFromThenTo from next to = fromStreamD $ D.intFromThenTo from next to
600+
601+-- | @numFromStep from step@ generates an infinite stream whose first element
602+-- is @from@ and the successive elements are in increments of @step@. This is
603+-- numerically stable but does not check for overflow or underflow for bounded
604+-- types. Note, for 'Integral' types 'intFromStep' is faster.
428605--
429606--
430607-- @
608+-- > S.toList $ S.take 4 $ S.numFromStep 0.1 2
609+-- [0.1,2.1,4.1,6.1]
610+-- > S.toList $ S.take 3 $ S.numFromStep 0.1 (-2)
611+-- [0.1,-1.9,-3.9,-5.9]
612+-- @
613+--
614+-- @since 0.6.0
615+{-# INLINE numFromStep #-}
616+numFromStep :: (IsStream t, Monad m, Num a) => a -> a -> t m a
617+numFromStep from stride = fromStreamD $ D.numFromStep from stride
618+
619+-- Even though the underlying implementation of fracFrom and fracFromThen works
620+-- for any 'Num' we have restricted these to 'Fractional' because these do not
621+-- perform any bounds check, in contrast to int versions and are therefore not
622+-- equivalent substitutes for those.
623+--
624+-- | Numerically stable enumeration from a 'Fractional' number in steps of size
625+-- @1@. @fracFrom from@ generates a stream whose first element is @from@ and
626+-- the successive elements are in increments of @1@. No overflow or underflow
627+-- checks are performed.
628+--
629+-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
630+--
631+-- @
632+-- > S.toList $ S.take 4 $ S.fracFrom 1.1
633+-- [1.1,2.1,3.1,4.1]
634+-- @
635+--
636+--
637+-- @since 0.6.0
638+{-# INLINE fracFrom #-}
639+fracFrom :: (IsStream t, Monad m, Fractional a) => a -> t m a
640+fracFrom from = fromStreamD $ D.numFrom from
641+
642+-- | Numerically stable enumeration from a 'Fractional' number in steps.
643+-- @fracFromThen from then@ generates a stream whose first element is @from@,
644+-- the second element is @then@ and the successive elements are in increments
645+-- of @then - from@. No overflow or underflow checks are performed.
646+--
647+-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
648+-- example:
649+--
650+-- @
651+-- > S.toList $ S.take 4 $ S.fracFromThen 1.1 2.1
652+-- [1.1,2.1,3.1,4.1]
653+-- > S.toList $ S.take 4 $ S.fracFromThen 1.1 (-2.1)
654+-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
655+-- @
656+--
657+-- @since 0.6.0
658+{-# INLINE fracFromThen #-}
659+fracFromThen :: (IsStream t, Monad m, Fractional a) => a -> a -> t m a
660+fracFromThen from next = fromStreamD $ D.numFromThen from next
661+
662+-- | Numerically stable enumeration from a 'Fractional' number to a given
663+-- limit. @fracFromTo from to@ generates a finite stream whose first element
664+-- is @from@ and successive elements are in increments of @1@ up to @to@.
665+--
666+-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
667+-- example:
668+--
669+-- @
670+-- > S.toList $ S.fracFromTo 1.1 4
671+-- [1.1,2.1,3.1,4.1]
672+-- > S.toList $ S.fracFromTo 1.1 4.6
673+-- [1.1,2.1,3.1,4.1,5.1]
674+-- @
675+--
676+-- Notice that the last element is equal to the specified @to@ value after
677+-- rounding to the nearest integer.
678+--
679+-- @since 0.6.0
680+{-# INLINE fracFromTo #-}
681+fracFromTo :: (IsStream t, Monad m, Fractional a, Ord a) => a -> a -> t m a
682+fracFromTo from to = fromStreamD $ D.fracFromTo from to
683+
684+-- | Numerically stable enumeration from a 'Fractional' number in steps up to a
685+-- given limit. @fracFromThenTo from then to@ generates a finite stream whose
686+-- first element is @from@, the second element is @then@ and the successive
687+-- elements are in increments of @then - from@ up to @to@.
688+--
689+-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
690+-- example:
691+--
692+-- @
693+-- > S.toList $ S.fracFromThenTo 0.1 2 6
694+-- [0.1,2.0,3.9,5.799999999999999]
695+-- > S.toList $ S.fracFromThenTo 0.1 (-2) (-6)
696+-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
697+-- @
698+--
699+--
700+-- @since 0.6.0
701+{-# INLINE fracFromThenTo #-}
702+fracFromThenTo
703+ :: (IsStream t, Monad m, Fractional a, Ord a)
704+ => a -> a -> a -> t m a
705+fracFromThenTo from next to = fromStreamD $ D.fracFromThenTo from next to
706+
707+-- |
708+-- @
709+-- fromIndices f = let g i = f i \`cons` g (i + 1) in g 0
710+-- @
711+--
712+-- Generate an infinite stream, whose values are the output of a function @f@
713+-- applied on the corresponding index. Index starts at 0.
714+--
715+-- @
716+-- > S.toList $ S.take 5 $ S.fromIndices id
717+-- [0,1,2,3,4]
718+-- @
719+--
720+-- @since 0.6.0
721+{-# INLINE fromIndices #-}
722+fromIndices :: (IsStream t, Monad m) => (Int -> a) -> t m a
723+fromIndices = fromStreamD . D.fromIndices
724+
725+-- XXX this needs to be concurrent
726+--
727+-- |
728+-- @
729+-- fromIndicesM f = let g i = f i \`consM` g (i + 1) in g 0
730+-- @
731+--
732+-- Generate an infinite stream, whose values are the output of a monadic
733+-- function @f@ applied on the corresponding index. Index starts at 0.
734+--
735+-- @since 0.6.0
736+{-# INLINE fromIndicesM #-}
737+fromIndicesM :: (IsStream t, Monad m) => (Int -> m a) -> t m a
738+fromIndicesM = fromStreamD . D.fromIndicesM
739+
740+-- |
741+-- @
742+-- replicateM = take n . repeatM
743+-- @
744+--
745+-- Generate a stream by performing a monadic action @n@ times. Same as:
746+--
747+-- @
431748-- runStream $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
432749-- runStream $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
433750-- @
@@ -435,20 +752,33 @@ generateM n = fromStreamD . D.generateM n
435752-- /Concurrent/
436753--
437754-- @since 0.1.1
755+{-# INLINE_EARLY replicateM #-}
438756replicateM :: (IsStream t, MonadAsync m) => Int -> m a -> t m a
439−replicateM n m = go n
440− where
441− go cnt = if cnt <= 0 then K.nil else m |: go (cnt - 1)
757+replicateM = K.replicateM
758+
759+{-# RULES "replicateM serial" replicateM = replicateMSerial #-}
760+{-# INLINE replicateMSerial #-}
761+replicateMSerial :: MonadAsync m => Int -> m a -> SerialT m a
762+replicateMSerial n = fromStreamS . S.replicateM n
442763
443−-- | Generate a stream by replicating the value @n@ times.
764+-- |
765+-- @
766+-- replicate = take n . repeat
767+-- @
444768--
445−replicate :: IsStream t => Int -> a -> t m a
446−replicate n a = go n
447− where
448− go cnt = if cnt <= 0 then K.nil else a `K.cons` go (cnt - 1)
769+-- Generate a stream of length @n@ by repeating a value @n@ times.
770+--
771+-- @since 0.6.0
772+replicate :: (IsStream t, Monad m) => Int -> a -> t m a
773+replicate n = fromStreamS . S.replicate n
449774
450−-- | Generate a stream by repeatedly executing a monadic action forever. Can be
451−-- expressed as @cycle1 . yieldM@.
775+-- |
776+-- @
777+-- repeatM = fix . consM
778+-- repeatM = cycle1 . yieldM
779+-- @
780+--
781+-- Generate a stream by repeatedly executing a monadic action forever.
452782--
453783-- @
454784-- runStream $ serially $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
@@ -462,7 +792,19 @@ repeatM :: (IsStream t, MonadAsync m) => m a -> t m a
462792repeatM = go
463793 where go m = m |: go m
464794
465−-- | Iterate a pure function from a seed value, streaming the results forever.
795+-- |
796+-- @
797+-- iterate f x = x \`cons` iterate f x
798+-- @
799+--
800+-- Generate an infinite stream with @x@ as the first element and each
801+-- successive element derived by applying the function @f@ on the previous
802+-- element.
803+--
804+-- @
805+-- > S.toList $ S.take 5 $ S.iterate (+1) 1
806+-- [1,2,3,4,5]
807+-- @
466808--
467809-- @since 0.1.2
468810iterate :: IsStream t => (a -> a) -> a -> t m a
@@ -470,8 +812,14 @@ iterate step = fromStream . go
470812 where
471813 go s = K.cons s (go (step s))
472814
473−-- | Iterate a monadic function from a seed value, streaming the results
474−-- forever.
815+-- |
816+-- @
817+-- iterateM f m = m \`consM` iterateM f m
818+-- @
819+--
820+-- Generate an infinite stream with the first element generated by the action
821+-- @m@ and each successive element derived by applying the monadic function
822+-- @f@ on the previous element.
475823--
476824-- When run concurrently, the next iteration can run concurrently with the
477825-- processing of the previous iteration. Note that more than one iteration
@@ -500,9 +848,13 @@ iterateM step = go
500848-- Conversions
501849------------------------------------------------------------------------------
502850
503−-- | Construct a stream from a list containing monadic actions. More efficient
504−-- list specific implementation of 'fromFoldableM' especially for serial
505−-- streams as it works well with fusion optimization.
851+-- |
852+-- @
853+-- fromListM = 'Prelude.foldr' 'K.consM' 'K.nil'
854+-- @
855+--
856+-- Construct a stream from a list of monadic actions. This is more efficient
857+-- than 'fromFoldableM' for serial streams.
506858--
507859-- @since 0.4.0
508860{-# INLINE_EARLY fromListM #-}
@@ -511,8 +863,12 @@ fromListM = fromStreamD . D.fromListM
511863{-# RULES "fromListM fallback to StreamK" [1]
512864 forall a. D.toStreamK (D.fromListM a) = fromFoldableM a #-}
513865
514−-- | Construct a stream from a 'Foldable' containing monadic actions. Same as
515−-- @'Prelude.foldr' 'consM' 'K.nil'@.
866+-- |
867+-- @
868+-- fromFoldableM = 'Prelude.foldr' 'consM' 'K.nil'
869+-- @
870+--
871+-- Construct a stream from a 'Foldable' containing monadic actions.
516872--
517873-- @
518874-- runStream $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
@@ -556,7 +912,7 @@ fromHandle h = fromStream go
556912-- stream into a list:
557913--
558914-- @
559−-- >> runIdentity $ foldrM (\\x xs -> return (x : xs)) [] (serially $ fromFoldable [1,2,3])
915+-- >> S.foldrM (\\x xs -> return (x : xs)) [] $ fromList [1,2,3]
560916-- [1,2,3]
561917-- @
562918--
@@ -565,19 +921,74 @@ fromHandle h = fromStream go
565921foldrM :: Monad m => (a -> b -> m b) -> b -> SerialT m a -> m b
566922foldrM = P.foldrM
567923
568−-- | Lazy right associative fold. For example, to fold a stream into a list:
924+-- | Lazy right associative fold.
925+--
926+-- @foldr f z xs@ deconstructs @xs@ one element @x@ at a time, applying the
927+-- function @f@ to @x@ and the tail of the output. The tail recurses until the
928+-- input finishes and @z@ is used as the tail end.
569929--
570930-- @
571−-- >> runIdentity $ foldr (:) [] (serially $ fromFoldable [1,2,3])
572−-- [1,2,3]
931+-- foldr f z xs == x1 \`f` tail1
932+-- tail1 == x2 \`f` tail2
933+-- tail2 == x3 \`f` tail3
934+-- ...
935+-- tailn == xn \`f` z
573936-- @
574937--
938+-- Resulting in a right associated expression:
939+--
940+-- @
941+-- foldr f z xs == x1 \`f` (x2 \`f` ...(xn \`f` z))
942+-- @
943+--
944+-- When the outermost operation in the fold function is a constructor, foldr
945+-- generates a structure that can be consumed lazily. For example:
946+--
947+-- @
948+-- > S.foldr (:) [] $ S.fromList [1,2,3,4]
949+-- [1,2,3,4]
950+-- @
951+--
952+-- @
953+-- 1 : tail
954+-- 1 : (2 : tail)
955+-- 1 : (2 : (3 : tail))
956+-- 1 : (2 : (3 : (4 : [])))
957+-- @
958+--
959+-- When the outermost operation is a function @foldr@ results in a right
960+-- associated expression which cannot be reduced until the whole expression has
961+-- been built. Therefore, it ends up consuming the whole input, buffering the
962+-- whole expression in memory before reduction can start. For example:
963+--
964+-- @
965+-- > S.foldr (+) 0 $ S.fromList [1,2,3,4]
966+-- 10
967+-- @
968+--
969+-- @
970+-- 1 + tail
971+-- 1 + (2 + tail)
972+-- 1 + (2 + (3 + tail))
973+-- 1 + (2 + (3 + (4 + 0)))
974+-- @
975+--
976+-- In @foldr@, the output tail is the source of recursion, we can stop
977+-- recursion and terminate the fold by yielding a terminal value as tail:
978+--
979+-- >> S.foldr (\x rest -> if x == 3 then [] else x : rest) [] $ S.fromList [4,1,3,undefined]
980+-- >[4,1]
981+--
982+-- The arguments to the folding function (@a -> b -> b@) are in the head and
983+-- tail order of the output, @a@ is the head and @b@ is the tail. Remember, in
984+-- a right fold the zero is on the right, it is the tail end.
985+--
575986-- @since 0.1.0
576987{-# INLINE foldr #-}
577988foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
578989foldr = P.foldr
579990
580−-- | Right fold, for non-empty streams, using first element as the starting
991+-- | Lazy right fold for non-empty streams, using first element as the starting
581992-- value. Returns 'Nothing' if the stream is empty.
582993--
583994-- @since 0.5.0
@@ -603,6 +1014,85 @@ foldl = foldx
6031014
6041015-- | Strict left associative fold.
6051016--
1017+-- @foldl' f z xs@ deconstructs @xs@ one element @x@ at a time, applying the
1018+-- function @f@ to the output accumulated till now (the head of the expression)
1019+-- and @x@. The accumulator starts with the initial value @z@ and keeps
1020+-- accumulating elements until the input finishes.
1021+--
1022+-- @
1023+-- head1 == z \`f` x1
1024+-- head2 == head1 \`f` x2
1025+-- head3 == head2 \`f` x3
1026+-- ...
1027+-- foldl' f z xs == head(n-1) \`f` xn
1028+-- @
1029+--
1030+-- Recursively building a left associated expression:
1031+--
1032+-- @
1033+-- foldl' f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
1034+-- @
1035+--
1036+-- When the outermost operation in the fold function is a (left associated)
1037+-- constructor, foldl' consumes the whole input to construct the new structure
1038+-- buffered in memory in the reverse order of the input. For example:
1039+--
1040+-- @
1041+-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
1042+-- [4,3,2,1]
1043+-- @
1044+--
1045+-- When the outermost operation is a function, @foldl'@ results in a left
1046+-- associated expression which is incrementally reduced at each step of the
1047+-- fold, thus never building the whole expression in memory. For example:
1048+--
1049+-- @
1050+-- > S.foldl' (+) 0 $ S.fromList [1,2,3,4]
1051+-- 10
1052+-- @
1053+--
1054+-- @
1055+-- 0 + 1
1056+-- (0 + 1) + 2
1057+-- ((0 + 1) + 2) + 3
1058+-- (((0 + 1) + 2) + 3) + 4
1059+-- @
1060+-- @
1061+-- 0 + 1 => reduce to 1
1062+-- 1 + 2 => reduce to 3
1063+-- 3 + 3 => reduce to 6
1064+-- 6 + 4 => reduce to 10
1065+-- @
1066+--
1067+-- We can stop recursion and terminate the fold by yielding an expression that
1068+-- is independent of the input element:
1069+--
1070+-- >> S.foldl' (\acc x -> if acc >= 8 then acc else x + acc) 0 $ S.fromList [4,1,3,undefined]
1071+-- >8
1072+--
1073+-- To stop on encountering the number 3 we will have to store the previous
1074+-- number in the accumulator so that we do not depend on the current input:
1075+--
1076+-- @
1077+-- > S.foldl' (\(acc, prev) x -> if prev == 3 then (acc,prev) else (x + acc,x))
1078+-- (0,0) $ S.fromList \[4,1,3,undefined]
1079+-- (8,3)
1080+-- @
1081+--
1082+-- To map the fold operation to stateful or event-driven programming, we can
1083+-- consider @z@ as the initial state and the stream being folded as a stream of
1084+-- events, thus @foldl'@ processes all the events in the stream updating the
1085+-- state on each event and then ultimately returning the final state.
1086+--
1087+-- The arguments to the folding function (@b -> a -> b@) are in the head and
1088+-- tail order of the output expression, @b@ is the head and @a@ is the tail.
1089+-- Remember, in a left fold the zero is on the left, at the head of the
1090+-- expression.
1091+--
1092+-- IMPORTANT: 'foldl'' evaluates the accumulator to WHNF. To avoid building
1093+-- lazy expressions inside the accumulator, and to help GHC optimize better, it
1094+-- is recommended that a strict data structure is used for accumulator.
1095+--
6061096-- @since 0.2.0
6071097{-# INLINE foldl' #-}
6081098foldl' :: Monad m => (b -> a -> b) -> b -> SerialT m a -> m b
@@ -622,7 +1112,6 @@ foldl1' step m = do
6221112 res <- foldl' step h t
6231113 return $ Just res
6241114
625−-- XXX replace the recursive "go" with explicit continuations.
6261115-- | Like 'foldx', but with a monadic step function.
6271116--
6281117-- @since 0.2.0
@@ -654,6 +1143,8 @@ null = K.null
6541143
6551144-- | Extract the first element of the stream, if any.
6561145--
1146+-- > head = (!! 0)
1147+--
6571148-- @since 0.1.0
6581149{-# INLINE head #-}
6591150head :: Monad m => SerialT m a -> m (Maybe a)
@@ -675,6 +1166,8 @@ init m = K.init (K.adapt m)
6751166
6761167-- | Extract the last element of the stream, if any.
6771168--
1169+-- > last xs = xs !! (length xs - 1)
1170+--
6781171-- @since 0.1.1
6791172{-# INLINE last #-}
6801173last :: Monad m => SerialT m a -> m (Maybe a)
@@ -722,75 +1215,99 @@ any p m = S.any p (toStreamS m)
7221215and :: Monad m => SerialT m Bool -> m Bool
7231216and = all (==True)
7241217
725−-- | Determines wheter at least one element of a boolean stream is True.
1218+-- | Determines whether at least one element of a boolean stream is True.
7261219--
7271220-- @since 0.5.0
7281221{-# INLINE or #-}
7291222or :: Monad m => SerialT m Bool -> m Bool
7301223or = any (==True)
7311224
732−-- | Determine the sum of all elements of a stream of numbers
1225+-- | Determine the sum of all elements of a stream of numbers. Returns @0@ when
1226+-- the stream is empty. Note that this is not numerically stable for floating
1227+-- point numbers.
7331228--
7341229-- @since 0.1.0
7351230{-# INLINE sum #-}
7361231sum :: (Monad m, Num a) => SerialT m a -> m a
7371232sum = foldl' (+) 0
7381233
739−-- | Determine the product of all elements of a stream of numbers
1234+-- | Determine the product of all elements of a stream of numbers. Returns @1@
1235+-- when the stream is empty.
7401236--
7411237-- @since 0.1.1
7421238{-# INLINE product #-}
7431239product :: (Monad m, Num a) => SerialT m a -> m a
7441240product = foldl' (*) 1
7451241
746−-- | Determine the minimum element in a stream.
1242+-- |
1243+-- @
1244+-- minimum = 'minimumBy' compare
1245+-- @
1246+--
1247+-- Determine the minimum element in a stream.
7471248--
7481249-- @since 0.1.0
7491250{-# INLINE minimum #-}
7501251minimum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
7511252minimum m = S.minimum (toStreamS m)
7521253
753−-- | Non-overloaded version of minimum.
1254+-- | Determine the minimum element in a stream using the supplied comparison
1255+-- function.
7541256--
1257+-- @since 0.6.0
7551258{-# INLINE minimumBy #-}
7561259minimumBy :: Monad m => (a -> a -> Ordering) -> SerialT m a -> m (Maybe a)
7571260minimumBy cmp m = S.minimumBy cmp (toStreamS m)
7581261
759−-- | Determine the maximum element in a stream.
1262+-- |
1263+-- @
1264+-- maximum = 'maximumBy' compare
1265+-- @
1266+--
1267+-- Determine the maximum element in a stream.
7601268--
1269+-- @since 0.1.0
7611270{-# INLINE maximum #-}
7621271maximum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
7631272maximum m = S.maximum (toStreamS m)
7641273
765−-- | Non-overloaded version of maximum.
1274+-- | Determine the maximum element in a stream using the supplied comparison
1275+-- function.
7661276--
1277+-- @since 0.6.0
7671278{-# INLINE maximumBy #-}
7681279maximumBy :: Monad m => (a -> a -> Ordering) -> SerialT m a -> m (Maybe a)
7691280maximumBy cmp m = S.maximumBy cmp (toStreamS m)
7701281
771−-- | Looks the element at the given index.
1282+-- | Lookup the element at the given index.
7721283--
1284+-- @since 0.6.0
7731285{-# INLINE (!!) #-}
7741286(!!) :: Monad m => SerialT m a -> Int -> m (Maybe a)
775−m !! i = (toStreamS m) S.!! i
1287+m !! i = toStreamS m S.!! i
7761288
777−-- | Looks the given key up, treating the given stream as an association list.
1289+-- | In a stream of (key-value) pairs @(a, b)@, return the value @b@ of the
1290+-- first pair where the key equals the given value @a@.
1291+--
1292+-- > lookup = snd <$> find ((==) . fst)
7781293--
7791294-- @since 0.5.0
7801295{-# INLINE lookup #-}
7811296lookup :: (Monad m, Eq a) => a -> SerialT m (a, b) -> m (Maybe b)
7821297lookup a m = S.lookup a (toStreamS m)
7831298
784−-- | Returns the first element of the stream satisfying the given predicate,
785−-- if any.
1299+-- | Like 'findM' but with a non-monadic predicate.
1300+--
1301+-- > find p = findM (return . p)
7861302--
7871303-- @since 0.5.0
7881304{-# INLINE find #-}
7891305find :: Monad m => (a -> Bool) -> SerialT m a -> m (Maybe a)
7901306find p m = S.find p (toStreamS m)
7911307
792−-- | Like 'find' but with a monadic predicate.
1308+-- | Returns the first element that satisfies the given predicate.
7931309--
1310+-- @since 0.6.0
7941311{-# INLINE findM #-}
7951312findM :: Monad m => (a -> m Bool) -> SerialT m a -> m (Maybe a)
7961313findM p m = S.findM p (toStreamS m)
@@ -803,8 +1320,7 @@ findM p m = S.findM p (toStreamS m)
8031320findIndices :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m Int
8041321findIndices p m = fromStreamS $ S.findIndices p (toStreamS m)
8051322
806−-- | Gives the index of the first stream element satisfying the given
807−-- preficate.
1323+-- | Returns the first index that satisfies the given predicate.
8081324--
8091325-- @since 0.5.0
8101326{-# INLINE findIndex #-}
@@ -819,46 +1335,74 @@ findIndex p = head . findIndices p
8191335elemIndices :: (IsStream t, Eq a, Monad m) => a -> t m a -> t m Int
8201336elemIndices a = findIndices (==a)
8211337
822−-- | Gives the first index of an element in the stream, which equals the given.
1338+-- | Returns the first index where a given value is found in the stream.
1339+--
1340+-- > elemIndex a = findIndex (== a)
8231341--
8241342-- @since 0.5.0
8251343{-# INLINE elemIndex #-}
8261344elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
827−elemIndex a = findIndex (==a)
1345+elemIndex a = findIndex (== a)
8281346
1347+-- | Map each element to a stream and then flatten the results into a single
1348+-- stream.
1349+--
1350+-- > concatMap f = concatMapM (return . f)
1351+--
1352+-- @since 0.6.0
8291353{-# INLINE concatMap #-}
830−concatMap :: Monad m => (a -> SerialT m b) -> SerialT m a -> SerialT m b
1354+concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
8311355concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
8321356
1357+-- | Map each element to a stream using a monadic function and then flatten the
1358+-- results into a single stream.
1359+--
1360+-- @since 0.6.0
8331361{-# INLINE concatMapM #-}
834−concatMapM :: Monad m => (a -> m (SerialT m b)) -> SerialT m a -> SerialT m b
1362+concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
8351363concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
8361364
8371365------------------------------------------------------------------------------
8381366-- Substreams
8391367------------------------------------------------------------------------------
8401368
841−-- | Takes two streams and returns 'True' iff the first stream is a prefix
842−-- of the second.
1369+-- | Returns 'True' if the first stream is the same as or a prefix of the
1370+-- second.
1371+--
1372+-- @
1373+-- > S.isPrefixOf (S.fromList "hello") (S.fromList "hello" :: SerialT IO Char)
1374+-- True
1375+-- @
8431376--
1377+-- @since 0.6.0
8441378{-# INLINE isPrefixOf #-}
8451379isPrefixOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
8461380isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
8471381
848−-- | Takes two streams and returns 'True' if all the elements of the
849−-- first stream occur, in order, in the second stream. The elements do
850−-- not have to occur consecutively.
1382+-- | Returns 'True' if all the elements of the first stream occur, in order, in
1383+-- the second stream. The elements do not have to occur consecutively. A stream
1384+-- is treated as a subsequence of itself.
1385+--
1386+-- @
1387+-- > S.isSubsequenceOf (S.fromList "hlo") (S.fromList "hello" :: SerialT IO Char)
1388+-- True
1389+-- @
8511390--
1391+-- @since 0.6.0
8521392{-# INLINE isSubsequenceOf #-}
8531393isSubsequenceOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
8541394isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)
8551395
856−-- | Drops the given prefix from a stream. Returns Nothing if the stream
857−-- doesn't start with the prefix given.
1396+-- | Drops the given prefix from a stream. Returns 'Nothing' if the stream does
1397+-- not start with the given prefix. Returns @Just nil@ when the prefix is the
1398+-- same as the stream.
8581399--
1400+-- @since 0.6.0
8591401{-# INLINE stripPrefix #-}
860−stripPrefix :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m (Maybe (t m a))
861−stripPrefix m1 m2 = fmap (fmap fromStreamD) $
1402+stripPrefix
1403+ :: (Eq a, IsStream t, Monad m)
1404+ => t m a -> t m a -> m (Maybe (t m a))
1405+stripPrefix m1 m2 = fmap fromStreamD <$>
8621406 D.stripPrefix (toStreamD m1) (toStreamD m2)
8631407
8641408------------------------------------------------------------------------------
@@ -878,14 +1422,24 @@ mapM_ f m = S.mapM_ f $ toStreamS m
8781422-- Conversions
8791423------------------------------------------------------------------------------
8801424
881−-- | Convert a stream into a list in the underlying monad.
1425+-- |
1426+-- @
1427+-- toList = S.foldr (:) []
1428+-- @
1429+--
1430+-- Convert a stream into a list in the underlying monad. Same as:
8821431--
8831432-- @since 0.1.0
8841433{-# INLINE toList #-}
8851434toList :: Monad m => SerialT m a -> m [a]
8861435toList = P.toList
8871436
888−-- | Write a stream of Strings to an IO Handle.
1437+-- |
1438+-- @
1439+-- toHandle h = S.mapM_ $ hPutStrLn h
1440+-- @
1441+--
1442+-- Write a stream of Strings to an IO Handle.
8891443--
8901444-- @since 0.1.0
8911445toHandle :: MonadIO m => IO.Handle -> SerialT m String -> m ()
@@ -917,56 +1471,116 @@ scanx = K.scanx
9171471scan :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
9181472scan = scanx
9191473
920−-- | Like 'scanl'' but with a monadic step function.
1474+-- XXX this needs to be concurrent
1475+-- | Like 'scanl'' but with a monadic fold function.
9211476--
9221477-- @since 0.4.0
9231478{-# INLINE scanlM' #-}
9241479scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
9251480scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
9261481
927−-- | Strict left scan. Like 'foldl'', but returns the folded value at each
928−-- step, generating a stream of all intermediate fold results. The first
929−-- element of the stream is the user supplied initial value, and the last
930−-- element of the stream is the same as the result of 'foldl''.
1482+-- | Strict left scan.
1483+--
1484+-- @
1485+-- > S.toList $ S.scanl' (+) 0 $ fromList [1,2,3,4]
1486+-- [0,1,3,6,10]
1487+-- @
1488+--
1489+-- @
1490+-- > S.toList $ S.scanl' (flip (:)) [] $ S.fromList [1,2,3,4]
1491+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
1492+-- @
1493+--
1494+-- The output of 'scanl'' is the initial value of the accumulator followed by
1495+-- all the intermediate steps and the final result of 'foldl''.
1496+--
1497+-- By streaming the accumulated state after each fold step, we can share the
1498+-- state across multiple stages of stream composition. Each stage can modify or
1499+-- extend the state, do some processing with it and emit it for the next stage,
1500+-- thus modularizing the stream processing. This can be useful in
1501+-- stateful or event-driven programming.
1502+--
1503+-- Consider the following example, computing the sum and the product of the
1504+-- elements in a stream in one go using a @foldl'@:
1505+--
1506+-- @
1507+-- > S.foldl' (\\(s, p) x -> (s + x, p * x)) (0,1) $ S.fromList \[1,2,3,4]
1508+-- (10,24)
1509+-- @
1510+--
1511+-- Using @scanl'@ we can compute the sum in the first stage and pass it down to
1512+-- the next stage for computing the product:
1513+--
1514+-- @
1515+-- > S.foldl' (\\(_, p) (s, x) -> (s, p * x)) (0,1)
1516+-- $ S.scanl' (\\(s, _) x -> (s + x, x)) (0,1)
1517+-- $ S.fromList \[1,2,3,4]
1518+-- (10,24)
1519+-- @
1520+--
1521+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
1522+-- lazy expressions inside the accumulator, and to help GHC optimize better, it
1523+-- is recommended that a strict data structure is used for accumulator.
9311524--
9321525-- @since 0.2.0
9331526{-# INLINE scanl' #-}
9341527scanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
9351528scanl' step z m = fromStreamS $ S.scanl' step z $ toStreamS m
9361529
937−-- | Like 'scanlM'' but with a non-strict accumulator.
1530+-- XXX enable once the signature (monadic zero) change is settled
1531+-- | Like scanl' but does not stream the initial value of the accumulator.
9381532--
939−{-# INLINE scanlM #-}
940−scanlM :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
941−scanlM step begin m = fromStreamD $ D.scanlM step begin $ toStreamD m
1533+-- > postscanl' f z xs = S.drop 1 $ scanl' f z xs
1534+--
1535+-- @since 0.6.0
1536+{-# INLINE _postscanl' #-}
1537+_postscanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
1538+_postscanl' step z m = fromStreamD $ D.postscanl' step z $ toStreamD m
1539+
1540+-- XXX this needs to be concurrent
1541+-- | Like postscanl' but with a monadic step function.
1542+--
1543+-- @since 0.6.0
1544+{-# INLINE _postscanlM' #-}
1545+_postscanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
1546+_postscanlM' step z m = fromStreamD $ D.postscanlM' step z $ toStreamD m
9421547
943−-- | Like 'scanl'' but with a non-strict accumulator.
944−{-# INLINE scanl #-}
945−scanl :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
946−scanl step z m = fromStreamD $ D.scanl step z $ toStreamD m
1548+-- XXX prescanl does not sound very useful, enable only if there is a
1549+-- compelling use case.
1550+--
1551+-- | Like scanl' but does not stream the final value of the accumulator.
1552+--
1553+-- @since 0.6.0
1554+{-# INLINE _prescanl' #-}
1555+_prescanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
1556+_prescanl' step z m = fromStreamD $ D.prescanl' step z $ toStreamD m
9471557
948−-- | Left scan with a monadic step function. Uses the first element of
949−-- the stream as the starting value. Returns an empty stream if the
950−-- given stream is empty.
1558+-- XXX this needs to be concurrent
1559+-- | Like postscanl' but with a monadic step function.
9511560--
952−{-# INLINE scanl1M #-}
953−scanl1M :: (IsStream t, Monad m) => (a -> a -> m a) -> t m a -> t m a
954−scanl1M step m = fromStreamD $ D.scanl1M step $ toStreamD m
1561+-- @since 0.6.0
1562+{-# INLINE _prescanlM' #-}
1563+_prescanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
1564+_prescanlM' step z m = fromStreamD $ D.prescanlM' step z $ toStreamD m
9551565
956−-- | Like 'scanl1M' but with a strict accumulator.
1566+-- XXX this needs to be concurrent
1567+-- | Like 'scanl1'' but with a monadic step function.
9571568--
1569+-- @since 0.6.0
9581570{-# INLINE scanl1M' #-}
9591571scanl1M' :: (IsStream t, Monad m) => (a -> a -> m a) -> t m a -> t m a
9601572scanl1M' step m = fromStreamD $ D.scanl1M' step $ toStreamD m
9611573
962−-- | Left scan, uses the first element of the stream as the starting
963−-- value. Returns an empty stream if the given stream is empty.
964−{-# INLINE scanl1 #-}
965−scanl1 :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> t m a
966−scanl1 step m = fromStreamD $ D.scanl1 step $ toStreamD m
967−
968−-- | Like 'scanl1'' but with a strict accumulator.
1574+-- | Like 'scanl'' but for a non-empty stream. The first element of the stream
1575+-- is used as the initial value of the accumulator. Does nothing if the stream
1576+-- is empty.
9691577--
1578+-- @
1579+-- > S.toList $ S.scanl1 (+) $ fromList [1,2,3,4]
1580+-- [1,3,6,10]
1581+-- @
1582+--
1583+-- @since 0.6.0
9701584{-# INLINE scanl1' #-}
9711585scanl1' :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> t m a
9721586scanl1' step m = fromStreamD $ D.scanl1' step $ toStreamD m
@@ -997,10 +1611,19 @@ filterM p m = fromStreamD $ D.filterM p $ toStreamD m
9971611
9981612-- | Drop repeated elements that are adjacent to each other.
9991613--
1614+-- @since 0.6.0
10001615{-# INLINE uniq #-}
10011616uniq :: (Eq a, IsStream t, Monad m) => t m a -> t m a
10021617uniq = fromStreamD . D.uniq . toStreamD
10031618
1619+-- | Ensures that all the elements of the stream are identical and then returns
1620+-- that unique element.
1621+--
1622+-- @since 0.6.0
1623+{-# INLINE the #-}
1624+the :: (Eq a, Monad m) => SerialT m a -> m (Maybe a)
1625+the m = S.the (toStreamS m)
1626+
10041627-- | Take first 'n' elements from the stream and discard the rest.
10051628--
10061629-- @since 0.1.0
@@ -1049,10 +1672,18 @@ dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
10491672-- Transformation by Mapping
10501673------------------------------------------------------------------------------
10511674
1052−-- | Replace each element of the stream with the result of a monadic action
1053−-- applied on the element.
1675+-- |
1676+-- @
1677+-- mapM f = sequence . map f
1678+-- @
1679+--
1680+-- Apply a monadic function to each element of the stream and replace it with
1681+-- the output of the resulting action.
10541682--
10551683-- @
1684+-- > runStream $ S.mapM putStr $ S.fromList ["a", "b", "c"]
1685+-- abc
1686+--
10561687-- runStream $ S.replicateM 10 (return 1)
10571688-- & (serially . S.mapM (\\x -> threadDelay 1000000 >> print x))
10581689--
@@ -1072,10 +1703,18 @@ mapM = K.mapM
10721703mapMSerial :: Monad m => (a -> m b) -> SerialT m a -> SerialT m b
10731704mapMSerial = Serial.mapM
10741705
1075−-- | Reduce a stream of monadic actions to a stream of the output of those
1076−-- actions.
1706+-- |
1707+-- @
1708+-- sequence = mapM id
1709+-- @
1710+--
1711+-- Replace the elements of a stream of monadic actions with the outputs of
1712+-- those actions.
10771713--
10781714-- @
1715+-- > runStream $ S.sequence $ S.fromList [putStr "a", putStr "b", putStrLn "c"]
1716+-- abc
1717+--
10791718-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
10801719-- & (serially . S.sequence)
10811720--
@@ -1142,17 +1781,34 @@ reverse m = fromStream $ go K.nil (toStream m)
11421781-- Transformation by Inserting
11431782------------------------------------------------------------------------------
11441783
1145−-- | Generate a stream by performing the monadic action inbetween all elements
1146−-- of the given stream.
1784+-- | Generate a stream by performing a monadic action between consecutive
1785+-- elements of the given stream.
1786+--
1787+-- /Concurrent (do not use with 'parallely' on infinite streams)/
1788+--
1789+-- @
1790+-- > S.toList $ S.intersperseM (putChar \'a' >> return ',') $ S.fromList "hello"
1791+-- aaaa"h,e,l,l,o"
1792+-- @
11471793--
11481794-- @since 0.5.0
11491795{-# INLINE intersperseM #-}
11501796intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
11511797intersperseM = K.intersperseM
11521798
1153−-- | Inserts @x@ before the first element in the Stream that is
1154−-- less than @x@, based on the user supplied comparison function.
1799+-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
1800+-- @stream@ that is less than @elem@ when compared using @cmp@.
11551801--
1802+-- @
1803+-- insertBy cmp x = 'mergeBy' cmp ('yield' x)
1804+-- @
1805+--
1806+-- @
1807+-- > S.toList $ S.insertBy compare 2 $ S.fromList [1,3,5]
1808+-- [1,2,3,5]
1809+-- @
1810+--
1811+-- @since 0.6.0
11561812{-# INLINE insertBy #-}
11571813insertBy ::
11581814 (IsStream t, Monad m) => (a -> a -> Ordering) -> a -> t m a -> t m a
@@ -1162,9 +1818,15 @@ insertBy cmp x m = fromStreamS $ S.insertBy cmp x (toStreamS m)
11621818-- Deleting
11631819------------------------------------------------------------------------------
11641820
1165−-- | Deletes the first occurence of @x@ in the Stream that satisfies the
1166−-- equality predicate.
1821+-- | Deletes the first occurence of the element in the stream that satisfies
1822+-- the given equality predicate.
11671823--
1824+-- @
1825+-- > S.toList $ S.deleteBy (==) 3 $ S.fromList [1,3,3,5]
1826+-- [1,3,5]
1827+-- @
1828+--
1829+-- @since 0.6.0
11681830{-# INLINE deleteBy #-}
11691831deleteBy :: (IsStream t, Monad m) => (a -> a -> Bool) -> a -> t m a -> t m a
11701832deleteBy cmp x m = fromStreamS $ S.deleteBy cmp x (toStreamS m)
@@ -1173,20 +1835,38 @@ deleteBy cmp x m = fromStreamS $ S.deleteBy cmp x (toStreamS m)
11731835-- Zipping
11741836------------------------------------------------------------------------------
11751837
1176−-- | Pair each element in a stream with its index.
1838+-- |
1839+-- > indexed = S.zipWith (,) (S.intFrom 0)
1840+--
1841+-- Pair each element in a stream with its index.
1842+--
1843+-- @
1844+-- > S.toList $ S.indexed $ S.fromList "hello"
1845+-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
1846+-- @
11771847--
1848+-- @since 0.6.0
11781849{-# INLINE indexed #-}
11791850indexed :: (IsStream t, Monad m) => t m a -> t m (Int, a)
11801851indexed = fromStreamD . D.indexed . toStreamD
11811852
1182−-- | Pair each element in a stream with its index, starting from the
1853+-- |
1854+-- > indexedR n = S.zipWith (,) (S.intFromThen n (n - 1))
1855+--
1856+-- Pair each element in a stream with its index, starting from the
11831857-- given index @n@ and counting down.
11841858--
1859+-- @
1860+-- > S.toList $ S.indexedR 10 $ S.fromList "hello"
1861+-- [(9,'h'),(8,'e'),(7,'l'),(6,'l'),(5,'o')]
1862+-- @
1863+--
1864+-- @since 0.6.0
11851865{-# INLINE indexedR #-}
11861866indexedR :: (IsStream t, Monad m) => Int -> t m a -> t m (Int, a)
11871867indexedR n = fromStreamD . D.indexedR n . toStreamD
11881868
1189−-- | Zip two streams serially using a monadic zipping function.
1869+-- | Like 'zipWith' but using a monadic zipping function.
11901870--
11911871-- @since 0.4.0
11921872{-# INLINABLE zipWithM #-}
@@ -1195,6 +1875,11 @@ zipWithM f m1 m2 = fromStreamS $ S.zipWithM f (toStreamS m1) (toStreamS m2)
11951875
11961876-- | Zip two streams serially using a pure zipping function.
11971877--
1878+-- @
1879+-- > S.toList $ S.zipWith (+) (S.fromList [1,2,3]) (S.fromList [4,5,6])
1880+-- [5,7,9]
1881+-- @
1882+--
11981883-- @since 0.1.0
11991884{-# INLINABLE zipWith #-}
12001885zipWith :: (IsStream t, Monad m) => (a -> b -> c) -> t m a -> t m b -> t m c
@@ -1204,36 +1889,115 @@ zipWith f m1 m2 = fromStreamS $ S.zipWith f (toStreamS m1) (toStreamS m2)
12041889-- Comparison
12051890------------------------------------------------------------------------------
12061891
1207−-- | Compare two streams for equality using a equality function.
1892+-- | Compare two streams for equality using an equality function.
12081893--
1209−-- @since 0.5.3
1894+-- @since 0.6.0
12101895{-# INLINABLE eqBy #-}
1211−eqBy :: Monad m => (a -> b -> Bool) -> SerialT m a -> SerialT m b -> m Bool
1896+eqBy :: (IsStream t, Monad m) => (a -> b -> Bool) -> t m a -> t m b -> m Bool
12121897eqBy = P.eqBy
12131898
1214−-- | Compare two streams using a comparison function.
1899+-- | Compare two streams lexicographically using a comparison function.
12151900--
1216−-- @since 0.5.3
1901+-- @since 0.6.0
12171902{-# INLINABLE cmpBy #-}
12181903cmpBy
1219− :: Monad m
1220− => (a -> b -> Ordering) -> SerialT m a -> SerialT m b -> m Ordering
1904+ :: (IsStream t, Monad m)
1905+ => (a -> b -> Ordering) -> t m a -> t m b -> m Ordering
12211906cmpBy = P.cmpBy
12221907
1223−-- | Non-overloaded version of merge.
1908+------------------------------------------------------------------------------
1909+-- Merge
1910+------------------------------------------------------------------------------
1911+
1912+-- | Merge two streams using a comparison function. The head elements of both
1913+-- the streams are compared and the smaller of the two elements is emitted, if
1914+-- both elements are equal then the element from the first stream is used
1915+-- first.
1916+--
1917+-- If the streams are sorted in ascending order, the resulting stream would
1918+-- also remain sorted in ascending order.
1919+--
1920+-- @
1921+-- > S.toList $ S.mergeBy compare (S.fromList [1,3,5]) (S.fromList [2,4,6,8])
1922+-- [1,2,3,4,5,6,8]
1923+-- @
1924+--
1925+-- @since 0.6.0
12241926{-# INLINABLE mergeBy #-}
12251927mergeBy ::
12261928 (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
12271929mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
12281930
1229−-- | Merge two sorted streams.
1931+-- | Like 'mergeBy' but with a monadic comparison function.
1932+--
1933+-- Merge two streams randomly:
1934+--
1935+-- @
1936+-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
1937+-- > S.toList $ S.mergeByM randomly (S.fromList [1,1,1,1]) (S.fromList [2,2,2,2])
1938+-- [2,1,2,2,2,1,1,1]
1939+-- @
1940+--
1941+-- Merge two streams in a proportion of 2:1:
1942+--
1943+-- @
1944+-- proportionately m n = do
1945+-- ref <- newIORef $ cycle $ concat [replicate m LT, replicate n GT]
1946+-- return $ \\_ _ -> do
1947+-- r <- readIORef ref
1948+-- writeIORef ref $ tail r
1949+-- return $ head r
1950+--
1951+-- main = do
1952+-- f <- proportionately 2 1
1953+-- xs <- S.toList $ S.mergeByM f (S.fromList [1,1,1,1,1,1]) (S.fromList [2,2,2])
1954+-- print xs
1955+-- @
1956+-- @
1957+-- [1,1,2,1,1,2,1,1,2]
1958+-- @
1959+--
1960+-- @since 0.6.0
1961+{-# INLINABLE mergeByM #-}
1962+mergeByM
1963+ :: (IsStream t, Monad m)
1964+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
1965+mergeByM f m1 m2 = fromStreamS $ S.mergeByM f (toStreamS m1) (toStreamS m2)
1966+
1967+-- Holding this back for now, we may want to use the name "merge" differently
1968+{-
1969+-- | Same as @'mergeBy' 'compare'@.
1970+--
1971+-- @
1972+-- > S.toList $ S.merge (S.fromList [1,3,5]) (S.fromList [2,4,6,8])
1973+-- [1,2,3,4,5,6,8]
1974+-- @
1975+--
1976+-- @since 0.6.0
12301977{-# INLINABLE merge #-}
12311978merge ::
12321979 (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
12331980merge = mergeBy compare
1234−
1235−-- | Ensures all the elements are identical and then returns
1236−-- that unique element.
1237−{-# INLINE the #-}
1238−the :: (Eq a, Monad m) => SerialT m a -> m (Maybe a)
1239−the m = S.the (toStreamS m)
1981+-}
1982+
1983+-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
1984+-- merged are generated concurrently).
1985+--
1986+-- @since 0.6.0
1987+mergeAsyncBy :: (IsStream t, MonadAsync m)
1988+ => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1989+mergeAsyncBy f m1 m2 = K.fromStream $ K.Stream $ \st stp sng yld -> do
1990+ ma <- mkAsync' (rstState st) m1
1991+ mb <- mkAsync' (rstState st) m2
1992+ K.unStream (K.toStream (K.mergeBy f ma mb)) (rstState st) stp sng yld
1993+
1994+-- | Like 'mergeByM' but merges concurrently (i.e. both the elements being
1995+-- merged are generated concurrently).
1996+--
1997+-- @since 0.6.0
1998+mergeAsyncByM :: (IsStream t, MonadAsync m)
1999+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
2000+mergeAsyncByM f m1 m2 = K.fromStream $ K.Stream $ \st stp sng yld -> do
2001+ ma <- mkAsync' (rstState st) m1
2002+ mb <- mkAsync' (rstState st) m2
2003+ K.unStream (K.toStream (K.mergeByM f ma mb)) (rstState st) stp sng yld
src/Streamly/Streams/Instances.hs+2−3
@@ -89,8 +89,7 @@ instance Show a => Show (STREAM Identity a) where { \
8989instance Read a => Read (STREAM Identity a) where { \
9090 readPrec = parens $ prec 10 $ do { \
9191 Ident "fromList" <- lexP; \
92− dl <- readPrec; \
93− return (fromList dl) }; \
92+ fromList <$> readPrec }; \
9493 readListPrec = readListPrecDefault }; \
9594 \
9695instance (a ~ Char) => IsString (STREAM Identity a) where { \
@@ -119,4 +118,4 @@ instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
119118instance Traversable (STREAM Identity) where { \
120119 {-# INLINE traverse #-}; \
121120 traverse f s = runIdentity $ P.foldr consA (pure mempty) s \
122− where { consA x ys = liftA2 (K.cons) (f x) ys }}
121+ where { consA x ys = liftA2 K.cons (f x) ys }}
src/Streamly/Streams/Prelude.hs+7−3
@@ -72,9 +72,13 @@ toStreamS = S.fromStreamK . toStream
7272-- Conversions
7373------------------------------------------------------------------------------
7474
75−-- | Construct a stream from a list containing pure values. More efficient list
76−-- specific implementation of 'K.fromFoldable' as it works well with fusion
77−-- optimization.
75+-- |
76+-- @
77+-- fromList = 'Prelude.foldr' 'K.cons' 'K.nil'
78+-- @
79+--
80+-- Construct a stream from a list of pure values. This is more efficient than
81+-- 'K.fromFoldable' for serial streams.
7882--
7983-- @since 0.4.0
8084{-# INLINE_EARLY fromList #-}
src/Streamly/Streams/Serial.hs+11−1
@@ -198,7 +198,17 @@ instance Monad m => Monad (SerialT m) where
198198mapM :: (IsStream t, Monad m) => (a -> m b) -> t m a -> t m b
199199mapM f m = fromStream $ D.toStreamK $ D.mapM f $ D.fromStreamK (toStream m)
200200
201−-- | Same as 'fmap'.
201+-- |
202+-- @
203+-- map = fmap
204+-- @
205+--
206+-- Same as 'fmap'.
207+--
208+-- @
209+-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
210+-- [2,3,4]
211+-- @
202212--
203213-- @since 0.4.0
204214{-# INLINE map #-}
src/Streamly/Streams/StreamD.hs+253−29
@@ -12,6 +12,7 @@
1212-- |
1313-- Module : Streamly.Streams.StreamD
1414-- Copyright : (c) 2018 Harendra Kumar
15+-- Copyright : (c) Roman Leshchinskiy 2008-2010
1516--
1617-- License : BSD3
1718-- Maintainer : harendra.kumar@gmail.com
@@ -27,7 +28,7 @@
2728-- import qualified Streamly.Streams.StreamD as D
2829-- @
2930
30−-- Some of functions in this file have been adapted from the vector
31+-- Some of the functions in this file have been adapted from the vector
3132-- library, https://hackage.haskell.org/package/vector.
3233
3334module Streamly.Streams.StreamD
@@ -51,15 +52,31 @@ module Streamly.Streams.StreamD
5152 -- ** Specialized Generation
5253 -- | Generate a monadic stream from a seed.
5354 , repeat
54− , enumFromStepN
55+ , replicate
56+ , replicateM
57+ , fromIndices
58+ , fromIndicesM
59+ , generate
60+ , generateM
61+
62+ -- ** Enumerations
63+ , intFromStep
64+ , intFrom
65+ , intFromThen
66+ , intFromTo
67+ , intFromThenTo
68+
69+ , numFromStep
70+ , numFrom
71+ , numFromThen
72+ , fracFromTo
73+ , fracFromThenTo
5574
5675 -- ** Conversions
5776 -- | Transform an input structure into a stream.
5877 -- | Direct style stream does not support @fromFoldable@.
5978 , yield
6079 , yieldM
61− , generate
62− , generateM
6380 , fromList
6481 , fromListM
6582 , fromStreamK
@@ -120,6 +137,13 @@ module Streamly.Streams.StreamD
120137 , scanl1M
121138 , scanl1
122139
140+ , prescanl'
141+ , prescanlM'
142+ , postscanl
143+ , postscanlM
144+ , postscanl'
145+ , postscanlM'
146+
123147 -- * Filtering
124148 , filter
125149 , filterM
@@ -158,6 +182,7 @@ module Streamly.Streams.StreamD
158182
159183 -- * Merging
160184 , mergeBy
185+ , mergeByM
161186
162187 -- * Transformation comprehensions
163188 , the
@@ -170,7 +195,7 @@ import Prelude
170195 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
171196 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
172197 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
173− (!!), scanl, scanl1, concatMap)
198+ (!!), scanl, scanl1, concatMap, replicate, enumFromTo)
174199
175200import Streamly.SVar (MonadAsync, State(..), defState, rstState)
176201
@@ -262,14 +287,143 @@ unfoldr f = unfoldrM (return . f)
262287repeat :: Monad m => a -> Stream m a
263288repeat x = Stream (\_ _ -> return $ Yield x ()) ()
264289
265−{-# INLINE_NORMAL enumFromStepN #-}
266−enumFromStepN :: (Num a, Monad m) => a -> a -> Int -> Stream m a
267−enumFromStepN from stride n =
268− from `seq` stride `seq` n `seq` Stream step (from, n)
290+{-# INLINE_NORMAL replicateM #-}
291+replicateM :: Monad m => Int -> m a -> Stream m a
292+replicateM n p = Stream step n
293+ where
294+ {-# INLINE_LATE step #-}
295+ step _ i | i <= 0 = return Stop
296+ | otherwise = do
297+ x <- p
298+ return $ Yield x (i - 1)
299+
300+{-# INLINE_NORMAL replicate #-}
301+replicate :: Monad m => Int -> a -> Stream m a
302+replicate n x = replicateM n (return x)
303+
304+-- This would not work properly for floats, therefore we put an Integral
305+-- constraint.
306+-- | Can be used to enumerate unbounded integrals. This does not check for
307+-- overflow or underflow for bounded integrals.
308+{-# INLINE_NORMAL intFromStep #-}
309+intFromStep :: (Integral a, Monad m) => a -> a -> Stream m a
310+intFromStep from stride =
311+ from `seq` stride `seq` Stream step from
269312 where
270313 {-# INLINE_LATE step #-}
271− step _ (x, i) | i > 0 = return $ Yield x (x + stride, i - 1)
272− | otherwise = return Stop
314+ step _ !x = return $ Yield x $! (x + stride)
315+
316+-- We are assuming that "to" is constrained by the type to be within
317+-- max/min bounds.
318+{-# INLINE intFromTo #-}
319+intFromTo :: (Monad m, Integral a) => a -> a -> Stream m a
320+intFromTo from to = takeWhile (<= to) $ intFromStep from 1
321+
322+{-# INLINE intFrom #-}
323+intFrom :: (Monad m, Integral a, Bounded a) => a -> Stream m a
324+intFrom from = intFromTo from maxBound
325+
326+data EnumState a = EnumInit | EnumYield a a a | EnumStop
327+
328+{-# INLINE_NORMAL intFromThenToUp #-}
329+intFromThenToUp :: (Monad m, Integral a) => a -> a -> a -> Stream m a
330+intFromThenToUp from next to = Stream step EnumInit
331+ where
332+ {-# INLINE_LATE step #-}
333+ step _ EnumInit =
334+ return $
335+ if to < next
336+ then if to < from
337+ then Stop
338+ else Yield from EnumStop
339+ else -- from <= next <= to
340+ let stride = next - from
341+ in Skip $ EnumYield from stride (to - stride)
342+
343+ step _ (EnumYield x stride toMinus) =
344+ return $
345+ if x > toMinus
346+ then Yield x EnumStop
347+ else Yield x $ EnumYield (x + stride) stride toMinus
348+
349+ step _ EnumStop = return Stop
350+
351+{-# INLINE_NORMAL intFromThenToDn #-}
352+intFromThenToDn :: (Monad m, Integral a) => a -> a -> a -> Stream m a
353+intFromThenToDn from next to = Stream step EnumInit
354+ where
355+ {-# INLINE_LATE step #-}
356+ step _ EnumInit =
357+ return $ if to > next
358+ then if to > from
359+ then Stop
360+ else Yield from EnumStop
361+ else -- from >= next >= to
362+ let stride = next - from
363+ in Skip $ EnumYield from stride (to - stride)
364+
365+ step _ (EnumYield x stride toMinus) =
366+ return $
367+ if x < toMinus
368+ then Yield x EnumStop
369+ else Yield x $ EnumYield (x + stride) stride toMinus
370+
371+ step _ EnumStop = return Stop
372+
373+-- {-# NOINLINE [1] efdtInt #-}
374+{-# INLINE_NORMAL intFromThenTo #-}
375+intFromThenTo :: (Monad m, Integral a) => a -> a -> a -> Stream m a
376+intFromThenTo from next to
377+ | next >= from = intFromThenToUp from next to
378+ | otherwise = intFromThenToDn from next to
379+
380+{-# INLINE_NORMAL intFromThen #-}
381+intFromThen :: (Monad m, Integral a, Bounded a) => a -> a -> Stream m a
382+intFromThen from next =
383+ if next > from
384+ then intFromThenToUp from next maxBound
385+ else intFromThenToDn from next minBound
386+
387+-- For floating point numbers if the increment is less than the precision then
388+-- it just gets lost. Therefore we cannot always increment it correctly by just
389+-- repeated addition.
390+-- 9007199254740992 + 1 + 1 :: Double => 9.007199254740992e15
391+-- 9007199254740992 + 2 :: Double => 9.007199254740994e15
392+
393+-- Instead we accumulate the increment counter and compute the increment
394+-- everytime before adding it to the starting number.
395+--
396+-- This works for Integrals as well as floating point numbers, but intFromStep
397+-- is faster for integrals.
398+{-# INLINE_NORMAL numFromStep #-}
399+numFromStep :: (Monad m, Num a) => a -> a -> Stream m a
400+numFromStep from stride = Stream step 0
401+ where
402+ {-# INLINE_LATE step #-}
403+ step _ !i = return $ (Yield $! (from + i * stride)) $! (i + 1)
404+
405+{-# INLINE_NORMAL numFrom #-}
406+numFrom :: (Monad m, Num a) => a -> Stream m a
407+numFrom from = numFromStep from 1
408+
409+{-# INLINE_NORMAL numFromThen #-}
410+numFromThen :: (Monad m, Num a) => a -> a -> Stream m a
411+numFromThen from next = numFromStep from (next - from)
412+
413+-- We cannot write a general function for Num. The only way to write code
414+-- portable between the two is to use a 'Real' constraint and convert between
415+-- Fractional and Integral using fromRational which is horribly slow.
416+{-# INLINE_NORMAL fracFromTo #-}
417+fracFromTo :: (Monad m, Fractional a, Ord a) => a -> a -> Stream m a
418+fracFromTo from to = takeWhile (<= to + 1/2) $ numFromStep from 1
419+
420+{-# INLINE_NORMAL fracFromThenTo #-}
421+fracFromThenTo :: (Monad m, Fractional a, Ord a) => a -> a -> a -> Stream m a
422+fracFromThenTo from next to = takeWhile predicate $ numFromThen from next
423+ where
424+ mid = (next - from) / 2
425+ predicate | next >= from = (<= to + mid)
426+ | otherwise = (>= to + mid)
273427
274428-------------------------------------------------------------------------------
275429-- Generation by Conversion
@@ -293,9 +447,18 @@ yieldM m = Stream step True
293447 step _ True = m >>= \x -> return $ Yield x False
294448 step _ False = return Stop
295449
296−{-# INLINE generate #-}
297−generate :: Monad m => Int -> (Int -> a) -> Stream m a
298−generate n gen = generateM n (return . gen)
450+{-# INLINE_NORMAL fromIndicesM #-}
451+fromIndicesM :: Monad m => (Int -> m a) -> Stream m a
452+fromIndicesM gen = Stream step 0
453+ where
454+ {-# INLINE_LATE step #-}
455+ step _ i = do
456+ x <- gen i
457+ return $ Yield x (i + 1)
458+
459+{-# INLINE fromIndices #-}
460+fromIndices :: Monad m => (Int -> a) -> Stream m a
461+fromIndices gen = fromIndicesM (return . gen)
299462
300463{-# INLINE_NORMAL generateM #-}
301464generateM :: Monad m => Int -> (Int -> m a) -> Stream m a
@@ -304,9 +467,13 @@ generateM n gen = n `seq` Stream step 0
304467 {-# INLINE_LATE step #-}
305468 step _ i | i < n = do
306469 x <- gen i
307− return $ Yield x (i+1)
470+ return $ Yield x (i + 1)
308471 | otherwise = return Stop
309472
473+{-# INLINE generate #-}
474+generate :: Monad m => Int -> (Int -> a) -> Stream m a
475+generate n gen = generateM n (return . gen)
476+
310477-- XXX we need the MonadAsync constraint because of a rewrite rule.
311478-- | Convert a list of monadic actions to a 'Stream'
312479{-# INLINE_LATE fromListM #-}
@@ -613,14 +780,11 @@ findIndices p (Stream step state) = Stream step' (state, 0)
613780 Skip s -> Skip (s, i+1)
614781 Stop -> Stop
615782
616−{-# INLINE concatMap #-}
617−concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
618−concatMap f = concatMapM (return . f)
619−
620783{-# INLINE_NORMAL concatMapM #-}
621784concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
622785concatMapM f (Stream step state) = Stream step' (Left state)
623786 where
787+ {-# INLINE_LATE step' #-}
624788 step' gst (Left st) = do
625789 r <- step (rstState gst) st
626790 case r of
@@ -639,6 +803,10 @@ concatMapM f (Stream step state) = Stream step' (Left state)
639803 return $ Skip (Right (Stream inner_step inner_s, st))
640804 Stop -> return $ Skip (Left st)
641805
806+{-# INLINE concatMap #-}
807+concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
808+concatMap f = concatMapM (return . f)
809+
642810------------------------------------------------------------------------------
643811-- Substreams
644812------------------------------------------------------------------------------
@@ -686,7 +854,9 @@ isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
686854 Stop -> return False
687855
688856{-# INLINE_NORMAL stripPrefix #-}
689−stripPrefix :: (Eq a, Monad m) => Stream m a -> Stream m a -> m (Maybe (Stream m a))
857+stripPrefix
858+ :: (Eq a, Monad m)
859+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
690860stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
691861 where
692862 go (sa, sb, Nothing) = do
@@ -750,6 +920,43 @@ fromStreamD = K.fromStream . toStreamK
750920-- Transformation by Folding (Scans)
751921------------------------------------------------------------------------------
752922
923+-- XXX Is a prescan useful, discarding the last step does not sound useful? I
924+-- am not sure about the utility of this function, so this is implemented but
925+-- not exposed. We can expose it if someone provides good reasons why this is
926+-- useful.
927+--
928+-- XXX We have to execute the stream one step ahead to know that we are at the
929+-- last step. The vector implementation of prescan executes the last fold step
930+-- but does not yield the result. This means we have executed the effect but
931+-- discarded value. This does not sound right. In this implementation we are
932+-- not executing the last fold step.
933+{-# INLINE_NORMAL prescanlM' #-}
934+prescanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
935+prescanlM' f mz (Stream step state) = Stream step' (state, mz)
936+ where
937+ {-# INLINE_LATE step' #-}
938+ step' gst (st, prev) = do
939+ r <- step (rstState gst) st
940+ case r of
941+ Yield x s -> do
942+ acc <- prev
943+ return $ Yield acc (s, f acc x)
944+ Skip s -> return $ Skip (s, prev)
945+ Stop -> return Stop
946+
947+{-# INLINE prescanl' #-}
948+prescanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b
949+prescanl' f z = prescanlM' (\a b -> return (f a b)) (return z)
950+
951+-- XXX if we make the initial value of the accumulator monadic then should we
952+-- execute it even if the stream is empty? In that case we would have generated
953+-- the effect but discarded the value, but that is what a fold does when the
954+-- stream is empty. Whatever we decide, need to reconcile this with prescan.
955+-- If we execute the initial value here without even using it then it is ok to
956+-- execute the last step there as well without using the value.
957+-- Looking at the duality with right fold, in case of right fold we always
958+-- perform the action when the construction terminates, so in case of left fold
959+-- we should perform it only when the reduction starts.
753960{-# INLINE_NORMAL postscanlM' #-}
754961postscanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
755962postscanlM' fstep begin (Stream step state) =
@@ -765,6 +972,10 @@ postscanlM' fstep begin (Stream step state) =
765972 Skip s -> return $ Skip (s, acc)
766973 Stop -> return Stop
767974
975+{-# INLINE_NORMAL postscanl' #-}
976+postscanl' :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a
977+postscanl' f = postscanlM' (\a b -> return (f a b))
978+
768979{-# INLINE_NORMAL postscanlM #-}
769980postscanlM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
770981postscanlM fstep begin (Stream step state) = Stream step' (state, begin)
@@ -779,6 +990,10 @@ postscanlM fstep begin (Stream step state) = Stream step' (state, begin)
779990 Skip s -> return $ Skip (s, acc)
780991 Stop -> return Stop
781992
993+{-# INLINE_NORMAL postscanl #-}
994+postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a
995+postscanl f = postscanlM (\a b -> return (f a b))
996+
782997{-# INLINE_NORMAL scanlM' #-}
783998scanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
784999scanlM' fstep begin s = begin `seq` (begin `cons` postscanlM' fstep begin s)
@@ -1198,9 +1413,11 @@ cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
11981413-- Merging
11991414------------------------------------------------------------------------------
12001415
1201−{-# INLINE_NORMAL mergeBy #-}
1202−mergeBy :: (Monad m) => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
1203−mergeBy f (Stream stepa ta) (Stream stepb tb) =
1416+{-# INLINE_NORMAL mergeByM #-}
1417+mergeByM
1418+ :: (Monad m)
1419+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
1420+mergeByM cmp (Stream stepa ta) (Stream stepb tb) =
12041421 Stream step (Just ta, Just tb, Nothing, Nothing)
12051422 where
12061423 {-# INLINE_LATE step #-}
@@ -1221,20 +1438,27 @@ mergeBy f (Stream stepa ta) (Stream stepb tb) =
12211438 Stop -> Skip (sa, Nothing, a, Nothing)
12221439
12231440 -- both the values are available
1224− step _ (sa, sb, Just a, Just b) =
1225− return $ case f a b of
1226− LT -> Yield a (sa, sb, Nothing, Just b)
1227− _ -> Yield b (sa, sb, Just a, Nothing)
1441+ step _ (sa, sb, Just a, Just b) = do
1442+ res <- cmp a b
1443+ return $ case res of
1444+ GT -> Yield b (sa, sb, Just a, Nothing)
1445+ _ -> Yield a (sa, sb, Nothing, Just b)
12281446
12291447 -- one of the values is missing, corresponding stream is done
1230− step _ (Nothing, sb, Nothing, Just b) = do
1448+ step _ (Nothing, sb, Nothing, Just b) =
12311449 return $ Yield b (Nothing, sb, Nothing, Nothing)
12321450
1233− step _ (sa, Nothing, Just a, Nothing) = do
1451+ step _ (sa, Nothing, Just a, Nothing) =
12341452 return $ Yield a (sa, Nothing, Nothing, Nothing)
12351453
12361454 step _ (Nothing, Nothing, Nothing, Nothing) = return Stop
12371455
1456+{-# INLINE mergeBy #-}
1457+mergeBy
1458+ :: (Monad m)
1459+ => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
1460+mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
1461+
12381462------------------------------------------------------------------------------
12391463-- Transformation comprehensions
12401464------------------------------------------------------------------------------
src/Streamly/Streams/StreamK.hs+67−51
@@ -59,6 +59,8 @@ module Streamly.Streams.StreamK
5959
6060 -- ** Specialized Generation
6161 , repeat
62+ , replicate
63+ , replicateM
6264
6365 -- ** Conversions
6466 , yield
@@ -139,6 +141,7 @@ module Streamly.Streams.StreamK
139141
140142 -- ** Merging
141143 , mergeBy
144+ , mergeByM
142145
143146 -- ** Transformation comprehensions
144147 , the
@@ -166,7 +169,7 @@ import Prelude
166169 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
167170 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
168171 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
169− foldr1, (!!))
172+ foldr1, (!!), replicate)
170173import qualified Prelude
171174
172175import Streamly.SVar
@@ -472,19 +475,38 @@ yieldM m = fromStream $ Stream $ \_ _ single _ -> m >>= single
472475once :: (Monad m, IsStream t) => m a -> t m a
473476once = yieldM
474477
475−-- | Generate an infinite stream by repeating a pure value.
476−-- Can be expressed as @cycle1 . yield@.
478+-- |
479+-- @
480+-- repeatM = fix . cons
481+-- repeatM = cycle1 . yield
482+-- @
483+--
484+-- Generate an infinite stream by repeating a pure value.
477485--
478486-- @since 0.4.0
479487repeat :: IsStream t => a -> t m a
480488repeat a = let x = cons a x in x
481489
490+replicateM :: (IsStream t, MonadAsync m) => Int -> m a -> t m a
491+replicateM n m = go n
492+ where
493+ go cnt = if cnt <= 0 then nil else m |: go (cnt - 1)
494+
495+replicate :: IsStream t => Int -> a -> t m a
496+replicate n a = go n
497+ where
498+ go cnt = if cnt <= 0 then nil else a `cons` go (cnt - 1)
499+
482500-------------------------------------------------------------------------------
483501-- Conversions
484502-------------------------------------------------------------------------------
485503
486−-- | Construct a stream from a 'Foldable' containing pure values. Same as
487−-- @'Prelude.foldr' 'cons' 'nil'@.
504+-- |
505+-- @
506+-- fromFoldable = 'Prelude.foldr' 'cons' 'nil'
507+-- @
508+--
509+-- Construct a stream from a 'Foldable' containing pure values:
488510--
489511-- @since 0.2.0
490512{-# INLINE fromFoldable #-}
@@ -556,6 +578,8 @@ foldr1 step m = do
556578-- left fold, but applies a user supplied extraction function (the third
557579-- argument) to the folded value at the end. This is designed to work with the
558580-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
581+--
582+-- Note that the accumulator is always evaluated including the initial value.
559583{-# INLINE foldx #-}
560584foldx :: (IsStream t, Monad m)
561585 => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
@@ -563,6 +587,8 @@ foldx step begin done m = get $ go (toStream m) begin
563587 where
564588 {-# NOINLINE get #-}
565589 get m1 =
590+ -- XXX we are not strictly evaluating the accumulator here. Is this
591+ -- okay?
566592 let single = return . done
567593 in unStream m1 undefined undefined single undefined
568594
@@ -719,7 +745,9 @@ minimum m = go Nothing (toStream m)
719745 in unStream m1 defState stop single yieldk
720746
721747{-# INLINE minimumBy #-}
722−minimumBy :: (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> m (Maybe a)
748+minimumBy
749+ :: (IsStream t, Monad m)
750+ => (a -> a -> Ordering) -> t m a -> m (Maybe a)
723751minimumBy cmp m = go Nothing (toStream m)
724752 where
725753 go Nothing m1 =
@@ -1076,54 +1104,42 @@ zipWithM f m1 m2 = fromStream $ go (toStream m1) (toStream m2)
10761104-- Merging
10771105------------------------------------------------------------------------------
10781106
1079−data MergeWithState l r o
1080− = MergeBoth l r
1081− | MergeWithLeft l r o
1082− | MergeWithRight l r o
1083−
10841107{-# INLINE mergeByS #-}
1085−mergeByS :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
1086−mergeByS cmp m1 m2 = go (MergeBoth m1 m2)
1087− where
1088− go (MergeBoth mx my) =
1089− Stream $ \st stp sng yld -> do
1090− let yield1 a ra =
1091− let single2 b = sng (cond a b)
1092− stop2 = yld a ra
1093− yield2 b rb =
1094− case cmp a b of
1095− LT -> yld a (go (MergeWithRight ra rb b))
1096− _ -> yld b (go (MergeWithLeft ra rb a))
1097− in unStream my (rstState st) stop2 single2 yield2
1098− single1 a = sng a
1099− stop1 = unStream my (rstState st) stp sng yld
1100− unStream mx (rstState st) stop1 single1 yield1
1101− go (MergeWithLeft mx my x) =
1102− Stream $ \st _ sng yld -> do
1103− let single2 b = sng (cond x b)
1104− stop2 = yld x mx
1105− yield2 b rb =
1106− case cmp x b of
1107− LT -> yld x (go (MergeWithRight mx rb b))
1108− _ -> yld b (go (MergeWithLeft mx rb x))
1109− unStream my (rstState st) stop2 single2 yield2
1110− go (MergeWithRight mx my x) =
1111− Stream $ \st _ sng yld -> do
1112− let single1 a = sng (cond a x)
1113− stop1 = yld x my
1114− yield1 a ra =
1115− case cmp a x of
1116− LT -> yld a (go (MergeWithRight ra my x))
1117− _ -> yld x (go (MergeWithLeft ra my a))
1118− unStream mx (rstState st) stop1 single1 yield1
1119− cond x y =
1120− if cmp x y == LT
1121− then x
1122− else y
1108+mergeByS
1109+ :: Monad m
1110+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
1111+mergeByS cmp = go
1112+ where
1113+ go mx my = Stream $ \st stp sng yld -> do
1114+ let mergeWithY a ra =
1115+ let stop2 = unStream mx (rstState st) stp sng yld
1116+ single2 b = do
1117+ r <- cmp a b
1118+ case r of
1119+ GT -> yld b (go (a `cons` ra) nil)
1120+ _ -> yld a (go ra (b `cons` nil))
1121+ yield2 b rb = do
1122+ r <- cmp a b
1123+ case r of
1124+ GT -> yld b (go (a `cons` ra) rb)
1125+ _ -> yld a (go ra (b `cons` rb))
1126+ in unStream my (rstState st) stop2 single2 yield2
1127+ let stopX = unStream my (rstState st) stp sng yld
1128+ singleX a = mergeWithY a nil
1129+ yieldX = mergeWithY
1130+ unStream mx (rstState st) stopX singleX yieldX
1131+
1132+{-# INLINABLE mergeByM #-}
1133+mergeByM
1134+ :: (IsStream t, Monad m)
1135+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
1136+mergeByM f m1 m2 = fromStream $ mergeByS f (toStream m1) (toStream m2)
11231137
11241138{-# INLINABLE mergeBy #-}
1125−mergeBy :: IsStream t => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1126−mergeBy f m1 m2 = fromStream $ mergeByS f (toStream m1) (toStream m2)
1139+mergeBy
1140+ :: (IsStream t, Monad m)
1141+ => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1142+mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
11271143
11281144------------------------------------------------------------------------------
11291145-- Transformation comprehensions
src/Streamly/Streams/Zip.hs+4−4
@@ -136,8 +136,8 @@ TRAVERSABLE_INSTANCE(ZipSerialM)
136136-- Parallel Zipping
137137------------------------------------------------------------------------------
138138
139−-- | Zip two streams concurrently (i.e. both the elements being zipped are
140−-- generated concurrently) using a pure zipping function.
139+-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
140+-- are generated concurrently.
141141--
142142-- @since 0.1.0
143143zipAsyncWith :: (IsStream t, MonadAsync m)
@@ -147,8 +147,8 @@ zipAsyncWith f m1 m2 = fromStream $ Stream $ \st stp sng yld -> do
147147 mb <- mkAsync' (rstState st) m2
148148 unStream (toStream (K.zipWith f ma mb)) (rstState st) stp sng yld
149149
150−-- | Zip two streams asyncly (i.e. both the elements being zipped are generated
151−-- concurrently) using a monadic zipping function.
150+-- | Like 'zipWithM' but zips concurrently i.e. both the streams being zipped
151+-- are generated concurrently.
152152--
153153-- @since 0.4.0
154154zipAsyncWithM :: (IsStream t, MonadAsync m)
src/Streamly/Tutorial.hs+4−6
@@ -235,19 +235,17 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
235235-- asynchronous consumption the outputs are consumed as they arrive i.e. first
236236-- come first serve order.
237237--
238−-- @
239238-- +------------+--------------+--------------+--------------+
240239-- | Type | Execution | Consumption | Concurrency |
241240-- +============+==============+==============+==============+
242−-- | 'Serial' | Serial | Serial | None |
241+-- | 'Serial' | Serial | Serial | None |
243242-- +------------+--------------+--------------+--------------+
244−-- | 'Ahead' | Asynchronous | Serial | bounded |
243+-- | 'Ahead' | Asynchronous | Serial | bounded |
245244-- +------------+--------------+--------------+--------------+
246−-- | 'Async' | Asynchronous | Asynchronous | bounded |
245+-- | 'Async' | Asynchronous | Asynchronous | bounded |
247246-- +------------+--------------+--------------+--------------+
248−-- | 'Parallel' | Asynchronous | Asynchronous | unbounded |
247+-- | 'Parallel' | Asynchronous | Asynchronous | unbounded |
249248-- +------------+--------------+--------------+--------------+
250−-- @
251249--
252250-- All these types can be freely inter-converted using type conversion
253251-- combinators or type annotations, without any cost, to achieve the desired
test/Prop.hs+293−251
@@ -20,7 +20,8 @@ import Data.Maybe (mapMaybe)
2020import GHC.Word (Word8)
2121
2222import Test.Hspec.QuickCheck
23−import Test.QuickCheck (counterexample, Property, withMaxSuccess)
23+import Test.QuickCheck
24+ (counterexample, Property, withMaxSuccess, forAll, choose)
2425import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
2526
2627import Test.Hspec as H
@@ -74,43 +75,86 @@ listEquals eq stream list = do
7475 )
7576 assert (stream `eq` list)
7677
77−constructWithReplicateM
78− :: IsStream t
79− => (t IO Int -> SerialT IO Int)
78+-------------------------------------------------------------------------------
79+-- Construction operations
80+-------------------------------------------------------------------------------
81+
82+constructWithLen
83+ :: (Show a, Eq a)
84+ => (Int -> t IO a)
85+ -> (Int -> [a])
86+ -> (t IO a -> SerialT IO a)
8087 -> Word8
8188 -> Property
82−constructWithReplicateM op len = withMaxSuccess maxTestCount $
89+constructWithLen mkStream mkList op len = withMaxSuccess maxTestCount $
8390 monadicIO $ do
84− let x = return (1 :: Int)
85− stream <- run $ (S.toList . op) (S.replicateM (fromIntegral len) x)
86− list <- run $ replicateM (fromIntegral len) x
91+ stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
92+ let list = mkList (fromIntegral len)
8793 listEquals (==) stream list
8894
89−constructWithReplicate
90− :: IsStream t
91− => (t IO Int -> SerialT IO Int)
95+constructWithLenM
96+ :: (Int -> t IO Int)
97+ -> (Int -> IO [Int])
98+ -> (t IO Int -> SerialT IO Int)
9299 -> Word8
93100 -> Property
94−constructWithReplicate op len = withMaxSuccess maxTestCount $
101+constructWithLenM mkStream mkList op len = withMaxSuccess maxTestCount $
95102 monadicIO $ do
96− let x = (1 :: Int)
97− stream <- run $ (S.toList . op) (S.replicate (fromIntegral len) x)
98− let list = replicate (fromIntegral len) x
103+ stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
104+ list <- run $ mkList (fromIntegral len)
99105 listEquals (==) stream list
100106
101−transformFromList
102− :: (Eq b, Show b) =>
103− ([a] -> t IO a)
104− -> ([b] -> [b] -> Bool)
105− -> ([a] -> [b])
106− -> (t IO a -> SerialT IO b)
107− -> [a]
107+constructWithReplicate, constructWithReplicateM, constructWithIntFromThenTo
108+ :: IsStream t
109+ => (t IO Int -> SerialT IO Int)
110+ -> Word8
108111 -> Property
109−transformFromList constr eq listOp op a =
110− monadicIO $ do
111− stream <- run ((S.toList . op) (constr a))
112− let list = listOp a
113− listEquals eq stream list
112+
113+constructWithReplicateM = constructWithLenM stream list
114+ where list = flip replicateM (return 1 :: IO Int)
115+ stream = flip S.replicateM (return 1 :: IO Int)
116+
117+constructWithReplicate = constructWithLen stream list
118+ where list = flip replicate (1 :: Int)
119+ stream = flip S.replicate (1 :: Int)
120+
121+constructWithIntFromThenTo op l =
122+ forAll (choose (minBound, maxBound)) $ \from ->
123+ forAll (choose (minBound, maxBound)) $ \next ->
124+ forAll (choose (minBound, maxBound)) $ \to ->
125+ let list len = take len [from,next..to]
126+ stream len = S.take len $ S.intFromThenTo from next to
127+ in constructWithLen stream list op l
128+
129+#if __GLASGOW_HASKELL__ >= 806
130+-- XXX try very small steps close to 0
131+constructWithDoubleFromThenTo
132+ :: IsStream t
133+ => (t IO Double -> SerialT IO Double)
134+ -> Word8
135+ -> Property
136+constructWithDoubleFromThenTo op l =
137+ forAll (choose (-9007199254740999,9007199254740999)) $ \from ->
138+ forAll (choose (-9007199254740999,9007199254740999)) $ \next ->
139+ forAll (choose (-9007199254740999,9007199254740999)) $ \to ->
140+ let list len = take len [from,next..to]
141+ stream len = S.take len $ S.fracFromThenTo from next to
142+ in constructWithLen stream list op l
143+#endif
144+
145+constructWithIterate :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
146+constructWithIterate t = do
147+ it "iterate" $
148+ (S.toList . t . S.take 100) (S.iterate (+ 1) (0 :: Int))
149+ `shouldReturn` take 100 (iterate (+ 1) 0)
150+ it "iterateM" $ do
151+ let addM y = return (y + 1)
152+ S.toList . t . S.take 100 $ S.iterateM addM (0 :: Int)
153+ `shouldReturn` take 100 (iterate (+ 1) 0)
154+
155+-------------------------------------------------------------------------------
156+-- Concurrent generation
157+-------------------------------------------------------------------------------
114158
115159mvarExcHandler :: String -> BlockedIndefinitelyOnMVar -> IO ()
116160mvarExcHandler label BlockedIndefinitelyOnMVar =
@@ -213,6 +257,32 @@ concurrentUnfoldrM eq op n =
213257 return x
214258 listEquals eq stream list
215259
260+concurrentOps
261+ :: IsStream t
262+ => ([Word8] -> t IO Word8)
263+ -> String
264+ -> ([Word8] -> [Word8] -> Bool)
265+ -> (t IO Word8 -> SerialT IO Word8)
266+ -> Spec
267+concurrentOps constr desc eq t = do
268+ let prop1 d p = prop d $ withMaxSuccess maxTestCount p
269+
270+ prop1 (desc <> " fromFoldableM") $ concurrentFromFoldable eq t
271+ prop1 (desc <> " unfoldrM") $ concurrentUnfoldrM eq t
272+ -- we pass it the length of the stream n and an mvar mv.
273+ -- The stream is [0..n]. The threads communicate in such a way that the
274+ -- actions coming first in the stream are dependent on the last action. So
275+ -- if the stream is not processed concurrently it will block forever.
276+ -- Note that if the size of the stream is bigger than the thread limit
277+ -- then it will block even if it is concurrent.
278+ prop1 (desc <> " mapM") $
279+ concurrentMapM constr eq $ \n mv stream ->
280+ t $ S.mapM (mvarSequenceOp mv n) stream
281+
282+-------------------------------------------------------------------------------
283+-- Concurrent Application
284+-------------------------------------------------------------------------------
285+
216286concurrentApplication :: IsStream t
217287 => ([Word8] -> [Word8] -> Bool)
218288 -> (t IO Word8 -> SerialT IO Word8)
@@ -269,6 +339,10 @@ concurrentFoldrApplication n =
269339 sourceUnfoldrM1 n |&. S.foldrM (\x xs -> return (x : xs)) []
270340 listEquals (==) stream list
271341
342+-------------------------------------------------------------------------------
343+-- Transformation operations
344+-------------------------------------------------------------------------------
345+
272346transformCombineFromList
273347 :: Semigroup (t IO Int)
274348 => ([Int] -> t IO Int)
@@ -288,143 +362,13 @@ transformCombineFromList constr eq listOp t op a b c =
288362 let list = a <> listOp (b <> c)
289363 listEquals eq stream list
290364
291−foldFromList
292− :: ([Int] -> t IO Int)
293− -> (t IO Int -> SerialT IO Int)
294− -> ([Int] -> [Int] -> Bool)
295− -> [Int]
296− -> Property
297−foldFromList constr op eq = transformFromList constr eq id op
298−
299−eliminateOp
300− :: (Show a, Eq a)
301− => ([s] -> t IO s)
302− -> ([s] -> a)
303− -> (t IO s -> IO a)
304− -> [s]
305− -> Property
306−eliminateOp constr listOp op a =
307− monadicIO $ do
308− stream <- run $ op (constr a)
309− let list = listOp a
310− equals (==) stream list
311−
312−elemOp
313− :: ([Word8] -> t IO Word8)
314− -> (t IO Word8 -> SerialT IO Word8)
315− -> (Word8 -> SerialT IO Word8 -> IO Bool)
316− -> (Word8 -> [Word8] -> Bool)
317− -> (Word8, [Word8])
318− -> Property
319−elemOp constr op streamOp listOp (x, xs) =
320− monadicIO $ do
321− stream <- run $ (streamOp x . op) (constr xs)
322− let list = listOp x xs
323− equals (==) stream list
324−
325−functorOps
326− :: Functor (t IO)
327− => ([Int] -> t IO Int)
328− -> String
329− -> ([Int] -> [Int] -> Bool)
330− -> (t IO Int -> SerialT IO Int)
331− -> Spec
332−functorOps constr desc eq t = do
333− prop (desc <> " id") $ transformFromList constr eq id t
334− prop (desc <> " fmap (+1)") $ transformFromList constr eq (fmap (+1)) $ t . fmap (+1)
335−
336−transformOps
337− :: IsStream t
338− => ([Int] -> t IO Int)
339− -> String
340− -> ([Int] -> [Int] -> Bool)
341− -> (t IO Int -> SerialT IO Int)
342− -> Spec
343−transformOps constr desc eq t = do
344− let transform = transformFromList constr eq
345− -- Filtering
346− prop (desc <> " filter False") $
347− transform (filter (const False)) $ t . S.filter (const False)
348− prop (desc <> " filter True") $
349− transform (filter (const True)) $ t . S.filter (const True)
350− prop (desc <> " filter even") $
351− transform (filter even) $ t . S.filter even
352−
353− prop (desc <> " take maxBound") $
354− transform (take maxBound) $ t . S.take maxBound
355− prop (desc <> " take 0") $ transform (take 0) $ t . S.take 0
356− prop (desc <> " take 1") $ transform (take 1) $ t . S.take 1
357− prop (desc <> " take 10") $ transform (take 10) $ t . S.take 10
358−
359− prop (desc <> " takeWhile True") $
360− transform (takeWhile (const True)) $ t . S.takeWhile (const True)
361− prop (desc <> " takeWhile False") $
362− transform (takeWhile (const False)) $ t . S.takeWhile (const False)
363− prop (desc <> " takeWhile > 0") $
364− transform (takeWhile (> 0)) $ t . S.takeWhile (> 0)
365−
366− let f x = if odd x then Just (x + 100) else Nothing
367− prop (desc <> " mapMaybe") $ transform (mapMaybe f) $ t . S.mapMaybe f
368−
369− prop (desc <> " drop maxBound") $
370− transform (drop maxBound) $ t . S.drop maxBound
371− prop (desc <> " drop 0") $ transform (drop 0) $ t . S.drop 0
372− prop (desc <> " drop 1") $ transform (drop 1) $ t . S.drop 1
373− prop (desc <> " drop 10") $ transform (drop 10) $ t . S.drop 10
374−
375− prop (desc <> " dropWhile True") $
376− transform (dropWhile (const True)) $ t . S.dropWhile (const True)
377− prop (desc <> " dropWhile False") $
378− transform (dropWhile (const False)) $ t . S.dropWhile (const False)
379− prop (desc <> " dropWhile > 0") $
380− transform (dropWhile (> 0)) $ t . S.dropWhile (> 0)
381− prop (desc <> " scan") $ transform (scanl' (+) 0) $ t . S.scanl' (+) 0
382− prop (desc <> " reverse") $ transform reverse $ t . S.reverse
383−
384− prop (desc <> " findIndices") $ transform (findIndices odd) $ t . S.findIndices odd
385− prop (desc <> " elemIndices") $ transform (elemIndices 3) $ t . S.elemIndices 3
386−
387− prop (desc <> " intersperseM") $ transform (intersperse 3) $ t . S.intersperseM (return 3)
388− prop (desc <> " insertBy maxBound") $
389− transform (insertBy compare maxBound) $ t . S.insertBy compare maxBound
390− prop (desc <> " insertBy 0") $
391− transform (insertBy compare 0) $ t . S.insertBy compare 0
392− prop (desc <> " insertBy 4") $
393− transform (insertBy compare 4) $ t . S.insertBy compare 4
394− prop (desc <> " deleteBy (<=) maxBound") $
395− transform (deleteBy (<=) maxBound) $ t . S.deleteBy (<=) maxBound
396− prop (desc <> " deleteBy (<=) 0") $
397− transform (deleteBy (<=) 0) $ t . S.deleteBy (<=) 0
398− prop (desc <> " deleteBy (==) 4") $
399− transform (deleteBy (==) 4) $ t . S.deleteBy (==) 4
400−
401− prop (desc <> " concatMap") $ transform (concatMap (const [1..10])) $
402− t . (\g -> adapt (S.concatMap (const (S.fromList [1..10])) (adapt g)))
403−
404−concurrentOps
405− :: IsStream t
406− => ([Word8] -> t IO Word8)
407− -> String
408− -> ([Word8] -> [Word8] -> Bool)
409− -> (t IO Word8 -> SerialT IO Word8)
410− -> Spec
411−concurrentOps constr desc eq t = do
412− let prop1 d p = prop d $ withMaxSuccess maxTestCount p
413−
414− prop1 (desc <> " fromFoldableM") $ concurrentFromFoldable eq t
415− prop1 (desc <> " unfoldrM") $ concurrentUnfoldrM eq t
416− -- we pass it the length of the stream n and an mvar mv.
417− -- The stream is [0..n]. The threads communicate in such a way that the
418− -- actions coming first in the stream are dependent on the last action. So
419− -- if the stream is not processed concurrently it will block forever.
420− -- Note that if the size of the stream is bigger than the thread limit
421− -- then it will block even if it is concurrent.
422− prop1 (desc <> " mapM") $
423− concurrentMapM constr eq $ \n mv stream ->
424− t $ S.mapM (mvarSequenceOp mv n) stream
425−
426365-- XXX add tests for MonadReader and MonadError etc. In case an SVar is
427366-- accidentally passed through them.
367+--
368+-- This tests transform ops along with detecting illegal sharing of SVar across
369+-- conurrent streams. These tests work for all stream types whereas
370+-- transformCombineOpsOrdered work only for ordered stream types i.e. excluding
371+-- the Async type.
428372transformCombineOpsCommon
429373 :: (IsStream t, Semigroup (t IO Int))
430374 => ([Int] -> t IO Int)
@@ -434,6 +378,7 @@ transformCombineOpsCommon
434378 -> Spec
435379transformCombineOpsCommon constr desc eq t = do
436380 let transform = transformCombineFromList constr eq
381+
437382 -- Filtering
438383 prop (desc <> " filter False") $
439384 transform (filter (const False)) t (S.filter (const False))
@@ -477,6 +422,12 @@ transformCombineOpsCommon constr desc eq t = do
477422 prop (desc <> " dropWhileM False") $
478423 transform (dropWhile (const False)) t (S.dropWhileM (const $ return False))
479424
425+ prop (desc <> " deleteBy (<=) maxBound") $
426+ transform (deleteBy (<=) maxBound) t (S.deleteBy (<=) maxBound)
427+ prop (desc <> " deleteBy (==) 4") $
428+ transform (deleteBy (==) 4) t (S.deleteBy (==) 4)
429+
430+ -- transformation
480431 prop (desc <> " mapM (+1)") $
481432 transform (fmap (+1)) t (S.mapM (\x -> return (x + 1)))
482433
@@ -486,22 +437,34 @@ transformCombineOpsCommon constr desc eq t = do
486437 (S.scanlM' (\_ a -> return a) 0)
487438 prop (desc <> " scanl") $ transform (scanl' (flip const) 0) t
488439 (S.scanl' (flip const) 0)
489− prop (desc <> " scanlM") $ transform (scanl (flip const) 0) t
490− (S.scanlM (\_ a -> return a) 0)
491− prop (desc <> " scanl1") $ transform (scanl1 (flip const)) t
492− (S.scanl1 (flip const))
493− prop (desc <> " scanl1M") $ transform (scanl1 (flip const)) t
494− (S.scanl1M (\_ a -> return a))
495440 prop (desc <> " scanl1'") $ transform (scanl1 (flip const)) t
496441 (S.scanl1' (flip const))
497442 prop (desc <> " scanl1M'") $ transform (scanl1 (flip const)) t
498443 (S.scanl1M' (\_ a -> return a))
499444
445+ let f x = if odd x then Just (x + 100) else Nothing
446+ prop (desc <> " mapMaybe") $ transform (mapMaybe f) t (S.mapMaybe f)
447+
448+ -- reordering
500449 prop (desc <> " reverse") $ transform reverse t S.reverse
501450
451+ -- inserting
502452 prop (desc <> " intersperseM") $
503− transform (intersperse 3) t (S.intersperseM $ return 3)
504−
453+ forAll (choose (minBound, maxBound)) $ \n ->
454+ transform (intersperse n) t (S.intersperseM $ return n)
455+ prop (desc <> " insertBy 0") $
456+ forAll (choose (minBound, maxBound)) $ \n ->
457+ transform (insertBy compare n) t (S.insertBy compare n)
458+
459+ -- multi-stream
460+ prop (desc <> " concatMap") $
461+ forAll (choose (0, 100)) $ \n ->
462+ transform (concatMap (const [1..n]))
463+ t (S.concatMap (const (S.fromList [1..n])))
464+
465+-- transformation tests that can only work reliably for ordered streams i.e.
466+-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
467+-- might yield a different result every time.
505468transformCombineOpsOrdered
506469 :: (IsStream t, Semigroup (t IO Int))
507470 => ([Int] -> t IO Int)
@@ -511,6 +474,7 @@ transformCombineOpsOrdered
511474 -> Spec
512475transformCombineOpsOrdered constr desc eq t = do
513476 let transform = transformCombineFromList constr eq
477+
514478 -- Filtering
515479 prop (desc <> " take 1") $ transform (take 1) t (S.take 1)
516480#ifdef DEVBUILD
@@ -531,15 +495,36 @@ transformCombineOpsOrdered constr desc eq t = do
531495 transform (dropWhile (> 0)) t (S.dropWhile (> 0))
532496 prop (desc <> " scan") $ transform (scanl' (+) 0) t (S.scanl' (+) 0)
533497
534− -- XXX this does not fail when the SVar is shared, need to fix.
535− prop (desc <> " concurrent application") $
536− transform (& fmap (+1)) t (|& S.map (+1))
498+ -- XXX add uniq
499+ prop (desc <> " deleteBy (<=) 0") $
500+ transform (deleteBy (<=) 0) t (S.deleteBy (<=) 0)
537501
538502 prop (desc <> " findIndices") $
539503 transform (findIndices odd) t (S.findIndices odd)
540504 prop (desc <> " elemIndices") $
541505 transform (elemIndices 0) t (S.elemIndices 0)
542506
507+ -- XXX this does not fail when the SVar is shared, need to fix.
508+ prop (desc <> " concurrent application") $
509+ transform (& fmap (+1)) t (|& S.map (+1))
510+
511+-------------------------------------------------------------------------------
512+-- Elimination operations
513+-------------------------------------------------------------------------------
514+
515+eliminateOp
516+ :: (Show a, Eq a)
517+ => ([s] -> t IO s)
518+ -> ([s] -> a)
519+ -> (t IO s -> IO a)
520+ -> [s]
521+ -> Property
522+eliminateOp constr listOp op a =
523+ monadicIO $ do
524+ stream <- run $ op (constr a)
525+ let list = listOp a
526+ equals (==) stream list
527+
543528wrapMaybe :: ([a1] -> a2) -> [a1] -> Maybe a2
544529wrapMaybe f x = if null x then Nothing else Just (f x)
545530
@@ -577,31 +562,42 @@ eliminationOps constr desc t = do
577562 prop (desc <> " sum") $ eliminateOp constr sum $ S.sum . t
578563 prop (desc <> " product") $ eliminateOp constr product $ S.product . t
579564
580− prop (desc <> " maximum") $ eliminateOp constr (wrapMaybe maximum) $ S.maximum . t
581− prop (desc <> " minimum") $ eliminateOp constr (wrapMaybe minimum) $ S.minimum . t
565+ prop (desc <> " maximum") $
566+ eliminateOp constr (wrapMaybe maximum) $ S.maximum . t
567+ prop (desc <> " minimum") $
568+ eliminateOp constr (wrapMaybe minimum) $ S.minimum . t
582569
583570 prop (desc <> " maximumBy compare") $
584− eliminateOp constr (wrapMaybe $ maximumBy compare) $ S.maximumBy compare . t
571+ eliminateOp constr (wrapMaybe $ maximumBy compare) $
572+ S.maximumBy compare . t
585573 prop (desc <> " maximumBy flip compare") $
586574 eliminateOp constr (wrapMaybe $ maximumBy $ flip compare) $
587575 S.maximumBy (flip compare) . t
588576 prop (desc <> " minimumBy compare") $
589− eliminateOp constr (wrapMaybe $ minimumBy compare) $ S.minimumBy compare . t
577+ eliminateOp constr (wrapMaybe $ minimumBy compare) $
578+ S.minimumBy compare . t
590579 prop (desc <> " minimumBy flip compare") $
591580 eliminateOp constr (wrapMaybe $ minimumBy $ flip compare) $
592581 S.minimumBy (flip compare) . t
593582
594− prop (desc <> " findIndex") $ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
595− prop (desc <> " elemIndex") $ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
583+ prop (desc <> " findIndex") $
584+ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
585+ prop (desc <> " elemIndex") $
586+ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
596587
597− prop (desc <> " !! 5") $ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
598− prop (desc <> " !! 4") $ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
588+ prop (desc <> " !! 5") $
589+ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
590+ prop (desc <> " !! 4") $
591+ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
599592
600593 prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
601594 prop (desc <> " lookup") $
602595 eliminateOp constr (lookup 3 . flip zip [1..]) $
603596 S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
597+ prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
604598
599+ -- Multi-stream eliminations
600+ -- Add eqBy, cmpBy
605601 -- XXX Write better tests for substreams.
606602 prop (desc <> " isPrefixOf 10") $ eliminateOp constr (isPrefixOf [1..10]) $
607603 S.isPrefixOf (S.fromList [(1::Int)..10]) . t
@@ -612,16 +608,14 @@ eliminationOps constr desc t = do
612608 (\s -> s >>= maybe (return Nothing) (fmap Just . S.toList)) .
613609 S.stripPrefix (S.fromList [(1::Int)..10]) . t
614610
615− prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
616−
617611-- head/tail/last may depend on the order in case of parallel streams
618612-- so we test these only for serial streams.
619−serialEliminationOps
613+eliminationOpsOrdered
620614 :: ([Int] -> t IO Int)
621615 -> String
622616 -> (t IO Int -> SerialT IO Int)
623617 -> Spec
624−serialEliminationOps constr desc t = do
618+eliminationOpsOrdered constr desc t = do
625619 prop (desc <> " head") $ eliminateOp constr (wrapMaybe head) $ S.head . t
626620 prop (desc <> " tail") $ eliminateOp constr (wrapMaybe tail) $ \x -> do
627621 r <- S.tail (t x)
@@ -635,14 +629,53 @@ serialEliminationOps constr desc t = do
635629 Nothing -> return Nothing
636630 Just s -> Just <$> S.toList s
637631
638−transformOpsWord8
632+elemOp
633+ :: ([Word8] -> t IO Word8)
634+ -> (t IO Word8 -> SerialT IO Word8)
635+ -> (Word8 -> SerialT IO Word8 -> IO Bool)
636+ -> (Word8 -> [Word8] -> Bool)
637+ -> (Word8, [Word8])
638+ -> Property
639+elemOp constr op streamOp listOp (x, xs) =
640+ monadicIO $ do
641+ stream <- run $ (streamOp x . op) (constr xs)
642+ let list = listOp x xs
643+ equals (==) stream list
644+
645+eliminationOpsWord8
639646 :: ([Word8] -> t IO Word8)
640647 -> String
641648 -> (t IO Word8 -> SerialT IO Word8)
642649 -> Spec
643−transformOpsWord8 constr desc t = do
650+eliminationOpsWord8 constr desc t = do
644651 prop (desc <> " elem") $ elemOp constr t S.elem elem
645− prop (desc <> " elem") $ elemOp constr t S.notElem notElem
652+ prop (desc <> " notElem") $ elemOp constr t S.notElem notElem
653+
654+-------------------------------------------------------------------------------
655+-- Semigroup operations
656+-------------------------------------------------------------------------------
657+
658+transformFromList
659+ :: (Eq b, Show b) =>
660+ ([a] -> t IO a)
661+ -> ([b] -> [b] -> Bool)
662+ -> ([a] -> [b])
663+ -> (t IO a -> SerialT IO b)
664+ -> [a]
665+ -> Property
666+transformFromList constr eq listOp op a =
667+ monadicIO $ do
668+ stream <- run ((S.toList . op) (constr a))
669+ let list = listOp a
670+ listEquals eq stream list
671+
672+foldFromList
673+ :: ([Int] -> t IO Int)
674+ -> (t IO Int -> SerialT IO Int)
675+ -> ([Int] -> [Int] -> Bool)
676+ -> [Int]
677+ -> Property
678+foldFromList constr op eq = transformFromList constr eq id op
646679
647680-- XXX concatenate streams of multiple elements rather than single elements
648681semigroupOps
@@ -660,6 +693,25 @@ semigroupOps desc eq t = do
660693 prop (desc <> " <>") $ foldFromList (foldMapWith (<>) singleton) t eq
661694 prop (desc <> " mappend") $ foldFromList (foldMapWith mappend singleton) t eq
662695
696+-------------------------------------------------------------------------------
697+-- Functor operations
698+-------------------------------------------------------------------------------
699+
700+functorOps
701+ :: Functor (t IO)
702+ => ([Int] -> t IO Int)
703+ -> String
704+ -> ([Int] -> [Int] -> Bool)
705+ -> (t IO Int -> SerialT IO Int)
706+ -> Spec
707+functorOps constr desc eq t = do
708+ prop (desc <> " id") $ transformFromList constr eq id t
709+ prop (desc <> " fmap (+1)") $ transformFromList constr eq (fmap (+1)) $ t . fmap (+1)
710+
711+-------------------------------------------------------------------------------
712+-- Applicative operations
713+-------------------------------------------------------------------------------
714+
663715applicativeOps
664716 :: Applicative (t IO)
665717 => ([Int] -> t IO Int)
@@ -673,6 +725,10 @@ applicativeOps constr eq t (a, b) = withMaxSuccess maxTestCount $
673725 let list = (,) <$> a <*> b
674726 listEquals eq stream list
675727
728+-------------------------------------------------------------------------------
729+-- Zip operations
730+-------------------------------------------------------------------------------
731+
676732zipApplicative
677733 :: (IsStream t, Applicative (t IO))
678734 => ([Int] -> t IO Int)
@@ -727,6 +783,10 @@ zipAsyncMonadic constr eq t (a, b) = withMaxSuccess maxTestCount $
727783 listEquals eq stream1 list
728784 listEquals eq stream2 list
729785
786+-------------------------------------------------------------------------------
787+-- Monad operations
788+-------------------------------------------------------------------------------
789+
730790monadThen
731791 :: Monad (t IO)
732792 => ([Int] -> t IO Int)
@@ -755,16 +815,6 @@ monadBind constr eq t (a, b) = withMaxSuccess maxTestCount $
755815 let list = a >>= \x -> (+ x) <$> b
756816 listEquals eq stream list
757817
758−constructWithIterate :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
759−constructWithIterate t = do
760− it "iterate" $
761− (S.toList . t . S.take 100) (S.iterate (+ 1) (0 :: Int))
762− `shouldReturn` take 100 (iterate (+ 1) 0)
763− it "iterateM" $ do
764− let addM y = return (y + 1)
765− S.toList . t . S.take 100 $ S.iterateM addM (0 :: Int)
766− `shouldReturn` take 100 (iterate (+ 1) 0)
767−
768818main :: IO ()
769819main = hspec
770820 $ H.parallel
@@ -838,17 +888,24 @@ main = hspec
838888 zipAsyncOps spec = mapOps spec $ makeOps zipAsyncly
839889
840890 describe "Construction" $ do
891+ serialOps $ prop "serially replicate" . constructWithReplicate
892+
841893 serialOps $ prop "serially replicateM" . constructWithReplicateM
842894 wSerialOps $ prop "wSerially replicateM" . constructWithReplicateM
843895 aheadOps $ prop "aheadly replicateM" . constructWithReplicateM
844896 asyncOps $ prop "asyncly replicateM" . constructWithReplicateM
845897 wAsyncOps $ prop "wAsyncly replicateM" . constructWithReplicateM
846898 parallelOps $ prop "parallely replicateM" . constructWithReplicateM
899+
900+ serialOps $ prop "serially intFromThenTo" .
901+ constructWithIntFromThenTo
902+#if __GLASGOW_HASKELL__ >= 806
903+ serialOps $ prop "serially DoubleFromThenTo" .
904+ constructWithDoubleFromThenTo
905+#endif
847906 -- XXX test for all types of streams
848907 constructWithIterate serially
849−
850− describe "Construction with replicate" $ do
851− serialOps $ prop "serially replicate" . constructWithReplicate
908+ -- XXX add tests for fromIndices
852909
853910 describe "Functor operations" $ do
854911 serialOps $ functorOps S.fromFoldable "serially" (==)
@@ -894,6 +951,7 @@ main = hspec
894951 wAsyncOps $ prop "wAsyncly applicative folded" . applicativeOps folded sortEq
895952 parallelOps $ prop "parallely applicative folded" . applicativeOps folded sortEq
896953
954+ -- XXX add tests for indexed/indexedR
897955 describe "Zip operations" $ do
898956 zipSerialOps $ prop "zipSerially applicative" . zipApplicative S.fromFoldable (==)
899957 zipSerialOps $ prop "zipSerially applicative folded" . zipApplicative folded (==)
@@ -916,6 +974,11 @@ main = hspec
916974 parallelOps $ prop "zip monadic parallely" . zipMonadic S.fromFoldable (==)
917975 parallelOps $ prop "zip monadic parallely folded" . zipMonadic folded (==)
918976
977+ -- XXX add merge tests like zip tests
978+ -- for mergeBy, we can split a list randomly into two lists and
979+ -- then merge them, it should result in original list
980+ -- describe "Merge operations" $ do
981+
919982 describe "Monad operations" $ do
920983 serialOps $ prop "serially monad then" . monadThen S.fromFoldable (==)
921984 wSerialOps $ prop "wSerially monad then" . monadThen S.fromFoldable sortEq
@@ -945,43 +1008,6 @@ main = hspec
9451008 wAsyncOps $ prop "wAsyncly monad bind folded" . monadBind folded sortEq
9461009 parallelOps $ prop "parallely monad bind folded" . monadBind folded sortEq
9471010
948− describe "Stream transform operations" $ do
949− serialOps $ transformOps S.fromFoldable "serially" (==)
950− wSerialOps $ transformOps S.fromFoldable "wSerially" (==)
951− aheadOps $ transformOps S.fromFoldable "aheadly" (==)
952− asyncOps $ transformOps S.fromFoldable "asyncly" sortEq
953− wAsyncOps $ transformOps S.fromFoldable "wAsyncly" sortEq
954− parallelOps $ transformOps S.fromFoldable "parallely" sortEq
955− zipSerialOps $ transformOps S.fromFoldable "zipSerially" (==)
956− zipAsyncOps $ transformOps S.fromFoldable "zipAsyncly" (==)
957−
958− serialOps $ transformOps folded "serially folded" (==)
959− wSerialOps $ transformOps folded "wSerially folded" (==)
960− aheadOps $ transformOps folded "aheadly folded" (==)
961− asyncOps $ transformOps folded "asyncly folded" sortEq
962− wAsyncOps $ transformOps folded "wAsyncly folded" sortEq
963− parallelOps $ transformOps folded "parallely folded" sortEq
964− zipSerialOps $ transformOps folded "zipSerially folded" (==)
965− zipAsyncOps $ transformOps folded "zipAsyncly folded" (==)
966−
967− serialOps $ transformOpsWord8 S.fromFoldable "serially"
968− wSerialOps $ transformOpsWord8 S.fromFoldable "wSerially"
969− aheadOps $ transformOpsWord8 S.fromFoldable "aheadly"
970− asyncOps $ transformOpsWord8 S.fromFoldable "asyncly"
971− wAsyncOps $ transformOpsWord8 S.fromFoldable "wAsyncly"
972− parallelOps $ transformOpsWord8 S.fromFoldable "parallely"
973− zipSerialOps $ transformOpsWord8 S.fromFoldable "zipSerially"
974− zipAsyncOps $ transformOpsWord8 S.fromFoldable "zipAsyncly"
975−
976− serialOps $ transformOpsWord8 folded "serially folded"
977− wSerialOps $ transformOpsWord8 folded "wSerially folded"
978− aheadOps $ transformOpsWord8 folded "aheadly folded"
979− asyncOps $ transformOpsWord8 folded "asyncly folded"
980− wAsyncOps $ transformOpsWord8 folded "wAsyncly folded"
981− parallelOps $ transformOpsWord8 folded "parallely folded"
982− zipSerialOps $ transformOpsWord8 folded "zipSerially folded"
983− zipAsyncOps $ transformOpsWord8 folded "zipAsyncly folded"
984−
9851011 -- These tests won't work with maxBuffer or maxThreads set to 1, so we
9861012 -- exclude those cases from these.
9871013 let mkOps t =
@@ -1017,8 +1043,6 @@ main = hspec
10171043 prop "concurrent foldl application" $ withMaxSuccess maxTestCount
10181044 concurrentFoldlApplication
10191045
1020− -- These tests are specifically targeted towards detecting illegal sharing
1021− -- of SVar across conurrent streams. All transform ops must be added here.
10221046 describe "Stream transform and combine operations" $ do
10231047 serialOps $ transformCombineOpsCommon S.fromFoldable "serially" (==)
10241048 wSerialOps $ transformCombineOpsCommon S.fromFoldable "wSerially" sortEq
@@ -1067,17 +1091,35 @@ main = hspec
10671091 zipSerialOps $ eliminationOps folded "zipSerially folded"
10681092 zipAsyncOps $ eliminationOps folded "zipAsyncly folded"
10691093
1094+ serialOps $ eliminationOpsWord8 S.fromFoldable "serially"
1095+ wSerialOps $ eliminationOpsWord8 S.fromFoldable "wSerially"
1096+ aheadOps $ eliminationOpsWord8 S.fromFoldable "aheadly"
1097+ asyncOps $ eliminationOpsWord8 S.fromFoldable "asyncly"
1098+ wAsyncOps $ eliminationOpsWord8 S.fromFoldable "wAsyncly"
1099+ parallelOps $ eliminationOpsWord8 S.fromFoldable "parallely"
1100+ zipSerialOps $ eliminationOpsWord8 S.fromFoldable "zipSerially"
1101+ zipAsyncOps $ eliminationOpsWord8 S.fromFoldable "zipAsyncly"
1102+
1103+ serialOps $ eliminationOpsWord8 folded "serially folded"
1104+ wSerialOps $ eliminationOpsWord8 folded "wSerially folded"
1105+ aheadOps $ eliminationOpsWord8 folded "aheadly folded"
1106+ asyncOps $ eliminationOpsWord8 folded "asyncly folded"
1107+ wAsyncOps $ eliminationOpsWord8 folded "wAsyncly folded"
1108+ parallelOps $ eliminationOpsWord8 folded "parallely folded"
1109+ zipSerialOps $ eliminationOpsWord8 folded "zipSerially folded"
1110+ zipAsyncOps $ eliminationOpsWord8 folded "zipAsyncly folded"
1111+
10701112 -- XXX Add a test where we chain all transformation APIs and make sure that
10711113 -- the state is being passed through all of them.
10721114 describe "Stream serial elimination operations" $ do
1073− serialOps $ serialEliminationOps S.fromFoldable "serially"
1074− wSerialOps $ serialEliminationOps S.fromFoldable "wSerially"
1075− aheadOps $ serialEliminationOps S.fromFoldable "aheadly"
1076− zipSerialOps $ serialEliminationOps S.fromFoldable "zipSerially"
1077− zipAsyncOps $ serialEliminationOps S.fromFoldable "zipAsyncly"
1078−
1079− serialOps $ serialEliminationOps folded "serially folded"
1080− wSerialOps $ serialEliminationOps folded "wSerially folded"
1081− aheadOps $ serialEliminationOps folded "aheadly folded"
1082− zipSerialOps $ serialEliminationOps folded "zipSerially folded"
1083− zipAsyncOps $ serialEliminationOps folded "zipAsyncly folded"
1115+ serialOps $ eliminationOpsOrdered S.fromFoldable "serially"
1116+ wSerialOps $ eliminationOpsOrdered S.fromFoldable "wSerially"
1117+ aheadOps $ eliminationOpsOrdered S.fromFoldable "aheadly"
1118+ zipSerialOps $ eliminationOpsOrdered S.fromFoldable "zipSerially"
1119+ zipAsyncOps $ eliminationOpsOrdered S.fromFoldable "zipAsyncly"
1120+
1121+ serialOps $ eliminationOpsOrdered folded "serially folded"
1122+ wSerialOps $ eliminationOpsOrdered folded "wSerially folded"
1123+ aheadOps $ eliminationOpsOrdered folded "aheadly folded"
1124+ zipSerialOps $ eliminationOpsOrdered folded "zipSerially folded"
1125+ zipAsyncOps $ eliminationOpsOrdered folded "zipAsyncly folded"