on master help sign in
Review of #138 (Prelude functions) and other changes
dbc1956Harendra Kumar
Changelog.md+17−5
@@ -1,12 +1,24 @@
1## Unreleased
1## Unreleased
2
2
3+### Breaking changes
4+
5+* 'Monad' constraint may be needed on some of the existing APIs.
6+
7+### Enhancements
8+
3* Add benchmarks to measure composed and iterated operations
9* Add benchmarks to measure composed and iterated operations
4* Performance improvements
10* Performance improvements
5−* Add `(!!)`, `insertBy`, `mergeBy`, `merge`, `the`, `maximumBy`,
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`
6− `minimumBy`, `scanl1`, `scanl1M`, `scanl1'`, `scanl1M'`, `scanl`, `scanlM`,
17+ * Scans: `scanl1'`, `scanl1M'
18+ * Filters: `uniq`, `insertBy`, `deleteBy`, `findM`
19+ * Multi-stream: `eqBy`, `cmpBy`, `mergeBy`, `mergeByM`, `mergeAsyncBy`,
7− `deleteBy`, `isPrefixOf`, `isSubsequenceOf`, `stripPrefix`,
20+ `mergeAsyncByM`, `isPrefixOf`, `isSubsequenceOf`, `stripPrefix`,
8− `replicate`, `generateM`, `generate`, `concatMap`, `concatMapM`,
21+ `concatMap`, `concatMapM`, `indexed`, `indexedR`
9− `findM`, `uniq`, `indexed`, `indexedR` to Prelude.
10
22
11## 0.5.2
23## 0.5.2
12
24
benchmark/Linear.hs+22−24
@@ -29,12 +29,12 @@ benchIOSink name f = bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source
29-- | Takes a source, and uses it with a default drain/fold method.
29-- | Takes a source, and uses it with a default drain/fold method.
30{-# INLINE benchIOSrc #-}
30{-# INLINE benchIOSrc #-}
31benchIOSrc
31benchIOSrc
32− :: (t IO Int -> SerialT IO Int)
32+ :: (t IO a -> SerialT IO a)
33 -> String
33 -> String
34− -> (Int -> t IO Int)
34+ -> (Int -> t IO a)
35 -> Benchmark
35 -> Benchmark
36benchIOSrc t name f =
36benchIOSrc 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
38
38
39{-# INLINE benchPure #-}
39{-# INLINE benchPure #-}
40benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
40benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
@@ -84,6 +84,11 @@ main =
84 [ -- Most basic, barely stream continuations running
84 [ -- Most basic, barely stream continuations running
85 benchIOSrc serially "unfoldr" Ops.sourceUnfoldr
85 benchIOSrc serially "unfoldr" Ops.sourceUnfoldr
86 , benchIOSrc serially "unfoldrM" Ops.sourceUnfoldrM
86 , 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
87 , benchIOSrc serially "fromList" Ops.sourceFromList
92 , benchIOSrc serially "fromList" Ops.sourceFromList
88 , benchIOSrc serially "fromListM" Ops.sourceFromListM
93 , benchIOSrc serially "fromListM" Ops.sourceFromListM
89 -- These are essentially cons and consM
94 -- These are essentially cons and consM
@@ -127,7 +132,7 @@ main =
127 ]
132 ]
128 , bgroup "transformation"
133 , bgroup "transformation"
129 [ benchIOSink "scan" (Ops.scan 1)
134 [ benchIOSink "scan" (Ops.scan 1)
130− , benchIOSink "scanl1" (Ops.scanl1 1)
135+ , benchIOSink "scanl1'" (Ops.scanl1' 1)
131 , benchIOSink "map" (Ops.map 1)
136 , benchIOSink "map" (Ops.map 1)
132 , benchIOSink "fmap" (Ops.fmap 1)
137 , benchIOSink "fmap" (Ops.fmap 1)
133 , benchIOSink "mapM" (Ops.mapM serially 1)
138 , benchIOSink "mapM" (Ops.mapM serially 1)
@@ -137,15 +142,10 @@ main =
137 Ops.sequence serially (Ops.sourceUnfoldrMAction n)
142 Ops.sequence serially (Ops.sourceUnfoldrMAction n)
138 , benchIOSink "findIndices" (Ops.findIndices 1)
143 , benchIOSink "findIndices" (Ops.findIndices 1)
139 , benchIOSink "elemIndices" (Ops.elemIndices 1)
144 , 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)
145 ]
145 ]
146 , bgroup "transformationX4"
146 , bgroup "transformationX4"
147 [ benchIOSink "scan" (Ops.scan 4)
147 [ benchIOSink "scan" (Ops.scan 4)
148− , benchIOSink "scanl1" (Ops.scanl1 4)
148+ , benchIOSink "scanl1'" (Ops.scanl1' 4)
149 , benchIOSink "map" (Ops.map 4)
149 , benchIOSink "map" (Ops.map 4)
150 , benchIOSink "fmap" (Ops.fmap 4)
150 , benchIOSink "fmap" (Ops.fmap 4)
151 , benchIOSink "mapM" (Ops.mapM serially 4)
151 , benchIOSink "mapM" (Ops.mapM serially 4)
@@ -155,11 +155,6 @@ main =
155 -- Ops.sequence serially (Ops.sourceUnfoldrMAction n)
155 -- Ops.sequence serially (Ops.sourceUnfoldrMAction n)
156 , benchIOSink "findIndices" (Ops.findIndices 4)
156 , benchIOSink "findIndices" (Ops.findIndices 4)
157 , benchIOSink "elemIndices" (Ops.elemIndices 4)
157 , 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
163 ]
158 ]
164 , bgroup "filtering"
159 , bgroup "filtering"
165 [ benchIOSink "filter-even" (Ops.filterEven 1)
160 [ benchIOSink "filter-even" (Ops.filterEven 1)
@@ -173,6 +168,8 @@ main =
173 , benchIOSink "dropWhile-true" (Ops.dropWhileTrue 1)
168 , benchIOSink "dropWhile-true" (Ops.dropWhileTrue 1)
174 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 1)
169 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 1)
175 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 1)
170 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 1)
171+ , benchIOSink "deleteBy" (Ops.deleteBy 1)
172+ , benchIOSink "insertBy" (Ops.insertBy 1)
176 ]
173 ]
177 , bgroup "filteringX4"
174 , bgroup "filteringX4"
178 [ benchIOSink "filter-even" (Ops.filterEven 4)
175 [ benchIOSink "filter-even" (Ops.filterEven 4)
@@ -186,23 +183,24 @@ main =
186 , benchIOSink "dropWhile-true" (Ops.dropWhileTrue 4)
183 , benchIOSink "dropWhile-true" (Ops.dropWhileTrue 4)
187 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 4)
184 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 4)
188 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 4)
185 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 4)
186+ , benchIOSink "deleteBy" (Ops.deleteBy 4)
187+ , benchIOSink "insertBy" (Ops.insertBy 4)
189 ]
188 ]
190− , bgroup "zipping"
189+ , bgroup "multi-stream"
191 [ benchIOSink "eqBy" Ops.eqBy
190 [ benchIOSink "eqBy" Ops.eqBy
192 , benchIOSink "cmpBy" Ops.cmpBy
191 , benchIOSink "cmpBy" Ops.cmpBy
193 , benchIOSink "zip" Ops.zip
192 , benchIOSink "zip" Ops.zip
194 , benchIOSink "zipM" Ops.zipM
193 , benchIOSink "zipM" Ops.zipM
195− ]
196− , bgroup "merging"
197− [ benchIOSink "merge" Ops.merge
194+ , benchIOSink "mergeBy" Ops.mergeBy
198− ]
199− , bgroup "subsequences"
200− [ benchIOSink "isPrefixOf" Ops.isPrefixOf
195+ , benchIOSink "isPrefixOf" Ops.isPrefixOf
201− , benchIOSink "isPrefixAll" Ops.isPrefixAll
202 , benchIOSink "isSubsequenceOf" Ops.isSubsequenceOf
196 , benchIOSink "isSubsequenceOf" Ops.isSubsequenceOf
203− , benchIOSink "evenSubsequence" Ops.evenSubsequence
204 , benchIOSink "stripPrefix" Ops.stripPrefix
197 , benchIOSink "stripPrefix" Ops.stripPrefix
198+ , benchIOSrc serially "concatMap" Ops.concatMap
205 ]
199 ]
200+ , bgroup "mixed"
201+ [ benchIOSink "sum-product-fold" Ops.sumProductFold
202+ , benchIOSink "sum-product-scan" Ops.sumProductScan
203+ ]
206 , bgroup "mixedX4"
204 , bgroup "mixedX4"
207 [ benchIOSink "scan-map" (Ops.scanMap 4)
205 [ benchIOSink "scan-map" (Ops.scanMap 4)
208 , benchIOSink "drop-map" (Ops.dropMap 4)
206 , benchIOSink "drop-map" (Ops.dropMap 4)
benchmark/LinearOps.hs+73−58
@@ -8,6 +8,8 @@
8{-# LANGUAGE CPP #-}
8{-# LANGUAGE CPP #-}
9{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE DeriveAnyClass #-}
12+{-# LANGUAGE DeriveGeneric #-}
11
13
12module LinearOps where
14module LinearOps where
13
15
@@ -17,10 +19,12 @@ import Data.Maybe (fromJust)
17import Prelude
19import Prelude
18 (Monad, Int, (+), ($), (.), return, fmap, even, (>), (<=), (==), (>=),
20 (Monad, Int, (+), ($), (.), return, fmap, even, (>), (<=), (==), (>=),
19 subtract, undefined, Maybe(..), odd, Bool, not, (>>=), mapM_, curry,
21 subtract, undefined, Maybe(..), odd, Bool, not, (>>=), mapM_, curry,
20− maxBound, div, IO, compare)
22+ maxBound, div, IO, compare, Double, fromIntegral, Integer)
21import qualified Prelude as P
23import qualified Prelude as P
22import qualified Data.Foldable as F
24import qualified Data.Foldable as F
23import qualified GHC.Exts as GHC
25import qualified GHC.Exts as GHC
26+import Control.DeepSeq (NFData)
27+import GHC.Generics (Generic)
24
28
25import qualified Streamly as S
29import qualified Streamly as S
26import qualified Streamly.Prelude as S
30import qualified Streamly.Prelude as S
@@ -46,7 +50,27 @@ type Stream m a = S.SerialT m a
46{-# INLINE source #-}
50{-# INLINE source #-}
47source :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
51source :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
48source n = S.serially $ sourceUnfoldrM n
52source 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
50
74
51{-# INLINE sourceFromList #-}
75{-# INLINE sourceFromList #-}
52sourceFromList :: (Monad m, S.IsStream t) => Int -> t m Int
76sourceFromList :: (Monad m, S.IsStream t) => Int -> t m Int
@@ -131,9 +155,7 @@ runStream :: Monad m => Stream m a -> m ()
131runStream = S.runStream
155runStream = S.runStream
132
156
133{-# INLINE toList #-}
157{-# 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]
137
159
138{-# INLINE last #-}
160{-# INLINE last #-}
139{-# INLINE maximum #-}
161{-# INLINE maximum #-}
@@ -154,7 +176,7 @@ minimumBy, maximumBy :: Monad m => Stream m Int -> m (Maybe Int)
154{-# INLINE length #-}
176{-# INLINE length #-}
155{-# INLINE sum #-}
177{-# INLINE sum #-}
156{-# INLINE product #-}
178{-# 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
158
180
159{-# INLINE all #-}
181{-# INLINE all #-}
160{-# INLINE any #-}
182{-# INLINE any #-}
@@ -165,7 +187,7 @@ foldl', length, sum, product :: Monad m => Stream m Int -> m Int
165elem, notElem, all, any, and, or :: Monad m => Stream m Int -> m Bool
187elem, notElem, all, any, and, or :: Monad m => Stream m Int -> m Bool
166
188
167{-# INLINE toNull #-}
189{-# 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 ()
169toNull t = runStream . t
191toNull t = runStream . t
170
192
171{-# INLINE uncons #-}
193{-# INLINE uncons #-}
@@ -197,9 +219,9 @@ mapM_ :: Monad m => Stream m Int -> m ()
197mapM_ = S.mapM_ (\_ -> return ())
219mapM_ = S.mapM_ (\_ -> return ())
198
220
199toList = S.toList
221toList = S.toList
200−foldr = S.foldr (:) []
222+foldr = S.foldr (+) 0
201foldr1 = S.foldr1 (+)
223foldr1 = S.foldr1 (+)
202−foldrM = S.foldrM (\a xs -> return (a : xs)) []
224+foldrM = S.foldrM (\a xs -> return $ a + xs) 0
203foldl' = S.foldl' (+) 0
225foldl' = S.foldl' (+) 0
204foldl1' = S.foldl1' (+)
226foldl1' = S.foldl1' (+)
205last = S.last
227last = S.last
@@ -254,7 +276,7 @@ composeN' n f =
254 _ -> undefined
276 _ -> undefined
255
277
256{-# INLINE scan #-}
278{-# INLINE scan #-}
257−{-# INLINE scanl1 #-}
279+{-# INLINE scanl1' #-}
258{-# INLINE map #-}
280{-# INLINE map #-}
259{-# INLINE fmap #-}
281{-# INLINE fmap #-}
260{-# INLINE mapMaybe #-}
282{-# INLINE mapMaybe #-}
@@ -272,15 +294,12 @@ composeN' n f =
272{-# INLINE dropWhileFalse #-}
294{-# INLINE dropWhileFalse #-}
273{-# INLINE findIndices #-}
295{-# INLINE findIndices #-}
274{-# INLINE elemIndices #-}
296{-# INLINE elemIndices #-}
275−{-# INLINE insertLarge #-}
297+{-# INLINE insertBy #-}
276−{-# INLINE insertSmall #-}
298+{-# INLINE deleteBy #-}
277−{-# INLINE deleteLarge #-}
278−{-# INLINE deleteSmall #-}
279−scan, scanl1, map, fmap, mapMaybe, filterEven, filterAllOut,
299+scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
280 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
300 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
281 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
301 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
282− findIndices, elemIndices, insertLarge, insertSmall, deleteLarge,
302+ findIndices, elemIndices, insertBy, deleteBy
283− deleteSmall
284 :: Monad m
303 :: Monad m
285 => Int -> Stream m Int -> m ()
304 => Int -> Stream m Int -> m ()
286
305
@@ -296,7 +315,7 @@ sequence :: (S.IsStream t, S.MonadAsync m)
296 => (t m Int -> S.SerialT m Int) -> t m (m Int) -> m ()
315 => (t m Int -> S.SerialT m Int) -> t m (m Int) -> m ()
297
316
298scan n = composeN n $ S.scanl' (+) 0
317scan n = composeN n $ S.scanl' (+) 0
299−scanl1 n = composeN n $ S.scanl1' (+)
318+scanl1' n = composeN n $ S.scanl1' (+)
300fmap n = composeN n $ Prelude.fmap (+1)
319fmap n = composeN n $ Prelude.fmap (+1)
301map n = composeN n $ S.map (+1)
320map n = composeN n $ S.map (+1)
302mapM t n = composeN' n $ t . S.mapM return
321mapM t n = composeN' n $ t . S.mapM return
@@ -319,11 +338,8 @@ dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
319dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
338dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
320findIndices n = composeN n $ S.findIndices (== maxValue)
339findIndices n = composeN n $ S.findIndices (== maxValue)
321elemIndices n = composeN n $ S.elemIndices maxValue
340elemIndices n = composeN n $ S.elemIndices maxValue
322−insertLarge n = composeN n $ S.insertBy compare maxValue
341+insertBy 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
342+deleteBy n = composeN n $ S.deleteBy (>=) maxValue
326−
327
343
328-------------------------------------------------------------------------------
344-------------------------------------------------------------------------------
329-- Iteration
345-- Iteration
@@ -373,16 +389,8 @@ iterateDropWhileTrue = iterateSource (S.dropWhile (<= maxValue)) maxIters
373
389
374{-# INLINE zip #-}
390{-# INLINE zip #-}
375{-# INLINE zipM #-}
391{-# INLINE zipM #-}
376−{-# INLINE merge #-}
392+{-# INLINE mergeBy #-}
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 ()
393+zip, zipM, mergeBy :: Monad m => Stream m Int -> m ()
386
394
387zip src = do
395zip src = do
388 r <- S.tail src
396 r <- S.tail src
@@ -393,33 +401,22 @@ zipM src = do
393 let src1 = fromJust r
401 let src1 = fromJust r
394 transform (S.zipWithM (curry return) src src1)
402 transform (S.zipWithM (curry return) src src1)
395
403
396−merge src = do
404+mergeBy src = do
397 r <- S.tail src
405 r <- S.tail src
398 let src1 = fromJust r
406 let src1 = fromJust r
399 transform (S.mergeBy P.compare src src1)
407 transform (S.mergeBy P.compare src src1)
400
408
401−isPrefixOf src = do
402− let src1 = S.take 10 src
403− _ <- S.isPrefixOf src1 src
409+{-# INLINE isPrefixOf #-}
404− return ()
410+{-# INLINE isSubsequenceOf #-}
405−
406−isPrefixAll src = do
407− _ <- S.isPrefixOf src src
408− return ()
409−
410−isSubsequenceOf src = do
411+isPrefixOf, isSubsequenceOf :: Monad m => Stream m Int -> m Bool
411− let src1 = S.take 10 src
412− _ <- S.isSubsequenceOf src1 src
413− return ()
414
412
415−evenSubsequence src = do
413+isPrefixOf src = S.isPrefixOf src src
416− let src1 = S.filter even src
417− _ <- S.isSubsequenceOf src1 src
414+isSubsequenceOf src = S.isSubsequenceOf src src
418− return ()
419
415
416+{-# INLINE stripPrefix #-}
417+stripPrefix :: Monad m => Stream m Int -> m ()
420stripPrefix src = do
418stripPrefix src = do
421− let src1 = S.take 10 src
422− _ <- S.stripPrefix src1 src
419+ _ <- S.stripPrefix src src
423 return ()
420 return ()
424
421
425{-# INLINE zipAsync #-}
422{-# INLINE zipAsync #-}
@@ -435,8 +432,6 @@ zipAsyncM src = do
435 let src1 = fromJust r
432 let src1 = fromJust r
436 transform (S.zipAsyncWithM (curry return) src src1)
433 transform (S.zipAsyncWithM (curry return) src src1)
437
434
438−concat _n = return ()
439−
440{-# INLINE eqBy #-}
435{-# INLINE eqBy #-}
441eqBy :: (Monad m, P.Eq a) => Stream m a -> m P.Bool
436eqBy :: (Monad m, P.Eq a) => Stream m a -> m P.Bool
442eqBy src = S.eqBy (==) src src
437eqBy src = S.eqBy (==) src src
@@ -445,6 +440,15 @@ eqBy src = S.eqBy (==) src src
445cmpBy :: (Monad m, P.Ord a) => Stream m a -> m P.Ordering
440cmpBy :: (Monad m, P.Ord a) => Stream m a -> m P.Ordering
446cmpBy src = S.cmpBy P.compare src src
441cmpBy src = S.cmpBy P.compare src src
447
442
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+
448-------------------------------------------------------------------------------
452-------------------------------------------------------------------------------
449-- Mixed Composition
453-- Mixed Composition
450-------------------------------------------------------------------------------
454-------------------------------------------------------------------------------
@@ -476,6 +480,17 @@ filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
476filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
480filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
477filterMap n = composeN n $ S.map (subtract 1) . S.filter (<= maxValue)
481filterMap n = composeN n $ S.map (subtract 1) . S.filter (<= maxValue)
478
482
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+
479-------------------------------------------------------------------------------
494-------------------------------------------------------------------------------
480-- Pure stream operations
495-- Pure stream operations
481-------------------------------------------------------------------------------
496-------------------------------------------------------------------------------
@@ -498,16 +513,16 @@ ordInstanceMin src = P.min src src
498
513
499{-# INLINE showInstance #-}
514{-# INLINE showInstance #-}
500showInstance :: Stream Identity Int -> P.String
515showInstance :: Stream Identity Int -> P.String
501−showInstance src = P.showsPrec 0 src ""
516+showInstance src = P.show src
502
517
503{-# INLINE showInstanceList #-}
518{-# INLINE showInstanceList #-}
504showInstanceList :: Stream Identity Int -> P.String
519showInstanceList :: 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])
506
521
507{-# INLINE readInstance #-}
522{-# INLINE readInstance #-}
508readInstance :: Stream Identity Int -> Stream Identity Int
523readInstance :: Stream Identity Int -> Stream Identity Int
509readInstance src =
524readInstance src =
510− let r = P.readsPrec 0 ("fromList [1"
525+ let r = P.reads ("fromList [1"
511 P.++ P.concat (P.replicate value ",1") P.++ "]")
526 P.++ P.concat (P.replicate value ",1") P.++ "]")
512 in case r of
527 in case r of
513 [(x,"")] -> src S.<> x
528 [(x,"")] -> src S.<> x
src/Streamly/Prelude.hs+1011−247
@@ -28,6 +28,10 @@
28-- provided for convenience and for consistency with other pure APIs in the
28-- provided for convenience and for consistency with other pure APIs in the
29-- @base@ package.
29-- @base@ package.
30--
30--
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+--
31-- Functions having a 'MonadAsync' constraint work concurrently when used with
35-- Functions having a 'MonadAsync' constraint work concurrently when used with
32-- appropriate stream type combinator. Please be careful to not use 'parallely'
36-- appropriate stream type combinator. Please be careful to not use 'parallely'
33-- with infinite streams.
37-- with infinite streams.
@@ -41,74 +45,128 @@
41module Streamly.Prelude
45module Streamly.Prelude
42 (
46 (
43 -- * Construction
47 -- * Construction
44− -- | All other stream construction and generation combinators described
48+ -- ** Primitives
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
50 -- | Primitives to construct a stream from pure values or monadic actions.
49 -- | 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+
51 K.nil
55 K.nil
52 , K.cons
56 , K.cons
53 , (K..:)
57 , (K..:)
54
58
55 , consM
59 , consM
56 , (|:)
60 , (|:)
61+
62+ -- ** From Values
63+ -- | Generate a monadic stream from a seed value or values.
57 , yield
64 , yield
58 , yieldM
65 , yieldM
66+ , K.repeat
67+ , repeatM
68+ , replicate
69+ , replicateM
59
70
60− -- ** From Streams
71+ -- ** Enumeration
61− -- | You can construct streams by appending or merging existing streams.
72+ -- | The haskell 'Enum' typeclass provides enumerations for member types.
62− -- When constructing streams from streams, '<>' and 'mempty' are the
73+ -- However, the typeclass is tied to lists, it is impossible to use it to
63− -- intuitive equivalents of 'K.cons' and 'K.nil', respectively. These
74+ -- write enumeration functions producing something other than a list in a
64− -- primitives can be very useful when constructing your own custom stream
75+ -- generic way.
65− -- combinators. Also see the variants of '<>' defined in the "Streamly"
76+ -- We can use the 'Enum' facilities to enumerate the type producing a list
66− -- module. Note that appending streams is inexpensive, it is much more
77+ -- and then convert it to a stream:
67− -- efficient than appending lists.
78+ --
68−
79+ -- @
69− -- * Generation
80+ -- 'fromList' $ 'Prelude.enumFromThen' from then
70− -- ** Unfold and Iterate
81+ -- @
71− -- | Note that the generative steps of unfold and iterate are inherently
82+ --
72− -- serial as the next step depends on the result of the previous step.
83+ -- However, this is not particularly efficient. Note that using
73− -- However, consumption of the result from the previous step can happen in
84+ -- enumeration functions e.g. 'Prelude.enumFromThen' is slightly faster
74− -- parallel with the generation by the next step.
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.
75 , unfoldr
104 , unfoldr
76 , unfoldrM
105 , unfoldrM
77 , iterate
106 , iterate
78 , iterateM
107 , iterateM
108+ , fromIndices
109+ , fromIndicesM
79
110
80− -- ** Replicate and Repeat
111+ -- ** From Containers
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
92 -- | Convert an input structure, container or source into a stream. All of
112 -- | Convert an input structure, container or source into a stream. All of
93 -- these can be expressed in terms of primitives.
113 -- these can be expressed in terms of primitives.
94− , generate
95− , generateM
96 , P.fromList
114 , P.fromList
97 , fromListM
115 , fromListM
98 , K.fromFoldable
116 , K.fromFoldable
99 , fromFoldableM
117 , fromFoldableM
100− , fromHandle
101
118
102− -- * Deconstruction
119+ -- ** From External Containers
103− , uncons
120+ , fromHandle
104
121
105 -- * Elimination
122 -- * Elimination
106
123
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+
107 -- ** General Folds
131 -- ** General Folds
108− -- | All the folds can be implemented in terms of 'uncons', however the
132+-- | Right and left folds.
109− -- specific implementations provided here are generally more efficient.
133+-- As a simple rule, always use lazy right fold for construction and strict
110− -- Folds are inherently serial as each step needs to use the result of
134+-- left fold for reduction. By construction we mean using a constructor as the
111− -- the previous step.
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+
112 , foldr
170 , foldr
113 , foldr1
171 , foldr1
114 , foldrM
172 , foldrM
@@ -118,108 +176,113 @@ module Streamly.Prelude
118 , foldx
176 , foldx
119 , foldxM
177 , foldxM
120
178
121− -- ** Specialized Folds
179+ -- ** To Elements
122− -- | These folds can be expressed in terms of the general fold routines but
180+ -- | Folds that extract selected elements of a stream or their properties.
123− -- the special versions here can be more efficient in many cases.
181+ , (!!)
124−
125− -- Filtering folds: extract parts of the stream
126 , head
182 , head
127− , tail
128 , last
183 , last
129− , init
184+ , findM
185+ , find
186+ , lookup
187+ , findIndex
188+ , elemIndex
130
189
131− -- Substreams
190+ -- ** To Parts
132− , isPrefixOf
191+ -- | Folds that extract selected parts of a stream.
133− , isSubsequenceOf
192+ , tail
134− , stripPrefix
193+ , init
135
194
136− -- Conditional folds: may terminate early based on a condition
195+ -- ** To Boolean
196+ -- | Folds that summarize the stream to a boolean value.
137 , null
197 , null
138 , elem
198 , elem
139− , elemIndex
140 , notElem
199 , notElem
141− , lookup
142− , findM
143− , find
144− , findIndex
145 , all
200 , all
146 , any
201 , any
147 , and
202 , and
148 , or
203 , or
149− , eqBy
150− , cmpBy
151− , (!!)
152− , concatMapM
153− , concatMap
154
204
155− -- Full folds - need to go through all elements
205+ -- ** To Summary
206+ -- | Folds that summarize the stream to a single value.
156 , length
207 , length
157− , maximum
158− , maximumBy
159− , minimum
160− , minimumBy
161 , sum
208 , sum
162 , product
209 , product
163
210
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
165 -- | Convert or divert a stream into an output structure, container or
221 -- | Convert or divert a stream into an output structure, container or
166 -- sink.
222 -- sink.
167 , toList
223 , toList
168 , toHandle
224 , toHandle
169
225
170 -- * Transformation
226 -- * 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.
175
227
176 -- ** Scanning
228 -- ** Scanning
177− -- | Scan is a transformation by continuously folding the result with the
229+ -- | Scans stream all the intermediate reduction steps of the corresponding
178− -- next element of the stream. This is the generalized way to transform a
230+ -- folds. The following equations hold for lists:
179− -- stream carrying state from previous transformation steps, other forms of
231+ --
180− -- transformation like map can be expressed in terms of this.
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+
181 , scanl'
246 , scanl'
182 , scanlM'
247 , scanlM'
183− , scanlM
248+ -- , postscanl'
184− , scanl
249+ -- , postscanlM'
185− , scanl1M'
250+ -- , prescanl'
251+ -- , prescanlM'
186 , scanl1'
252 , scanl1'
187− , scanl1M
253+ , scanl1M'
188− , scanl1
189 , scanx
254 , scanx
190
255
191 -- ** Mapping
256 -- ** Mapping
192− -- | Map is a special form of scan where no state is carried from one step
257+ -- | Map is a strictly one-to-one transformation of stream elements. It
193− -- to the next.
258+ -- cannot add or remove elements from the stream, just transforms them.
194 , Serial.map
259 , Serial.map
195− , mapM
196
260
197 -- ** Flattening
261 -- ** Flattening
198 , sequence
262 , sequence
199−
263+ , mapM
200− -- * Filtering and Insertion
201− -- | Adding or removing elements from the stream thus changing the length
202− -- of the stream.
203
264
204 -- ** Filtering
265 -- ** Filtering
266+ -- | Filtering may remove some elements from the stream.
267+
205 , filter
268 , filter
206 , filterM
269 , filterM
207− , uniq
208 , take
270 , take
209 , takeWhile
271 , takeWhile
210 , takeWhileM
272 , takeWhileM
211 , drop
273 , drop
212 , dropWhile
274 , dropWhile
213 , dropWhileM
275 , dropWhileM
276+ , deleteBy
277+ , uniq
214
278
215− -- ** Inserting
279+ -- ** Insertion
216− , intersperseM
280+ -- | Insertion adds more elements to the stream.
217− , insertBy
218
281
219− -- ** Deleting
282+ , insertBy
220− , deleteBy
283+ , intersperseM
221
284
222− -- * Reordering
285+ -- ** Reordering
223 , reverse
286 , reverse
224
287
225 -- * Hybrid Operations
288 -- * Hybrid Operations
@@ -234,20 +297,51 @@ module Streamly.Prelude
234 , findIndices
297 , findIndices
235 , elemIndices
298 , elemIndices
236
299
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+
237− -- * Zipping
325+ -- ** Zipping
238− , indexed
239− , indexedR
240 , zipWith
326 , zipWith
241 , zipWithM
327 , zipWithM
242 , Z.zipAsyncWith
328 , Z.zipAsyncWith
243 , Z.zipAsyncWithM
329 , Z.zipAsyncWithM
244
330
245− -- * Merging
331+ -- Special zips
246− , mergeBy
332+ , indexed
247− , merge
333+ , indexedR
248
334
249− -- * Transformation comprehensions
335+ -- ** Flattening
250− , the
336+ , concatMapM
337+ , concatMap
338+
339+ -- ** Folds
340+ , eqBy
341+ , cmpBy
342+ , isPrefixOf
343+ , isSubsequenceOf
344+ , stripPrefix
251
345
252 -- * Deprecated
346 -- * Deprecated
253 , K.once
347 , K.once
@@ -270,6 +364,7 @@ import qualified Prelude
270import qualified System.IO as IO
364import qualified System.IO as IO
271
365
272import Streamly.SVar (MonadAsync, defState, rstState)
366import Streamly.SVar (MonadAsync, defState, rstState)
367+import Streamly.Streams.Async (mkAsync')
273import Streamly.Streams.Combinators (maxYields)
368import Streamly.Streams.Combinators (maxYields)
274import Streamly.Streams.Prelude (fromStreamS, toStreamS)
369import Streamly.Streams.Prelude (fromStreamS, toStreamS)
275import Streamly.Streams.StreamD (fromStreamD, toStreamD)
370import Streamly.Streams.StreamD (fromStreamD, toStreamD)
@@ -306,10 +401,18 @@ uncons m = K.uncons (K.adapt m)
306-- Generation by Unfolding
401-- Generation by Unfolding
307------------------------------------------------------------------------------
402------------------------------------------------------------------------------
308
403
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+--
309−-- | Build a stream by unfolding a /pure/ step function starting from a seed.
412+-- Build a stream by unfolding a /pure/ step function @step@ starting from a
310−-- The step function returns the next element in the stream and the next seed
413+-- seed @s@. The step function returns the next element in the stream and the
311−-- value. When it is done it returns 'Nothing' and the stream ends. For
414+-- next seed value. When it is done it returns 'Nothing' and the stream ends.
312−-- example,
415+-- For example,
313--
416--
314-- @
417-- @
315-- let f b =
418-- let f b =
@@ -322,15 +425,6 @@ uncons m = K.uncons (K.adapt m)
322-- [0,1,2,3]
425-- [0,1,2,3]
323-- @
426-- @
324--
427--
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−--
334-- @since 0.1.0
428-- @since 0.1.0
335{-# INLINE_EARLY unfoldr #-}
429{-# INLINE_EARLY unfoldr #-}
336unfoldr :: (Monad m, IsStream t) => (b -> Maybe (a, b)) -> b -> t m a
430unfoldr :: (Monad m, IsStream t) => (b -> Maybe (a, b)) -> b -> t m a
@@ -384,20 +478,36 @@ unfoldrMSerial step seed = fromStreamS (S.unfoldrM step seed)
384
478
385-- Faster than yieldM because there is no bind.
479-- Faster than yieldM because there is no bind.
386--
480--
481+-- |
482+-- @
483+-- yield a = a \`cons` nil
484+-- @
485+--
387−-- | Create a singleton stream from a pure value. Same as @a `cons` nil@ but
486+-- Create a singleton stream from a pure value.
388−-- slighly more efficient. Note that in monadic streams, 'yield' is the same
487+--
488+-- The following holds in monadic streams, but not in Zip streams:
489+--
490+-- @
491+-- yield = pure
492+-- yield = yieldM . pure
493+-- @
494+--
389−-- as 'pure' or 'return', however, in Zip applicative streams it is not the
495+-- In Zip applicative streams 'yield' is not the same as 'pure' because in that
390−-- same as 'pure' because in that case 'pure' is equivalent to 'repeat'
496+-- case 'pure' is equivalent to 'repeat' instead. 'yield' and 'pure' are
391−-- instead. In all other stream types, 'yield' is the same as @yieldM . pure@
497+-- equally efficient, in other cases 'yield' may be slightly more efficient
392−-- but more efficient.
498+-- than the other equivalent definitions.
393--
499--
394-- @since 0.4.0
500-- @since 0.4.0
395{-# INLINE yield #-}
501{-# INLINE yield #-}
396yield :: IsStream t => a -> t m a
502yield :: IsStream t => a -> t m a
397yield = K.yield
503yield = K.yield
398
504
505+-- |
506+-- @
507+-- yieldM m = m \`consM` nil
508+-- @
509+--
399−-- | Create a singleton stream from a monadic action. Same as @m \`consM` nil@
510+-- Create a singleton stream from a monadic action.
400−-- but more efficient.
401--
511--
402-- @
512-- @
403-- > toList $ yieldM getLine
513-- > toList $ yieldM getLine
@@ -410,24 +520,231 @@ yield = K.yield
410yieldM :: (Monad m, IsStream t) => m a -> t m a
520yieldM :: (Monad m, IsStream t) => m a -> t m a
411yieldM = K.yieldM
521yieldM = K.yieldM
412
522
413−-- | Generate a stream of given length, from a given pure
523+-- | @intFromStep from step@ generates an infinite stream whose first element
414−-- function of its indices.
524+-- is @from@ and the successive elements are in increments of @step@. This does
415−{-# INLINE generate #-}
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 #-}
416−generate :: (IsStream t, Monad m) => Int -> (Int -> a) -> t m a
536+intFromStep :: (IsStream t, Monad m, Integral a) => a -> a -> t m a
417−generate n = fromStreamD . D.generate n
537+intFromStep from stride = fromStreamD $ D.intFromStep from stride
418
538
419−-- | Generate a stream of given length, from a given monadic
539+-- | Enumerate an 'Integral' type. @intFrom from@ generates a stream whose
420−-- function of its indices.
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.
421--
542--
543+-- @
544+-- > S.toList $ S.take 4 $ S.intFrom (0 :: Int)
545+-- [0,1,2,3]
546+-- @
547+--
548+-- @since 0.6.0
422−{-# INLINE generateM #-}
549+{-# INLINE intFrom #-}
423−generateM :: (IsStream t, Monad m) => Int -> (Int -> m a) -> t m a
550+intFrom :: (IsStream t, Monad m, Integral a, Bounded a) => a -> t m a
424−generateM n = fromStreamD . D.generateM n
551+intFrom from = fromStreamD $ D.intFrom from
425
552
426−-- | Generate a stream by performing a monadic action @n@ times. Can be
553+-- | Enumerate an 'Integral' type in steps. @intFromThen from then@ generates a
427−-- expressed as @stimes n (yieldM m)@.
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.
428--
605--
429--
606--
430-- @
607-- @
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+-- @
431-- runStream $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
748-- runStream $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
432-- runStream $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
749-- runStream $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
433-- @
750-- @
@@ -435,20 +752,33 @@ generateM n = fromStreamD . D.generateM n
435-- /Concurrent/
752-- /Concurrent/
436--
753--
437-- @since 0.1.1
754-- @since 0.1.1
755+{-# INLINE_EARLY replicateM #-}
438replicateM :: (IsStream t, MonadAsync m) => Int -> m a -> t m a
756replicateM :: (IsStream t, MonadAsync m) => Int -> m a -> t m a
439−replicateM n m = go n
757+replicateM = K.replicateM
440− where
758+
441− go cnt = if cnt <= 0 then K.nil else m |: go (cnt - 1)
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
442
763
443−-- | Generate a stream by replicating the value @n@ times.
764+-- |
765+-- @
766+-- replicate = take n . repeat
767+-- @
444--
768--
769+-- Generate a stream of length @n@ by repeating a value @n@ times.
770+--
771+-- @since 0.6.0
445−replicate :: IsStream t => Int -> a -> t m a
772+replicate :: (IsStream t, Monad m) => Int -> a -> t m a
446−replicate n a = go n
773+replicate n = fromStreamS . S.replicate n
447− where
448− go cnt = if cnt <= 0 then K.nil else a `K.cons` go (cnt - 1)
449
774
775+-- |
776+-- @
777+-- repeatM = fix . consM
778+-- repeatM = cycle1 . yieldM
779+-- @
780+--
450−-- | Generate a stream by repeatedly executing a monadic action forever. Can be
781+-- Generate a stream by repeatedly executing a monadic action forever.
451−-- expressed as @cycle1 . yieldM@.
452--
782--
453-- @
783-- @
454-- runStream $ serially $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
784-- 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
462repeatM = go
792repeatM = go
463 where go m = m |: go m
793 where go m = m |: go m
464
794
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+-- @
466--
808--
467-- @since 0.1.2
809-- @since 0.1.2
468iterate :: IsStream t => (a -> a) -> a -> t m a
810iterate :: IsStream t => (a -> a) -> a -> t m a
@@ -470,8 +812,14 @@ iterate step = fromStream . go
470 where
812 where
471 go s = K.cons s (go (step s))
813 go s = K.cons s (go (step s))
472
814
473−-- | Iterate a monadic function from a seed value, streaming the results
815+-- |
474−-- forever.
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.
475--
823--
476-- When run concurrently, the next iteration can run concurrently with the
824-- When run concurrently, the next iteration can run concurrently with the
477-- processing of the previous iteration. Note that more than one iteration
825-- processing of the previous iteration. Note that more than one iteration
@@ -500,9 +848,13 @@ iterateM step = go
500-- Conversions
848-- Conversions
501------------------------------------------------------------------------------
849------------------------------------------------------------------------------
502
850
851+-- |
852+-- @
853+-- fromListM = 'Prelude.foldr' 'K.consM' 'K.nil'
854+-- @
855+--
503−-- | Construct a stream from a list containing monadic actions. More efficient
856+-- Construct a stream from a list of monadic actions. This is more efficient
504−-- list specific implementation of 'fromFoldableM' especially for serial
857+-- than 'fromFoldableM' for serial streams.
505−-- streams as it works well with fusion optimization.
506--
858--
507-- @since 0.4.0
859-- @since 0.4.0
508{-# INLINE_EARLY fromListM #-}
860{-# INLINE_EARLY fromListM #-}
@@ -511,8 +863,12 @@ fromListM = fromStreamD . D.fromListM
511{-# RULES "fromListM fallback to StreamK" [1]
863{-# RULES "fromListM fallback to StreamK" [1]
512 forall a. D.toStreamK (D.fromListM a) = fromFoldableM a #-}
864 forall a. D.toStreamK (D.fromListM a) = fromFoldableM a #-}
513
865
866+-- |
867+-- @
868+-- fromFoldableM = 'Prelude.foldr' 'consM' 'K.nil'
869+-- @
870+--
514−-- | Construct a stream from a 'Foldable' containing monadic actions. Same as
871+-- Construct a stream from a 'Foldable' containing monadic actions.
515−-- @'Prelude.foldr' 'consM' 'K.nil'@.
516--
872--
517-- @
873-- @
518-- runStream $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
874-- runStream $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
@@ -556,7 +912,7 @@ fromHandle h = fromStream go
556-- stream into a list:
912-- stream into a list:
557--
913--
558-- @
914-- @
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]
560-- [1,2,3]
916-- [1,2,3]
561-- @
917-- @
562--
918--
@@ -565,19 +921,74 @@ fromHandle h = fromStream go
565foldrM :: Monad m => (a -> b -> m b) -> b -> SerialT m a -> m b
921foldrM :: Monad m => (a -> b -> m b) -> b -> SerialT m a -> m b
566foldrM = P.foldrM
922foldrM = P.foldrM
567
923
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.
569--
929--
570-- @
930-- @
571−-- >> runIdentity $ foldr (:) [] (serially $ fromFoldable [1,2,3])
931+-- foldr f z xs == x1 \`f` tail1
572−-- [1,2,3]
932+-- tail1 == x2 \`f` tail2
933+-- tail2 == x3 \`f` tail3
934+-- ...
935+-- tailn == xn \`f` z
573-- @
936-- @
574--
937--
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+--
575-- @since 0.1.0
986-- @since 0.1.0
576{-# INLINE foldr #-}
987{-# INLINE foldr #-}
577foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
988foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
578foldr = P.foldr
989foldr = P.foldr
579
990
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
581-- value. Returns 'Nothing' if the stream is empty.
992-- value. Returns 'Nothing' if the stream is empty.
582--
993--
583-- @since 0.5.0
994-- @since 0.5.0
@@ -603,6 +1014,85 @@ foldl = foldx
603
1014
604-- | Strict left associative fold.
1015-- | Strict left associative fold.
605--
1016--
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+--
606-- @since 0.2.0
1096-- @since 0.2.0
607{-# INLINE foldl' #-}
1097{-# INLINE foldl' #-}
608foldl' :: Monad m => (b -> a -> b) -> b -> SerialT m a -> m b
1098foldl' :: Monad m => (b -> a -> b) -> b -> SerialT m a -> m b
@@ -622,7 +1112,6 @@ foldl1' step m = do
622 res <- foldl' step h t
1112 res <- foldl' step h t
623 return $ Just res
1113 return $ Just res
624
1114
625−-- XXX replace the recursive "go" with explicit continuations.
626-- | Like 'foldx', but with a monadic step function.
1115-- | Like 'foldx', but with a monadic step function.
627--
1116--
628-- @since 0.2.0
1117-- @since 0.2.0
@@ -654,6 +1143,8 @@ null = K.null
654
1143
655-- | Extract the first element of the stream, if any.
1144-- | Extract the first element of the stream, if any.
656--
1145--
1146+-- > head = (!! 0)
1147+--
657-- @since 0.1.0
1148-- @since 0.1.0
658{-# INLINE head #-}
1149{-# INLINE head #-}
659head :: Monad m => SerialT m a -> m (Maybe a)
1150head :: Monad m => SerialT m a -> m (Maybe a)
@@ -675,6 +1166,8 @@ init m = K.init (K.adapt m)
675
1166
676-- | Extract the last element of the stream, if any.
1167-- | Extract the last element of the stream, if any.
677--
1168--
1169+-- > last xs = xs !! (length xs - 1)
1170+--
678-- @since 0.1.1
1171-- @since 0.1.1
679{-# INLINE last #-}
1172{-# INLINE last #-}
680last :: Monad m => SerialT m a -> m (Maybe a)
1173last :: Monad m => SerialT m a -> m (Maybe a)
@@ -722,75 +1215,99 @@ any p m = S.any p (toStreamS m)
722and :: Monad m => SerialT m Bool -> m Bool
1215and :: Monad m => SerialT m Bool -> m Bool
723and = all (==True)
1216and = all (==True)
724
1217
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.
726--
1219--
727-- @since 0.5.0
1220-- @since 0.5.0
728{-# INLINE or #-}
1221{-# INLINE or #-}
729or :: Monad m => SerialT m Bool -> m Bool
1222or :: Monad m => SerialT m Bool -> m Bool
730or = any (==True)
1223or = any (==True)
731
1224
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.
733--
1228--
734-- @since 0.1.0
1229-- @since 0.1.0
735{-# INLINE sum #-}
1230{-# INLINE sum #-}
736sum :: (Monad m, Num a) => SerialT m a -> m a
1231sum :: (Monad m, Num a) => SerialT m a -> m a
737sum = foldl' (+) 0
1232sum = foldl' (+) 0
738
1233
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.
740--
1236--
741-- @since 0.1.1
1237-- @since 0.1.1
742{-# INLINE product #-}
1238{-# INLINE product #-}
743product :: (Monad m, Num a) => SerialT m a -> m a
1239product :: (Monad m, Num a) => SerialT m a -> m a
744product = foldl' (*) 1
1240product = foldl' (*) 1
745
1241
1242+-- |
1243+-- @
1244+-- minimum = 'minimumBy' compare
1245+-- @
1246+--
746−-- | Determine the minimum element in a stream.
1247+-- Determine the minimum element in a stream.
747--
1248--
748-- @since 0.1.0
1249-- @since 0.1.0
749{-# INLINE minimum #-}
1250{-# INLINE minimum #-}
750minimum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
1251minimum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
751minimum m = S.minimum (toStreamS m)
1252minimum m = S.minimum (toStreamS m)
752
1253
753−-- | Non-overloaded version of minimum.
1254+-- | Determine the minimum element in a stream using the supplied comparison
1255+-- function.
754--
1256--
1257+-- @since 0.6.0
755{-# INLINE minimumBy #-}
1258{-# INLINE minimumBy #-}
756minimumBy :: Monad m => (a -> a -> Ordering) -> SerialT m a -> m (Maybe a)
1259minimumBy :: Monad m => (a -> a -> Ordering) -> SerialT m a -> m (Maybe a)
757minimumBy cmp m = S.minimumBy cmp (toStreamS m)
1260minimumBy cmp m = S.minimumBy cmp (toStreamS m)
758
1261
1262+-- |
1263+-- @
1264+-- maximum = 'maximumBy' compare
1265+-- @
1266+--
759−-- | Determine the maximum element in a stream.
1267+-- Determine the maximum element in a stream.
760--
1268--
1269+-- @since 0.1.0
761{-# INLINE maximum #-}
1270{-# INLINE maximum #-}
762maximum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
1271maximum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
763maximum m = S.maximum (toStreamS m)
1272maximum m = S.maximum (toStreamS m)
764
1273
765−-- | Non-overloaded version of maximum.
1274+-- | Determine the maximum element in a stream using the supplied comparison
1275+-- function.
766--
1276--
1277+-- @since 0.6.0
767{-# INLINE maximumBy #-}
1278{-# INLINE maximumBy #-}
768maximumBy :: Monad m => (a -> a -> Ordering) -> SerialT m a -> m (Maybe a)
1279maximumBy :: Monad m => (a -> a -> Ordering) -> SerialT m a -> m (Maybe a)
769maximumBy cmp m = S.maximumBy cmp (toStreamS m)
1280maximumBy cmp m = S.maximumBy cmp (toStreamS m)
770
1281
771−-- | Looks the element at the given index.
1282+-- | Lookup the element at the given index.
772--
1283--
1284+-- @since 0.6.0
773{-# INLINE (!!) #-}
1285{-# INLINE (!!) #-}
774(!!) :: Monad m => SerialT m a -> Int -> m (Maybe a)
1286(!!) :: Monad m => SerialT m a -> Int -> m (Maybe a)
775−m !! i = (toStreamS m) S.!! i
1287+m !! i = toStreamS m S.!! i
776
1288
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)
778--
1293--
779-- @since 0.5.0
1294-- @since 0.5.0
780{-# INLINE lookup #-}
1295{-# INLINE lookup #-}
781lookup :: (Monad m, Eq a) => a -> SerialT m (a, b) -> m (Maybe b)
1296lookup :: (Monad m, Eq a) => a -> SerialT m (a, b) -> m (Maybe b)
782lookup a m = S.lookup a (toStreamS m)
1297lookup a m = S.lookup a (toStreamS m)
783
1298
784−-- | Returns the first element of the stream satisfying the given predicate,
1299+-- | Like 'findM' but with a non-monadic predicate.
785−-- if any.
1300+--
1301+-- > find p = findM (return . p)
786--
1302--
787-- @since 0.5.0
1303-- @since 0.5.0
788{-# INLINE find #-}
1304{-# INLINE find #-}
789find :: Monad m => (a -> Bool) -> SerialT m a -> m (Maybe a)
1305find :: Monad m => (a -> Bool) -> SerialT m a -> m (Maybe a)
790find p m = S.find p (toStreamS m)
1306find p m = S.find p (toStreamS m)
791
1307
792−-- | Like 'find' but with a monadic predicate.
1308+-- | Returns the first element that satisfies the given predicate.
793--
1309--
1310+-- @since 0.6.0
794{-# INLINE findM #-}
1311{-# INLINE findM #-}
795findM :: Monad m => (a -> m Bool) -> SerialT m a -> m (Maybe a)
1312findM :: Monad m => (a -> m Bool) -> SerialT m a -> m (Maybe a)
796findM p m = S.findM p (toStreamS m)
1313findM p m = S.findM p (toStreamS m)
@@ -803,8 +1320,7 @@ findM p m = S.findM p (toStreamS m)
803findIndices :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m Int
1320findIndices :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m Int
804findIndices p m = fromStreamS $ S.findIndices p (toStreamS m)
1321findIndices p m = fromStreamS $ S.findIndices p (toStreamS m)
805
1322
806−-- | Gives the index of the first stream element satisfying the given
1323+-- | Returns the first index that satisfies the given predicate.
807−-- preficate.
808--
1324--
809-- @since 0.5.0
1325-- @since 0.5.0
810{-# INLINE findIndex #-}
1326{-# INLINE findIndex #-}
@@ -819,46 +1335,74 @@ findIndex p = head . findIndices p
819elemIndices :: (IsStream t, Eq a, Monad m) => a -> t m a -> t m Int
1335elemIndices :: (IsStream t, Eq a, Monad m) => a -> t m a -> t m Int
820elemIndices a = findIndices (==a)
1336elemIndices a = findIndices (==a)
821
1337
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)
823--
1341--
824-- @since 0.5.0
1342-- @since 0.5.0
825{-# INLINE elemIndex #-}
1343{-# INLINE elemIndex #-}
826elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
1344elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
827−elemIndex a = findIndex (==a)
1345+elemIndex a = findIndex (== a)
828
1346
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
829{-# INLINE concatMap #-}
1353{-# 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
831concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
1355concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
832
1356
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
833{-# INLINE concatMapM #-}
1361{-# 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
835concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
1363concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
836
1364
837------------------------------------------------------------------------------
1365------------------------------------------------------------------------------
838-- Substreams
1366-- Substreams
839------------------------------------------------------------------------------
1367------------------------------------------------------------------------------
840
1368
841−-- | Takes two streams and returns 'True' iff the first stream is a prefix
1369+-- | Returns 'True' if the first stream is the same as or a prefix of the
842−-- of the second.
1370+-- second.
1371+--
1372+-- @
1373+-- > S.isPrefixOf (S.fromList "hello") (S.fromList "hello" :: SerialT IO Char)
1374+-- True
1375+-- @
843--
1376--
1377+-- @since 0.6.0
844{-# INLINE isPrefixOf #-}
1378{-# INLINE isPrefixOf #-}
845isPrefixOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
1379isPrefixOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
846isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
1380isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
847
1381
848−-- | Takes two streams and returns 'True' if all the elements of the
1382+-- | Returns 'True' if all the elements of the first stream occur, in order, in
849−-- first stream occur, in order, in the second stream. The elements do
850−-- not have to occur consecutively.
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+-- @
851--
1390--
1391+-- @since 0.6.0
852{-# INLINE isSubsequenceOf #-}
1392{-# INLINE isSubsequenceOf #-}
853isSubsequenceOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
1393isSubsequenceOf :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m Bool
854isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)
1394isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)
855
1395
856−-- | Drops the given prefix from a stream. Returns Nothing if the stream
1396+-- | Drops the given prefix from a stream. Returns 'Nothing' if the stream does
857−-- doesn't start with the prefix given.
1397+-- not start with the given prefix. Returns @Just nil@ when the prefix is the
1398+-- same as the stream.
858--
1399--
1400+-- @since 0.6.0
859{-# INLINE stripPrefix #-}
1401{-# INLINE stripPrefix #-}
1402+stripPrefix
860−stripPrefix :: (Eq a, IsStream t, Monad m) => t m a -> t m a -> m (Maybe (t m a))
1403+ :: (Eq a, IsStream t, Monad m)
1404+ => t m a -> t m a -> m (Maybe (t m a))
861−stripPrefix m1 m2 = fmap (fmap fromStreamD) $
1405+stripPrefix m1 m2 = fmap fromStreamD <$>
862 D.stripPrefix (toStreamD m1) (toStreamD m2)
1406 D.stripPrefix (toStreamD m1) (toStreamD m2)
863
1407
864------------------------------------------------------------------------------
1408------------------------------------------------------------------------------
@@ -878,14 +1422,24 @@ mapM_ f m = S.mapM_ f $ toStreamS m
878-- Conversions
1422-- Conversions
879------------------------------------------------------------------------------
1423------------------------------------------------------------------------------
880
1424
1425+-- |
1426+-- @
1427+-- toList = S.foldr (:) []
1428+-- @
1429+--
881−-- | Convert a stream into a list in the underlying monad.
1430+-- Convert a stream into a list in the underlying monad. Same as:
882--
1431--
883-- @since 0.1.0
1432-- @since 0.1.0
884{-# INLINE toList #-}
1433{-# INLINE toList #-}
885toList :: Monad m => SerialT m a -> m [a]
1434toList :: Monad m => SerialT m a -> m [a]
886toList = P.toList
1435toList = P.toList
887
1436
1437+-- |
1438+-- @
1439+-- toHandle h = S.mapM_ $ hPutStrLn h
1440+-- @
1441+--
888−-- | Write a stream of Strings to an IO Handle.
1442+-- Write a stream of Strings to an IO Handle.
889--
1443--
890-- @since 0.1.0
1444-- @since 0.1.0
891toHandle :: MonadIO m => IO.Handle -> SerialT m String -> m ()
1445toHandle :: MonadIO m => IO.Handle -> SerialT m String -> m ()
@@ -917,56 +1471,116 @@ scanx = K.scanx
917scan :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
1471scan :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
918scan = scanx
1472scan = scanx
919
1473
1474+-- XXX this needs to be concurrent
920−-- | Like 'scanl'' but with a monadic step function.
1475+-- | Like 'scanl'' but with a monadic fold function.
921--
1476--
922-- @since 0.4.0
1477-- @since 0.4.0
923{-# INLINE scanlM' #-}
1478{-# INLINE scanlM' #-}
924scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
1479scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
925scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
1480scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
926
1481
927−-- | Strict left scan. Like 'foldl'', but returns the folded value at each
1482+-- | Strict left scan.
928−-- step, generating a stream of all intermediate fold results. The first
1483+--
929−-- element of the stream is the user supplied initial value, and the last
1484+-- @
930−-- element of the stream is the same as the result of 'foldl''.
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.
931--
1524--
932-- @since 0.2.0
1525-- @since 0.2.0
933{-# INLINE scanl' #-}
1526{-# INLINE scanl' #-}
934scanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
1527scanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
935scanl' step z m = fromStreamS $ S.scanl' step z $ toStreamS m
1528scanl' step z m = fromStreamS $ S.scanl' step z $ toStreamS m
936
1529
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.
938--
1532--
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
939−{-# INLINE scanlM #-}
1544+{-# INLINE _postscanlM' #-}
940−scanlM :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
1545+_postscanlM' :: (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
1546+_postscanlM' step z m = fromStreamD $ D.postscanlM' step z $ toStreamD m
942
1547
1548+-- XXX prescanl does not sound very useful, enable only if there is a
1549+-- compelling use case.
1550+--
943−-- | Like 'scanl'' but with a non-strict accumulator.
1551+-- | Like scanl' but does not stream the final value of the accumulator.
1552+--
1553+-- @since 0.6.0
944−{-# INLINE scanl #-}
1554+{-# INLINE _prescanl' #-}
945−scanl :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
1555+_prescanl' :: (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
1556+_prescanl' step z m = fromStreamD $ D.prescanl' step z $ toStreamD m
947
1557
1558+-- XXX this needs to be concurrent
948−-- | Left scan with a monadic step function. Uses the first element of
1559+-- | Like postscanl' but with a monadic step function.
949−-- the stream as the starting value. Returns an empty stream if the
950−-- given stream is empty.
951--
1560--
1561+-- @since 0.6.0
952−{-# INLINE scanl1M #-}
1562+{-# INLINE _prescanlM' #-}
953−scanl1M :: (IsStream t, Monad m) => (a -> a -> m a) -> t m a -> t m a
1563+_prescanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
954−scanl1M step m = fromStreamD $ D.scanl1M step $ toStreamD m
1564+_prescanlM' step z m = fromStreamD $ D.prescanlM' step z $ toStreamD m
955
1565
956−-- | Like 'scanl1M' but with a strict accumulator.
1566+-- XXX this needs to be concurrent
1567+-- | Like 'scanl1'' but with a monadic step function.
957--
1568--
1569+-- @since 0.6.0
958{-# INLINE scanl1M' #-}
1570{-# INLINE scanl1M' #-}
959scanl1M' :: (IsStream t, Monad m) => (a -> a -> m a) -> t m a -> t m a
1571scanl1M' :: (IsStream t, Monad m) => (a -> a -> m a) -> t m a -> t m a
960scanl1M' step m = fromStreamD $ D.scanl1M' step $ toStreamD m
1572scanl1M' step m = fromStreamD $ D.scanl1M' step $ toStreamD m
961
1573
962−-- | Left scan, uses the first element of the stream as the starting
1574+-- | Like 'scanl'' but for a non-empty stream. The first element of the stream
963−-- value. Returns an empty stream if the given stream is empty.
1575+-- is used as the initial value of the accumulator. Does nothing if the stream
964−{-# INLINE scanl1 #-}
1576+-- is empty.
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.
969--
1577--
1578+-- @
1579+-- > S.toList $ S.scanl1 (+) $ fromList [1,2,3,4]
1580+-- [1,3,6,10]
1581+-- @
1582+--
1583+-- @since 0.6.0
970{-# INLINE scanl1' #-}
1584{-# INLINE scanl1' #-}
971scanl1' :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> t m a
1585scanl1' :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> t m a
972scanl1' step m = fromStreamD $ D.scanl1' step $ toStreamD m
1586scanl1' step m = fromStreamD $ D.scanl1' step $ toStreamD m
@@ -997,10 +1611,19 @@ filterM p m = fromStreamD $ D.filterM p $ toStreamD m
997
1611
998-- | Drop repeated elements that are adjacent to each other.
1612-- | Drop repeated elements that are adjacent to each other.
999--
1613--
1614+-- @since 0.6.0
1000{-# INLINE uniq #-}
1615{-# INLINE uniq #-}
1001uniq :: (Eq a, IsStream t, Monad m) => t m a -> t m a
1616uniq :: (Eq a, IsStream t, Monad m) => t m a -> t m a
1002uniq = fromStreamD . D.uniq . toStreamD
1617uniq = fromStreamD . D.uniq . toStreamD
1003
1618
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+
1004-- | Take first 'n' elements from the stream and discard the rest.
1627-- | Take first 'n' elements from the stream and discard the rest.
1005--
1628--
1006-- @since 0.1.0
1629-- @since 0.1.0
@@ -1049,10 +1672,18 @@ dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
1049-- Transformation by Mapping
1672-- Transformation by Mapping
1050------------------------------------------------------------------------------
1673------------------------------------------------------------------------------
1051
1674
1675+-- |
1676+-- @
1677+-- mapM f = sequence . map f
1678+-- @
1679+--
1052−-- | Replace each element of the stream with the result of a monadic action
1680+-- Apply a monadic function to each element of the stream and replace it with
1053−-- applied on the element.
1681+-- the output of the resulting action.
1054--
1682--
1055-- @
1683-- @
1684+-- > runStream $ S.mapM putStr $ S.fromList ["a", "b", "c"]
1685+-- abc
1686+--
1056-- runStream $ S.replicateM 10 (return 1)
1687-- runStream $ S.replicateM 10 (return 1)
1057-- & (serially . S.mapM (\\x -> threadDelay 1000000 >> print x))
1688-- & (serially . S.mapM (\\x -> threadDelay 1000000 >> print x))
1058--
1689--
@@ -1072,10 +1703,18 @@ mapM = K.mapM
1072mapMSerial :: Monad m => (a -> m b) -> SerialT m a -> SerialT m b
1703mapMSerial :: Monad m => (a -> m b) -> SerialT m a -> SerialT m b
1073mapMSerial = Serial.mapM
1704mapMSerial = Serial.mapM
1074
1705
1706+-- |
1707+-- @
1708+-- sequence = mapM id
1709+-- @
1710+--
1075−-- | Reduce a stream of monadic actions to a stream of the output of those
1711+-- Replace the elements of a stream of monadic actions with the outputs of
1076−-- actions.
1712+-- those actions.
1077--
1713--
1078-- @
1714-- @
1715+-- > runStream $ S.sequence $ S.fromList [putStr "a", putStr "b", putStrLn "c"]
1716+-- abc
1717+--
1079-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1718-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1080-- & (serially . S.sequence)
1719-- & (serially . S.sequence)
1081--
1720--
@@ -1142,17 +1781,34 @@ reverse m = fromStream $ go K.nil (toStream m)
1142-- Transformation by Inserting
1781-- Transformation by Inserting
1143------------------------------------------------------------------------------
1782------------------------------------------------------------------------------
1144
1783
1145−-- | Generate a stream by performing the monadic action inbetween all elements
1784+-- | Generate a stream by performing a monadic action between consecutive
1146−-- of the given stream.
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+-- @
1147--
1793--
1148-- @since 0.5.0
1794-- @since 0.5.0
1149{-# INLINE intersperseM #-}
1795{-# INLINE intersperseM #-}
1150intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1796intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1151intersperseM = K.intersperseM
1797intersperseM = K.intersperseM
1152
1798
1153−-- | Inserts @x@ before the first element in the Stream that is
1799+-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
1154−-- less than @x@, based on the user supplied comparison function.
1800+-- @stream@ that is less than @elem@ when compared using @cmp@.
1155--
1801--
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
1156{-# INLINE insertBy #-}
1812{-# INLINE insertBy #-}
1157insertBy ::
1813insertBy ::
1158 (IsStream t, Monad m) => (a -> a -> Ordering) -> a -> t m a -> t m a
1814 (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)
1162-- Deleting
1818-- Deleting
1163------------------------------------------------------------------------------
1819------------------------------------------------------------------------------
1164
1820
1165−-- | Deletes the first occurence of @x@ in the Stream that satisfies the
1821+-- | Deletes the first occurence of the element in the stream that satisfies
1166−-- equality predicate.
1822+-- the given equality predicate.
1167--
1823--
1824+-- @
1825+-- > S.toList $ S.deleteBy (==) 3 $ S.fromList [1,3,3,5]
1826+-- [1,3,5]
1827+-- @
1828+--
1829+-- @since 0.6.0
1168{-# INLINE deleteBy #-}
1830{-# INLINE deleteBy #-}
1169deleteBy :: (IsStream t, Monad m) => (a -> a -> Bool) -> a -> t m a -> t m a
1831deleteBy :: (IsStream t, Monad m) => (a -> a -> Bool) -> a -> t m a -> t m a
1170deleteBy cmp x m = fromStreamS $ S.deleteBy cmp x (toStreamS m)
1832deleteBy 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)
1173-- Zipping
1835-- Zipping
1174------------------------------------------------------------------------------
1836------------------------------------------------------------------------------
1175
1837
1838+-- |
1839+-- > indexed = S.zipWith (,) (S.intFrom 0)
1840+--
1176−-- | Pair each element in a stream with its index.
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+-- @
1177--
1847--
1848+-- @since 0.6.0
1178{-# INLINE indexed #-}
1849{-# INLINE indexed #-}
1179indexed :: (IsStream t, Monad m) => t m a -> t m (Int, a)
1850indexed :: (IsStream t, Monad m) => t m a -> t m (Int, a)
1180indexed = fromStreamD . D.indexed . toStreamD
1851indexed = fromStreamD . D.indexed . toStreamD
1181
1852
1853+-- |
1854+-- > indexedR n = S.zipWith (,) (S.intFromThen n (n - 1))
1855+--
1182−-- | Pair each element in a stream with its index, starting from the
1856+-- Pair each element in a stream with its index, starting from the
1183-- given index @n@ and counting down.
1857-- given index @n@ and counting down.
1184--
1858--
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
1185{-# INLINE indexedR #-}
1865{-# INLINE indexedR #-}
1186indexedR :: (IsStream t, Monad m) => Int -> t m a -> t m (Int, a)
1866indexedR :: (IsStream t, Monad m) => Int -> t m a -> t m (Int, a)
1187indexedR n = fromStreamD . D.indexedR n . toStreamD
1867indexedR n = fromStreamD . D.indexedR n . toStreamD
1188
1868
1189−-- | Zip two streams serially using a monadic zipping function.
1869+-- | Like 'zipWith' but using a monadic zipping function.
1190--
1870--
1191-- @since 0.4.0
1871-- @since 0.4.0
1192{-# INLINABLE zipWithM #-}
1872{-# INLINABLE zipWithM #-}
@@ -1195,6 +1875,11 @@ zipWithM f m1 m2 = fromStreamS $ S.zipWithM f (toStreamS m1) (toStreamS m2)
1195
1875
1196-- | Zip two streams serially using a pure zipping function.
1876-- | Zip two streams serially using a pure zipping function.
1197--
1877--
1878+-- @
1879+-- > S.toList $ S.zipWith (+) (S.fromList [1,2,3]) (S.fromList [4,5,6])
1880+-- [5,7,9]
1881+-- @
1882+--
1198-- @since 0.1.0
1883-- @since 0.1.0
1199{-# INLINABLE zipWith #-}
1884{-# INLINABLE zipWith #-}
1200zipWith :: (IsStream t, Monad m) => (a -> b -> c) -> t m a -> t m b -> t m c
1885zipWith :: (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)
1204-- Comparison
1889-- Comparison
1205------------------------------------------------------------------------------
1890------------------------------------------------------------------------------
1206
1891
1207−-- | Compare two streams for equality using a equality function.
1892+-- | Compare two streams for equality using an equality function.
1208--
1893--
1209−-- @since 0.5.3
1894+-- @since 0.6.0
1210{-# INLINABLE eqBy #-}
1895{-# 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
1212eqBy = P.eqBy
1897eqBy = P.eqBy
1213
1898
1214−-- | Compare two streams using a comparison function.
1899+-- | Compare two streams lexicographically using a comparison function.
1215--
1900--
1216−-- @since 0.5.3
1901+-- @since 0.6.0
1217{-# INLINABLE cmpBy #-}
1902{-# INLINABLE cmpBy #-}
1218cmpBy
1903cmpBy
1219− :: Monad m
1904+ :: (IsStream t, Monad m)
1220− => (a -> b -> Ordering) -> SerialT m a -> SerialT m b -> m Ordering
1905+ => (a -> b -> Ordering) -> t m a -> t m b -> m Ordering
1221cmpBy = P.cmpBy
1906cmpBy = P.cmpBy
1222
1907
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
1224{-# INLINABLE mergeBy #-}
1926{-# INLINABLE mergeBy #-}
1225mergeBy ::
1927mergeBy ::
1226 (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1928 (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1227mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
1929mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
1228
1930
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
1230{-# INLINABLE merge #-}
1977{-# INLINABLE merge #-}
1231merge ::
1978merge ::
1232 (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
1979 (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
1233merge = mergeBy compare
1980merge = mergeBy compare
1234−
1981+-}
1235−-- | Ensures all the elements are identical and then returns
1982+
1236−-- that unique element.
1983+-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
1237−{-# INLINE the #-}
1984+-- merged are generated concurrently).
1238−the :: (Eq a, Monad m) => SerialT m a -> m (Maybe a)
1985+--
1239−the m = S.the (toStreamS m)
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 { \
89instance Read a => Read (STREAM Identity a) where { \
89instance Read a => Read (STREAM Identity a) where { \
90 readPrec = parens $ prec 10 $ do { \
90 readPrec = parens $ prec 10 $ do { \
91 Ident "fromList" <- lexP; \
91 Ident "fromList" <- lexP; \
92− dl <- readPrec; \
92+ fromList <$> readPrec }; \
93− return (fromList dl) }; \
94 readListPrec = readListPrecDefault }; \
93 readListPrec = readListPrecDefault }; \
95 \
94 \
96instance (a ~ Char) => IsString (STREAM Identity a) where { \
95instance (a ~ Char) => IsString (STREAM Identity a) where { \
@@ -119,4 +118,4 @@ instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
119instance Traversable (STREAM Identity) where { \
118instance Traversable (STREAM Identity) where { \
120 {-# INLINE traverse #-}; \
119 {-# INLINE traverse #-}; \
121 traverse f s = runIdentity $ P.foldr consA (pure mempty) s \
120 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
72-- Conversions
72-- Conversions
73------------------------------------------------------------------------------
73------------------------------------------------------------------------------
74
74
75+-- |
76+-- @
77+-- fromList = 'Prelude.foldr' 'K.cons' 'K.nil'
78+-- @
79+--
75−-- | Construct a stream from a list containing pure values. More efficient list
80+-- Construct a stream from a list of pure values. This is more efficient than
76−-- specific implementation of 'K.fromFoldable' as it works well with fusion
81+-- 'K.fromFoldable' for serial streams.
77−-- optimization.
78--
82--
79-- @since 0.4.0
83-- @since 0.4.0
80{-# INLINE_EARLY fromList #-}
84{-# INLINE_EARLY fromList #-}
src/Streamly/Streams/Serial.hs+11−1
@@ -198,7 +198,17 @@ instance Monad m => Monad (SerialT m) where
198mapM :: (IsStream t, Monad m) => (a -> m b) -> t m a -> t m b
198mapM :: (IsStream t, Monad m) => (a -> m b) -> t m a -> t m b
199mapM f m = fromStream $ D.toStreamK $ D.mapM f $ D.fromStreamK (toStream m)
199mapM f m = fromStream $ D.toStreamK $ D.mapM f $ D.fromStreamK (toStream m)
200
200
201+-- |
202+-- @
203+-- map = fmap
204+-- @
205+--
201−-- | Same as 'fmap'.
206+-- Same as 'fmap'.
207+--
208+-- @
209+-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
210+-- [2,3,4]
211+-- @
202--
212--
203-- @since 0.4.0
213-- @since 0.4.0
204{-# INLINE map #-}
214{-# INLINE map #-}
src/Streamly/Streams/StreamD.hs+253−29
@@ -12,6 +12,7 @@
12-- |
12-- |
13-- Module : Streamly.Streams.StreamD
13-- Module : Streamly.Streams.StreamD
14-- Copyright : (c) 2018 Harendra Kumar
14-- Copyright : (c) 2018 Harendra Kumar
15+-- Copyright : (c) Roman Leshchinskiy 2008-2010
15--
16--
16-- License : BSD3
17-- License : BSD3
17-- Maintainer : harendra.kumar@gmail.com
18-- Maintainer : harendra.kumar@gmail.com
@@ -27,7 +28,7 @@
27-- import qualified Streamly.Streams.StreamD as D
28-- import qualified Streamly.Streams.StreamD as D
28-- @
29-- @
29
30
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
31-- library, https://hackage.haskell.org/package/vector.
32-- library, https://hackage.haskell.org/package/vector.
32
33
33module Streamly.Streams.StreamD
34module Streamly.Streams.StreamD
@@ -51,15 +52,31 @@ module Streamly.Streams.StreamD
51 -- ** Specialized Generation
52 -- ** Specialized Generation
52 -- | Generate a monadic stream from a seed.
53 -- | Generate a monadic stream from a seed.
53 , repeat
54 , 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
55
74
56 -- ** Conversions
75 -- ** Conversions
57 -- | Transform an input structure into a stream.
76 -- | Transform an input structure into a stream.
58 -- | Direct style stream does not support @fromFoldable@.
77 -- | Direct style stream does not support @fromFoldable@.
59 , yield
78 , yield
60 , yieldM
79 , yieldM
61− , generate
62− , generateM
63 , fromList
80 , fromList
64 , fromListM
81 , fromListM
65 , fromStreamK
82 , fromStreamK
@@ -120,6 +137,13 @@ module Streamly.Streams.StreamD
120 , scanl1M
137 , scanl1M
121 , scanl1
138 , scanl1
122
139
140+ , prescanl'
141+ , prescanlM'
142+ , postscanl
143+ , postscanlM
144+ , postscanl'
145+ , postscanlM'
146+
123 -- * Filtering
147 -- * Filtering
124 , filter
148 , filter
125 , filterM
149 , filterM
@@ -158,6 +182,7 @@ module Streamly.Streams.StreamD
158
182
159 -- * Merging
183 -- * Merging
160 , mergeBy
184 , mergeBy
185+ , mergeByM
161
186
162 -- * Transformation comprehensions
187 -- * Transformation comprehensions
163 , the
188 , the
@@ -170,7 +195,7 @@ import Prelude
170 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
195 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
171 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
196 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
172 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
197 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
173− (!!), scanl, scanl1, concatMap)
198+ (!!), scanl, scanl1, concatMap, replicate, enumFromTo)
174
199
175import Streamly.SVar (MonadAsync, State(..), defState, rstState)
200import Streamly.SVar (MonadAsync, State(..), defState, rstState)
176
201
@@ -262,14 +287,143 @@ unfoldr f = unfoldrM (return . f)
262repeat :: Monad m => a -> Stream m a
287repeat :: Monad m => a -> Stream m a
263repeat x = Stream (\_ _ -> return $ Yield x ()) ()
288repeat x = Stream (\_ _ -> return $ Yield x ()) ()
264
289
265−{-# INLINE_NORMAL enumFromStepN #-}
290+{-# INLINE_NORMAL replicateM #-}
266−enumFromStepN :: (Num a, Monad m) => a -> a -> Int -> Stream m a
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
267−enumFromStepN from stride n =
310+intFromStep from stride =
268− from `seq` stride `seq` n `seq` Stream step (from, n)
311+ from `seq` stride `seq` Stream step from
269 where
312 where
270 {-# INLINE_LATE step #-}
313 {-# INLINE_LATE step #-}
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 #-}
271− step _ (x, i) | i > 0 = return $ Yield x (x + stride, i - 1)
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)
272− | otherwise = return Stop
426+ | otherwise = (>= to + mid)
273
427
274-------------------------------------------------------------------------------
428-------------------------------------------------------------------------------
275-- Generation by Conversion
429-- Generation by Conversion
@@ -293,9 +447,18 @@ yieldM m = Stream step True
293 step _ True = m >>= \x -> return $ Yield x False
447 step _ True = m >>= \x -> return $ Yield x False
294 step _ False = return Stop
448 step _ False = return Stop
295
449
296−{-# INLINE generate #-}
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 #-}
297−generate :: Monad m => Int -> (Int -> a) -> Stream m a
460+fromIndices :: Monad m => (Int -> a) -> Stream m a
298−generate n gen = generateM n (return . gen)
461+fromIndices gen = fromIndicesM (return . gen)
299
462
300{-# INLINE_NORMAL generateM #-}
463{-# INLINE_NORMAL generateM #-}
301generateM :: Monad m => Int -> (Int -> m a) -> Stream m a
464generateM :: Monad m => Int -> (Int -> m a) -> Stream m a
@@ -304,9 +467,13 @@ generateM n gen = n `seq` Stream step 0
304 {-# INLINE_LATE step #-}
467 {-# INLINE_LATE step #-}
305 step _ i | i < n = do
468 step _ i | i < n = do
306 x <- gen i
469 x <- gen i
307− return $ Yield x (i+1)
470+ return $ Yield x (i + 1)
308 | otherwise = return Stop
471 | otherwise = return Stop
309
472
473+{-# INLINE generate #-}
474+generate :: Monad m => Int -> (Int -> a) -> Stream m a
475+generate n gen = generateM n (return . gen)
476+
310-- XXX we need the MonadAsync constraint because of a rewrite rule.
477-- XXX we need the MonadAsync constraint because of a rewrite rule.
311-- | Convert a list of monadic actions to a 'Stream'
478-- | Convert a list of monadic actions to a 'Stream'
312{-# INLINE_LATE fromListM #-}
479{-# INLINE_LATE fromListM #-}
@@ -613,14 +780,11 @@ findIndices p (Stream step state) = Stream step' (state, 0)
613 Skip s -> Skip (s, i+1)
780 Skip s -> Skip (s, i+1)
614 Stop -> Stop
781 Stop -> Stop
615
782
616−{-# INLINE concatMap #-}
617−concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
618−concatMap f = concatMapM (return . f)
619−
620{-# INLINE_NORMAL concatMapM #-}
783{-# INLINE_NORMAL concatMapM #-}
621concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
784concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
622concatMapM f (Stream step state) = Stream step' (Left state)
785concatMapM f (Stream step state) = Stream step' (Left state)
623 where
786 where
787+ {-# INLINE_LATE step' #-}
624 step' gst (Left st) = do
788 step' gst (Left st) = do
625 r <- step (rstState gst) st
789 r <- step (rstState gst) st
626 case r of
790 case r of
@@ -639,6 +803,10 @@ concatMapM f (Stream step state) = Stream step' (Left state)
639 return $ Skip (Right (Stream inner_step inner_s, st))
803 return $ Skip (Right (Stream inner_step inner_s, st))
640 Stop -> return $ Skip (Left st)
804 Stop -> return $ Skip (Left st)
641
805
806+{-# INLINE concatMap #-}
807+concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
808+concatMap f = concatMapM (return . f)
809+
642------------------------------------------------------------------------------
810------------------------------------------------------------------------------
643-- Substreams
811-- Substreams
644------------------------------------------------------------------------------
812------------------------------------------------------------------------------
@@ -686,7 +854,9 @@ isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
686 Stop -> return False
854 Stop -> return False
687
855
688{-# INLINE_NORMAL stripPrefix #-}
856{-# INLINE_NORMAL stripPrefix #-}
857+stripPrefix
858+ :: (Eq a, Monad m)
689−stripPrefix :: (Eq a, Monad m) => Stream m a -> Stream m a -> m (Maybe (Stream m a))
859+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
690stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
860stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
691 where
861 where
692 go (sa, sb, Nothing) = do
862 go (sa, sb, Nothing) = do
@@ -750,6 +920,43 @@ fromStreamD = K.fromStream . toStreamK
750-- Transformation by Folding (Scans)
920-- Transformation by Folding (Scans)
751------------------------------------------------------------------------------
921------------------------------------------------------------------------------
752
922
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.
753{-# INLINE_NORMAL postscanlM' #-}
960{-# INLINE_NORMAL postscanlM' #-}
754postscanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
961postscanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
755postscanlM' fstep begin (Stream step state) =
962postscanlM' fstep begin (Stream step state) =
@@ -765,6 +972,10 @@ postscanlM' fstep begin (Stream step state) =
765 Skip s -> return $ Skip (s, acc)
972 Skip s -> return $ Skip (s, acc)
766 Stop -> return Stop
973 Stop -> return Stop
767
974
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+
768{-# INLINE_NORMAL postscanlM #-}
979{-# INLINE_NORMAL postscanlM #-}
769postscanlM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
980postscanlM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
770postscanlM fstep begin (Stream step state) = Stream step' (state, begin)
981postscanlM fstep begin (Stream step state) = Stream step' (state, begin)
@@ -779,6 +990,10 @@ postscanlM fstep begin (Stream step state) = Stream step' (state, begin)
779 Skip s -> return $ Skip (s, acc)
990 Skip s -> return $ Skip (s, acc)
780 Stop -> return Stop
991 Stop -> return Stop
781
992
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+
782{-# INLINE_NORMAL scanlM' #-}
997{-# INLINE_NORMAL scanlM' #-}
783scanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
998scanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b
784scanlM' fstep begin s = begin `seq` (begin `cons` postscanlM' fstep begin s)
999scanlM' 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
1198-- Merging
1413-- Merging
1199------------------------------------------------------------------------------
1414------------------------------------------------------------------------------
1200
1415
1201−{-# INLINE_NORMAL mergeBy #-}
1416+{-# INLINE_NORMAL mergeByM #-}
1417+mergeByM
1418+ :: (Monad m)
1202−mergeBy :: (Monad m) => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
1419+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
1203−mergeBy f (Stream stepa ta) (Stream stepb tb) =
1420+mergeByM cmp (Stream stepa ta) (Stream stepb tb) =
1204 Stream step (Just ta, Just tb, Nothing, Nothing)
1421 Stream step (Just ta, Just tb, Nothing, Nothing)
1205 where
1422 where
1206 {-# INLINE_LATE step #-}
1423 {-# INLINE_LATE step #-}
@@ -1221,20 +1438,27 @@ mergeBy f (Stream stepa ta) (Stream stepb tb) =
1221 Stop -> Skip (sa, Nothing, a, Nothing)
1438 Stop -> Skip (sa, Nothing, a, Nothing)
1222
1439
1223 -- both the values are available
1440 -- both the values are available
1224− step _ (sa, sb, Just a, Just b) =
1441+ step _ (sa, sb, Just a, Just b) = do
1442+ res <- cmp a b
1225− return $ case f a b of
1443+ return $ case res of
1226− LT -> Yield a (sa, sb, Nothing, Just b)
1444+ GT -> Yield b (sa, sb, Just a, Nothing)
1227− _ -> Yield b (sa, sb, Just a, Nothing)
1445+ _ -> Yield a (sa, sb, Nothing, Just b)
1228
1446
1229 -- one of the values is missing, corresponding stream is done
1447 -- 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) =
1231 return $ Yield b (Nothing, sb, Nothing, Nothing)
1449 return $ Yield b (Nothing, sb, Nothing, Nothing)
1232
1450
1233− step _ (sa, Nothing, Just a, Nothing) = do
1451+ step _ (sa, Nothing, Just a, Nothing) =
1234 return $ Yield a (sa, Nothing, Nothing, Nothing)
1452 return $ Yield a (sa, Nothing, Nothing, Nothing)
1235
1453
1236 step _ (Nothing, Nothing, Nothing, Nothing) = return Stop
1454 step _ (Nothing, Nothing, Nothing, Nothing) = return Stop
1237
1455
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+
1238------------------------------------------------------------------------------
1462------------------------------------------------------------------------------
1239-- Transformation comprehensions
1463-- Transformation comprehensions
1240------------------------------------------------------------------------------
1464------------------------------------------------------------------------------
src/Streamly/Streams/StreamK.hs+67−51
@@ -59,6 +59,8 @@ module Streamly.Streams.StreamK
59
59
60 -- ** Specialized Generation
60 -- ** Specialized Generation
61 , repeat
61 , repeat
62+ , replicate
63+ , replicateM
62
64
63 -- ** Conversions
65 -- ** Conversions
64 , yield
66 , yield
@@ -139,6 +141,7 @@ module Streamly.Streams.StreamK
139
141
140 -- ** Merging
142 -- ** Merging
141 , mergeBy
143 , mergeBy
144+ , mergeByM
142
145
143 -- ** Transformation comprehensions
146 -- ** Transformation comprehensions
144 , the
147 , the
@@ -166,7 +169,7 @@ import Prelude
166 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
169 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
167 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
170 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
168 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
171 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
169− foldr1, (!!))
172+ foldr1, (!!), replicate)
170import qualified Prelude
173import qualified Prelude
171
174
172import Streamly.SVar
175import Streamly.SVar
@@ -472,19 +475,38 @@ yieldM m = fromStream $ Stream $ \_ _ single _ -> m >>= single
472once :: (Monad m, IsStream t) => m a -> t m a
475once :: (Monad m, IsStream t) => m a -> t m a
473once = yieldM
476once = yieldM
474
477
478+-- |
479+-- @
480+-- repeatM = fix . cons
481+-- repeatM = cycle1 . yield
482+-- @
483+--
475−-- | Generate an infinite stream by repeating a pure value.
484+-- Generate an infinite stream by repeating a pure value.
476−-- Can be expressed as @cycle1 . yield@.
477--
485--
478-- @since 0.4.0
486-- @since 0.4.0
479repeat :: IsStream t => a -> t m a
487repeat :: IsStream t => a -> t m a
480repeat a = let x = cons a x in x
488repeat a = let x = cons a x in x
481
489
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+
482-------------------------------------------------------------------------------
500-------------------------------------------------------------------------------
483-- Conversions
501-- Conversions
484-------------------------------------------------------------------------------
502-------------------------------------------------------------------------------
485
503
504+-- |
505+-- @
506+-- fromFoldable = 'Prelude.foldr' 'cons' 'nil'
507+-- @
508+--
486−-- | Construct a stream from a 'Foldable' containing pure values. Same as
509+-- Construct a stream from a 'Foldable' containing pure values:
487−-- @'Prelude.foldr' 'cons' 'nil'@.
488--
510--
489-- @since 0.2.0
511-- @since 0.2.0
490{-# INLINE fromFoldable #-}
512{-# INLINE fromFoldable #-}
@@ -556,6 +578,8 @@ foldr1 step m = do
556-- left fold, but applies a user supplied extraction function (the third
578-- left fold, but applies a user supplied extraction function (the third
557-- argument) to the folded value at the end. This is designed to work with the
579-- argument) to the folded value at the end. This is designed to work with the
558-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
580-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
581+--
582+-- Note that the accumulator is always evaluated including the initial value.
559{-# INLINE foldx #-}
583{-# INLINE foldx #-}
560foldx :: (IsStream t, Monad m)
584foldx :: (IsStream t, Monad m)
561 => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
585 => (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
563 where
587 where
564 {-# NOINLINE get #-}
588 {-# NOINLINE get #-}
565 get m1 =
589 get m1 =
590+ -- XXX we are not strictly evaluating the accumulator here. Is this
591+ -- okay?
566 let single = return . done
592 let single = return . done
567 in unStream m1 undefined undefined single undefined
593 in unStream m1 undefined undefined single undefined
568
594
@@ -719,7 +745,9 @@ minimum m = go Nothing (toStream m)
719 in unStream m1 defState stop single yieldk
745 in unStream m1 defState stop single yieldk
720
746
721{-# INLINE minimumBy #-}
747{-# INLINE minimumBy #-}
748+minimumBy
749+ :: (IsStream t, Monad m)
722−minimumBy :: (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> m (Maybe a)
750+ => (a -> a -> Ordering) -> t m a -> m (Maybe a)
723minimumBy cmp m = go Nothing (toStream m)
751minimumBy cmp m = go Nothing (toStream m)
724 where
752 where
725 go Nothing m1 =
753 go Nothing m1 =
@@ -1076,54 +1104,42 @@ zipWithM f m1 m2 = fromStream $ go (toStream m1) (toStream m2)
1076-- Merging
1104-- Merging
1077------------------------------------------------------------------------------
1105------------------------------------------------------------------------------
1078
1106
1079−data MergeWithState l r o
1080− = MergeBoth l r
1081− | MergeWithLeft l r o
1082− | MergeWithRight l r o
1083−
1084{-# INLINE mergeByS #-}
1107{-# INLINE mergeByS #-}
1108+mergeByS
1109+ :: Monad m
1085−mergeByS :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
1110+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
1086−mergeByS cmp m1 m2 = go (MergeBoth m1 m2)
1111+mergeByS cmp = go
1087− where
1112+ where
1088− go (MergeBoth mx my) =
1089− Stream $ \st stp sng yld -> do
1113+ go mx my = Stream $ \st stp sng yld -> do
1090− let yield1 a ra =
1114+ let mergeWithY a ra =
1115+ let stop2 = unStream mx (rstState st) stp sng yld
1091− let single2 b = sng (cond a b)
1116+ single2 b = do
1117+ r <- cmp a b
1118+ case r of
1119+ GT -> yld b (go (a `cons` ra) nil)
1092− stop2 = yld a ra
1120+ _ -> yld a (go ra (b `cons` nil))
1093− yield2 b rb =
1121+ yield2 b rb = do
1094− case cmp a b of
1122+ r <- cmp a b
1123+ case r of
1095− LT -> yld a (go (MergeWithRight ra rb b))
1124+ GT -> yld b (go (a `cons` ra) rb)
1096− _ -> yld b (go (MergeWithLeft ra rb a))
1125+ _ -> yld a (go ra (b `cons` rb))
1097− in unStream my (rstState st) stop2 single2 yield2
1126+ in unStream my (rstState st) stop2 single2 yield2
1098− single1 a = sng a
1099− stop1 = unStream my (rstState st) stp sng yld
1127+ let stopX = unStream my (rstState st) stp sng yld
1100− unStream mx (rstState st) stop1 single1 yield1
1128+ singleX a = mergeWithY a nil
1101− go (MergeWithLeft mx my x) =
1129+ yieldX = mergeWithY
1102− Stream $ \st _ sng yld -> do
1130+ unStream mx (rstState st) stopX singleX yieldX
1103− let single2 b = sng (cond x b)
1131+
1104− stop2 = yld x mx
1132+{-# INLINABLE mergeByM #-}
1105− yield2 b rb =
1133+mergeByM
1106− case cmp x b of
1134+ :: (IsStream t, Monad m)
1107− LT -> yld x (go (MergeWithRight mx rb b))
1135+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
1108− _ -> yld b (go (MergeWithLeft mx rb x))
1136+mergeByM f m1 m2 = fromStream $ mergeByS f (toStream m1) (toStream m2)
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
1123
1137
1124{-# INLINABLE mergeBy #-}
1138{-# INLINABLE mergeBy #-}
1139+mergeBy
1140+ :: (IsStream t, Monad m)
1125−mergeBy :: IsStream t => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1141+ => (a -> a -> Ordering) -> t m a -> t m a -> t m a
1126−mergeBy f m1 m2 = fromStream $ mergeByS f (toStream m1) (toStream m2)
1142+mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
1127
1143
1128------------------------------------------------------------------------------
1144------------------------------------------------------------------------------
1129-- Transformation comprehensions
1145-- Transformation comprehensions
src/Streamly/Streams/Zip.hs+4−4
@@ -136,8 +136,8 @@ TRAVERSABLE_INSTANCE(ZipSerialM)
136-- Parallel Zipping
136-- Parallel Zipping
137------------------------------------------------------------------------------
137------------------------------------------------------------------------------
138
138
139−-- | Zip two streams concurrently (i.e. both the elements being zipped are
139+-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
140−-- generated concurrently) using a pure zipping function.
140+-- are generated concurrently.
141--
141--
142-- @since 0.1.0
142-- @since 0.1.0
143zipAsyncWith :: (IsStream t, MonadAsync m)
143zipAsyncWith :: (IsStream t, MonadAsync m)
@@ -147,8 +147,8 @@ zipAsyncWith f m1 m2 = fromStream $ Stream $ \st stp sng yld -> do
147 mb <- mkAsync' (rstState st) m2
147 mb <- mkAsync' (rstState st) m2
148 unStream (toStream (K.zipWith f ma mb)) (rstState st) stp sng yld
148 unStream (toStream (K.zipWith f ma mb)) (rstState st) stp sng yld
149
149
150−-- | Zip two streams asyncly (i.e. both the elements being zipped are generated
150+-- | Like 'zipWithM' but zips concurrently i.e. both the streams being zipped
151−-- concurrently) using a monadic zipping function.
151+-- are generated concurrently.
152--
152--
153-- @since 0.4.0
153-- @since 0.4.0
154zipAsyncWithM :: (IsStream t, MonadAsync m)
154zipAsyncWithM :: (IsStream t, MonadAsync m)
src/Streamly/Tutorial.hs+4−6
@@ -235,19 +235,17 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
235-- asynchronous consumption the outputs are consumed as they arrive i.e. first
235-- asynchronous consumption the outputs are consumed as they arrive i.e. first
236-- come first serve order.
236-- come first serve order.
237--
237--
238−-- @
239-- +------------+--------------+--------------+--------------+
238-- +------------+--------------+--------------+--------------+
240-- | Type | Execution | Consumption | Concurrency |
239-- | Type | Execution | Consumption | Concurrency |
241-- +============+==============+==============+==============+
240-- +============+==============+==============+==============+
242−-- | 'Serial' | Serial | Serial | None |
241+-- | 'Serial' | Serial | Serial | None |
243-- +------------+--------------+--------------+--------------+
242-- +------------+--------------+--------------+--------------+
244−-- | 'Ahead' | Asynchronous | Serial | bounded |
243+-- | 'Ahead' | Asynchronous | Serial | bounded |
245-- +------------+--------------+--------------+--------------+
244-- +------------+--------------+--------------+--------------+
246−-- | 'Async' | Asynchronous | Asynchronous | bounded |
245+-- | 'Async' | Asynchronous | Asynchronous | bounded |
247-- +------------+--------------+--------------+--------------+
246-- +------------+--------------+--------------+--------------+
248−-- | 'Parallel' | Asynchronous | Asynchronous | unbounded |
247+-- | 'Parallel' | Asynchronous | Asynchronous | unbounded |
249-- +------------+--------------+--------------+--------------+
248-- +------------+--------------+--------------+--------------+
250−-- @
251--
249--
252-- All these types can be freely inter-converted using type conversion
250-- All these types can be freely inter-converted using type conversion
253-- combinators or type annotations, without any cost, to achieve the desired
251-- combinators or type annotations, without any cost, to achieve the desired
test/Prop.hs+293−251
@@ -20,7 +20,8 @@ import Data.Maybe (mapMaybe)
20import GHC.Word (Word8)
20import GHC.Word (Word8)
21
21
22import Test.Hspec.QuickCheck
22import Test.Hspec.QuickCheck
23+import Test.QuickCheck
23−import Test.QuickCheck (counterexample, Property, withMaxSuccess)
24+ (counterexample, Property, withMaxSuccess, forAll, choose)
24import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
25import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
25
26
26import Test.Hspec as H
27import Test.Hspec as H
@@ -74,43 +75,86 @@ listEquals eq stream list = do
74 )
75 )
75 assert (stream `eq` list)
76 assert (stream `eq` list)
76
77
77−constructWithReplicateM
78+-------------------------------------------------------------------------------
78− :: IsStream t
79+-- Construction operations
80+-------------------------------------------------------------------------------
81+
82+constructWithLen
83+ :: (Show a, Eq a)
84+ => (Int -> t IO a)
85+ -> (Int -> [a])
79− => (t IO Int -> SerialT IO Int)
86+ -> (t IO a -> SerialT IO a)
80 -> Word8
87 -> Word8
81 -> Property
88 -> Property
82−constructWithReplicateM op len = withMaxSuccess maxTestCount $
89+constructWithLen mkStream mkList op len = withMaxSuccess maxTestCount $
83 monadicIO $ do
90 monadicIO $ do
84− let x = return (1 :: Int)
85− stream <- run $ (S.toList . op) (S.replicateM (fromIntegral len) x)
91+ stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
86− list <- run $ replicateM (fromIntegral len) x
92+ let list = mkList (fromIntegral len)
87 listEquals (==) stream list
93 listEquals (==) stream list
88
94
89−constructWithReplicate
95+constructWithLenM
90− :: IsStream t
96+ :: (Int -> t IO Int)
97+ -> (Int -> IO [Int])
91− => (t IO Int -> SerialT IO Int)
98+ -> (t IO Int -> SerialT IO Int)
92 -> Word8
99 -> Word8
93 -> Property
100 -> Property
94−constructWithReplicate op len = withMaxSuccess maxTestCount $
101+constructWithLenM mkStream mkList op len = withMaxSuccess maxTestCount $
95 monadicIO $ do
102 monadicIO $ do
96− let x = (1 :: Int)
97− stream <- run $ (S.toList . op) (S.replicate (fromIntegral len) x)
103+ stream <- run $ (S.toList . op) (mkStream (fromIntegral len))
98− let list = replicate (fromIntegral len) x
104+ list <- run $ mkList (fromIntegral len)
99 listEquals (==) stream list
105 listEquals (==) stream list
100
106
101−transformFromList
107+constructWithReplicate, constructWithReplicateM, constructWithIntFromThenTo
102− :: (Eq b, Show b) =>
108+ :: IsStream t
103− ([a] -> t IO a)
104− -> ([b] -> [b] -> Bool)
105− -> ([a] -> [b])
106− -> (t IO a -> SerialT IO b)
109+ => (t IO Int -> SerialT IO Int)
107− -> [a]
110+ -> Word8
108 -> Property
111 -> Property
109−transformFromList constr eq listOp op a =
112+
110− monadicIO $ do
113+constructWithReplicateM = constructWithLenM stream list
111− stream <- run ((S.toList . op) (constr a))
114+ where list = flip replicateM (return 1 :: IO Int)
112− let list = listOp a
115+ stream = flip S.replicateM (return 1 :: IO Int)
113− listEquals eq stream list
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+-------------------------------------------------------------------------------
114
158
115mvarExcHandler :: String -> BlockedIndefinitelyOnMVar -> IO ()
159mvarExcHandler :: String -> BlockedIndefinitelyOnMVar -> IO ()
116mvarExcHandler label BlockedIndefinitelyOnMVar =
160mvarExcHandler label BlockedIndefinitelyOnMVar =
@@ -213,6 +257,32 @@ concurrentUnfoldrM eq op n =
213 return x
257 return x
214 listEquals eq stream list
258 listEquals eq stream list
215
259
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+
216concurrentApplication :: IsStream t
286concurrentApplication :: IsStream t
217 => ([Word8] -> [Word8] -> Bool)
287 => ([Word8] -> [Word8] -> Bool)
218 -> (t IO Word8 -> SerialT IO Word8)
288 -> (t IO Word8 -> SerialT IO Word8)
@@ -269,6 +339,10 @@ concurrentFoldrApplication n =
269 sourceUnfoldrM1 n |&. S.foldrM (\x xs -> return (x : xs)) []
339 sourceUnfoldrM1 n |&. S.foldrM (\x xs -> return (x : xs)) []
270 listEquals (==) stream list
340 listEquals (==) stream list
271
341
342+-------------------------------------------------------------------------------
343+-- Transformation operations
344+-------------------------------------------------------------------------------
345+
272transformCombineFromList
346transformCombineFromList
273 :: Semigroup (t IO Int)
347 :: Semigroup (t IO Int)
274 => ([Int] -> t IO Int)
348 => ([Int] -> t IO Int)
@@ -288,143 +362,13 @@ transformCombineFromList constr eq listOp t op a b c =
288 let list = a <> listOp (b <> c)
362 let list = a <> listOp (b <> c)
289 listEquals eq stream list
363 listEquals eq stream list
290
364
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−
426-- XXX add tests for MonadReader and MonadError etc. In case an SVar is
365-- XXX add tests for MonadReader and MonadError etc. In case an SVar is
427-- accidentally passed through them.
366-- 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.
428transformCombineOpsCommon
372transformCombineOpsCommon
429 :: (IsStream t, Semigroup (t IO Int))
373 :: (IsStream t, Semigroup (t IO Int))
430 => ([Int] -> t IO Int)
374 => ([Int] -> t IO Int)
@@ -434,6 +378,7 @@ transformCombineOpsCommon
434 -> Spec
378 -> Spec
435transformCombineOpsCommon constr desc eq t = do
379transformCombineOpsCommon constr desc eq t = do
436 let transform = transformCombineFromList constr eq
380 let transform = transformCombineFromList constr eq
381+
437 -- Filtering
382 -- Filtering
438 prop (desc <> " filter False") $
383 prop (desc <> " filter False") $
439 transform (filter (const False)) t (S.filter (const False))
384 transform (filter (const False)) t (S.filter (const False))
@@ -477,6 +422,12 @@ transformCombineOpsCommon constr desc eq t = do
477 prop (desc <> " dropWhileM False") $
422 prop (desc <> " dropWhileM False") $
478 transform (dropWhile (const False)) t (S.dropWhileM (const $ return False))
423 transform (dropWhile (const False)) t (S.dropWhileM (const $ return False))
479
424
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
480 prop (desc <> " mapM (+1)") $
431 prop (desc <> " mapM (+1)") $
481 transform (fmap (+1)) t (S.mapM (\x -> return (x + 1)))
432 transform (fmap (+1)) t (S.mapM (\x -> return (x + 1)))
482
433
@@ -486,22 +437,34 @@ transformCombineOpsCommon constr desc eq t = do
486 (S.scanlM' (\_ a -> return a) 0)
437 (S.scanlM' (\_ a -> return a) 0)
487 prop (desc <> " scanl") $ transform (scanl' (flip const) 0) t
438 prop (desc <> " scanl") $ transform (scanl' (flip const) 0) t
488 (S.scanl' (flip const) 0)
439 (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))
495 prop (desc <> " scanl1'") $ transform (scanl1 (flip const)) t
440 prop (desc <> " scanl1'") $ transform (scanl1 (flip const)) t
496 (S.scanl1' (flip const))
441 (S.scanl1' (flip const))
497 prop (desc <> " scanl1M'") $ transform (scanl1 (flip const)) t
442 prop (desc <> " scanl1M'") $ transform (scanl1 (flip const)) t
498 (S.scanl1M' (\_ a -> return a))
443 (S.scanl1M' (\_ a -> return a))
499
444
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
500 prop (desc <> " reverse") $ transform reverse t S.reverse
449 prop (desc <> " reverse") $ transform reverse t S.reverse
501
450
451+ -- inserting
502 prop (desc <> " intersperseM") $
452 prop (desc <> " intersperseM") $
453+ forAll (choose (minBound, maxBound)) $ \n ->
503− transform (intersperse 3) t (S.intersperseM $ return 3)
454+ transform (intersperse n) t (S.intersperseM $ return n)
504−
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.
505transformCombineOpsOrdered
468transformCombineOpsOrdered
506 :: (IsStream t, Semigroup (t IO Int))
469 :: (IsStream t, Semigroup (t IO Int))
507 => ([Int] -> t IO Int)
470 => ([Int] -> t IO Int)
@@ -511,6 +474,7 @@ transformCombineOpsOrdered
511 -> Spec
474 -> Spec
512transformCombineOpsOrdered constr desc eq t = do
475transformCombineOpsOrdered constr desc eq t = do
513 let transform = transformCombineFromList constr eq
476 let transform = transformCombineFromList constr eq
477+
514 -- Filtering
478 -- Filtering
515 prop (desc <> " take 1") $ transform (take 1) t (S.take 1)
479 prop (desc <> " take 1") $ transform (take 1) t (S.take 1)
516#ifdef DEVBUILD
480#ifdef DEVBUILD
@@ -531,15 +495,36 @@ transformCombineOpsOrdered constr desc eq t = do
531 transform (dropWhile (> 0)) t (S.dropWhile (> 0))
495 transform (dropWhile (> 0)) t (S.dropWhile (> 0))
532 prop (desc <> " scan") $ transform (scanl' (+) 0) t (S.scanl' (+) 0)
496 prop (desc <> " scan") $ transform (scanl' (+) 0) t (S.scanl' (+) 0)
533
497
534− -- XXX this does not fail when the SVar is shared, need to fix.
498+ -- XXX add uniq
535− prop (desc <> " concurrent application") $
499+ prop (desc <> " deleteBy (<=) 0") $
536− transform (& fmap (+1)) t (|& S.map (+1))
500+ transform (deleteBy (<=) 0) t (S.deleteBy (<=) 0)
537
501
538 prop (desc <> " findIndices") $
502 prop (desc <> " findIndices") $
539 transform (findIndices odd) t (S.findIndices odd)
503 transform (findIndices odd) t (S.findIndices odd)
540 prop (desc <> " elemIndices") $
504 prop (desc <> " elemIndices") $
541 transform (elemIndices 0) t (S.elemIndices 0)
505 transform (elemIndices 0) t (S.elemIndices 0)
542
506
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+
543wrapMaybe :: ([a1] -> a2) -> [a1] -> Maybe a2
528wrapMaybe :: ([a1] -> a2) -> [a1] -> Maybe a2
544wrapMaybe f x = if null x then Nothing else Just (f x)
529wrapMaybe f x = if null x then Nothing else Just (f x)
545
530
@@ -577,31 +562,42 @@ eliminationOps constr desc t = do
577 prop (desc <> " sum") $ eliminateOp constr sum $ S.sum . t
562 prop (desc <> " sum") $ eliminateOp constr sum $ S.sum . t
578 prop (desc <> " product") $ eliminateOp constr product $ S.product . t
563 prop (desc <> " product") $ eliminateOp constr product $ S.product . t
579
564
565+ prop (desc <> " maximum") $
580− prop (desc <> " maximum") $ eliminateOp constr (wrapMaybe maximum) $ S.maximum . t
566+ eliminateOp constr (wrapMaybe maximum) $ S.maximum . t
567+ prop (desc <> " minimum") $
581− prop (desc <> " minimum") $ eliminateOp constr (wrapMaybe minimum) $ S.minimum . t
568+ eliminateOp constr (wrapMaybe minimum) $ S.minimum . t
582
569
583 prop (desc <> " maximumBy compare") $
570 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
585 prop (desc <> " maximumBy flip compare") $
573 prop (desc <> " maximumBy flip compare") $
586 eliminateOp constr (wrapMaybe $ maximumBy $ flip compare) $
574 eliminateOp constr (wrapMaybe $ maximumBy $ flip compare) $
587 S.maximumBy (flip compare) . t
575 S.maximumBy (flip compare) . t
588 prop (desc <> " minimumBy compare") $
576 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
590 prop (desc <> " minimumBy flip compare") $
579 prop (desc <> " minimumBy flip compare") $
591 eliminateOp constr (wrapMaybe $ minimumBy $ flip compare) $
580 eliminateOp constr (wrapMaybe $ minimumBy $ flip compare) $
592 S.minimumBy (flip compare) . t
581 S.minimumBy (flip compare) . t
593
582
583+ prop (desc <> " findIndex") $
594− prop (desc <> " findIndex") $ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
584+ eliminateOp constr (findIndex odd) $ S.findIndex odd . t
585+ prop (desc <> " elemIndex") $
595− prop (desc <> " elemIndex") $ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
586+ eliminateOp constr (elemIndex 3) $ S.elemIndex 3 . t
596
587
588+ prop (desc <> " !! 5") $
597− prop (desc <> " !! 5") $ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
589+ eliminateOp constr (wrapOutOfBounds (!!) 5) $ (S.!! 5) . t
590+ prop (desc <> " !! 4") $
598− prop (desc <> " !! 4") $ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
591+ eliminateOp constr (wrapOutOfBounds (!!) 0) $ (S.!! 0) . t
599
592
600 prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
593 prop (desc <> " find") $ eliminateOp constr (find even) $ S.find even . t
601 prop (desc <> " lookup") $
594 prop (desc <> " lookup") $
602 eliminateOp constr (lookup 3 . flip zip [1..]) $
595 eliminateOp constr (lookup 3 . flip zip [1..]) $
603 S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
596 S.lookup 3 . S.zipWith (\a b -> (b, a)) (S.fromList [(1::Int)..]) . t
597+ prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
604
598
599+ -- Multi-stream eliminations
600+ -- Add eqBy, cmpBy
605 -- XXX Write better tests for substreams.
601 -- XXX Write better tests for substreams.
606 prop (desc <> " isPrefixOf 10") $ eliminateOp constr (isPrefixOf [1..10]) $
602 prop (desc <> " isPrefixOf 10") $ eliminateOp constr (isPrefixOf [1..10]) $
607 S.isPrefixOf (S.fromList [(1::Int)..10]) . t
603 S.isPrefixOf (S.fromList [(1::Int)..10]) . t
@@ -612,16 +608,14 @@ eliminationOps constr desc t = do
612 (\s -> s >>= maybe (return Nothing) (fmap Just . S.toList)) .
608 (\s -> s >>= maybe (return Nothing) (fmap Just . S.toList)) .
613 S.stripPrefix (S.fromList [(1::Int)..10]) . t
609 S.stripPrefix (S.fromList [(1::Int)..10]) . t
614
610
615− prop (desc <> " the") $ eliminateOp constr wrapThe $ S.the . t
616−
617-- head/tail/last may depend on the order in case of parallel streams
611-- head/tail/last may depend on the order in case of parallel streams
618-- so we test these only for serial streams.
612-- so we test these only for serial streams.
619−serialEliminationOps
613+eliminationOpsOrdered
620 :: ([Int] -> t IO Int)
614 :: ([Int] -> t IO Int)
621 -> String
615 -> String
622 -> (t IO Int -> SerialT IO Int)
616 -> (t IO Int -> SerialT IO Int)
623 -> Spec
617 -> Spec
624−serialEliminationOps constr desc t = do
618+eliminationOpsOrdered constr desc t = do
625 prop (desc <> " head") $ eliminateOp constr (wrapMaybe head) $ S.head . t
619 prop (desc <> " head") $ eliminateOp constr (wrapMaybe head) $ S.head . t
626 prop (desc <> " tail") $ eliminateOp constr (wrapMaybe tail) $ \x -> do
620 prop (desc <> " tail") $ eliminateOp constr (wrapMaybe tail) $ \x -> do
627 r <- S.tail (t x)
621 r <- S.tail (t x)
@@ -635,14 +629,53 @@ serialEliminationOps constr desc t = do
635 Nothing -> return Nothing
629 Nothing -> return Nothing
636 Just s -> Just <$> S.toList s
630 Just s -> Just <$> S.toList s
637
631
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
639 :: ([Word8] -> t IO Word8)
646 :: ([Word8] -> t IO Word8)
640 -> String
647 -> String
641 -> (t IO Word8 -> SerialT IO Word8)
648 -> (t IO Word8 -> SerialT IO Word8)
642 -> Spec
649 -> Spec
643−transformOpsWord8 constr desc t = do
650+eliminationOpsWord8 constr desc t = do
644 prop (desc <> " elem") $ elemOp constr t S.elem elem
651 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
646
679
647-- XXX concatenate streams of multiple elements rather than single elements
680-- XXX concatenate streams of multiple elements rather than single elements
648semigroupOps
681semigroupOps
@@ -660,6 +693,25 @@ semigroupOps desc eq t = do
660 prop (desc <> " <>") $ foldFromList (foldMapWith (<>) singleton) t eq
693 prop (desc <> " <>") $ foldFromList (foldMapWith (<>) singleton) t eq
661 prop (desc <> " mappend") $ foldFromList (foldMapWith mappend singleton) t eq
694 prop (desc <> " mappend") $ foldFromList (foldMapWith mappend singleton) t eq
662
695
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+
663applicativeOps
715applicativeOps
664 :: Applicative (t IO)
716 :: Applicative (t IO)
665 => ([Int] -> t IO Int)
717 => ([Int] -> t IO Int)
@@ -673,6 +725,10 @@ applicativeOps constr eq t (a, b) = withMaxSuccess maxTestCount $
673 let list = (,) <$> a <*> b
725 let list = (,) <$> a <*> b
674 listEquals eq stream list
726 listEquals eq stream list
675
727
728+-------------------------------------------------------------------------------
729+-- Zip operations
730+-------------------------------------------------------------------------------
731+
676zipApplicative
732zipApplicative
677 :: (IsStream t, Applicative (t IO))
733 :: (IsStream t, Applicative (t IO))
678 => ([Int] -> t IO Int)
734 => ([Int] -> t IO Int)
@@ -727,6 +783,10 @@ zipAsyncMonadic constr eq t (a, b) = withMaxSuccess maxTestCount $
727 listEquals eq stream1 list
783 listEquals eq stream1 list
728 listEquals eq stream2 list
784 listEquals eq stream2 list
729
785
786+-------------------------------------------------------------------------------
787+-- Monad operations
788+-------------------------------------------------------------------------------
789+
730monadThen
790monadThen
731 :: Monad (t IO)
791 :: Monad (t IO)
732 => ([Int] -> t IO Int)
792 => ([Int] -> t IO Int)
@@ -755,16 +815,6 @@ monadBind constr eq t (a, b) = withMaxSuccess maxTestCount $
755 let list = a >>= \x -> (+ x) <$> b
815 let list = a >>= \x -> (+ x) <$> b
756 listEquals eq stream list
816 listEquals eq stream list
757
817
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−
768main :: IO ()
818main :: IO ()
769main = hspec
819main = hspec
770 $ H.parallel
820 $ H.parallel
@@ -838,17 +888,24 @@ main = hspec
838 zipAsyncOps spec = mapOps spec $ makeOps zipAsyncly
888 zipAsyncOps spec = mapOps spec $ makeOps zipAsyncly
839
889
840 describe "Construction" $ do
890 describe "Construction" $ do
891+ serialOps $ prop "serially replicate" . constructWithReplicate
892+
841 serialOps $ prop "serially replicateM" . constructWithReplicateM
893 serialOps $ prop "serially replicateM" . constructWithReplicateM
842 wSerialOps $ prop "wSerially replicateM" . constructWithReplicateM
894 wSerialOps $ prop "wSerially replicateM" . constructWithReplicateM
843 aheadOps $ prop "aheadly replicateM" . constructWithReplicateM
895 aheadOps $ prop "aheadly replicateM" . constructWithReplicateM
844 asyncOps $ prop "asyncly replicateM" . constructWithReplicateM
896 asyncOps $ prop "asyncly replicateM" . constructWithReplicateM
845 wAsyncOps $ prop "wAsyncly replicateM" . constructWithReplicateM
897 wAsyncOps $ prop "wAsyncly replicateM" . constructWithReplicateM
846 parallelOps $ prop "parallely replicateM" . constructWithReplicateM
898 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
847 -- XXX test for all types of streams
906 -- XXX test for all types of streams
848 constructWithIterate serially
907 constructWithIterate serially
849−
908+ -- XXX add tests for fromIndices
850− describe "Construction with replicate" $ do
851− serialOps $ prop "serially replicate" . constructWithReplicate
852
909
853 describe "Functor operations" $ do
910 describe "Functor operations" $ do
854 serialOps $ functorOps S.fromFoldable "serially" (==)
911 serialOps $ functorOps S.fromFoldable "serially" (==)
@@ -894,6 +951,7 @@ main = hspec
894 wAsyncOps $ prop "wAsyncly applicative folded" . applicativeOps folded sortEq
951 wAsyncOps $ prop "wAsyncly applicative folded" . applicativeOps folded sortEq
895 parallelOps $ prop "parallely applicative folded" . applicativeOps folded sortEq
952 parallelOps $ prop "parallely applicative folded" . applicativeOps folded sortEq
896
953
954+ -- XXX add tests for indexed/indexedR
897 describe "Zip operations" $ do
955 describe "Zip operations" $ do
898 zipSerialOps $ prop "zipSerially applicative" . zipApplicative S.fromFoldable (==)
956 zipSerialOps $ prop "zipSerially applicative" . zipApplicative S.fromFoldable (==)
899 zipSerialOps $ prop "zipSerially applicative folded" . zipApplicative folded (==)
957 zipSerialOps $ prop "zipSerially applicative folded" . zipApplicative folded (==)
@@ -916,6 +974,11 @@ main = hspec
916 parallelOps $ prop "zip monadic parallely" . zipMonadic S.fromFoldable (==)
974 parallelOps $ prop "zip monadic parallely" . zipMonadic S.fromFoldable (==)
917 parallelOps $ prop "zip monadic parallely folded" . zipMonadic folded (==)
975 parallelOps $ prop "zip monadic parallely folded" . zipMonadic folded (==)
918
976
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+
919 describe "Monad operations" $ do
982 describe "Monad operations" $ do
920 serialOps $ prop "serially monad then" . monadThen S.fromFoldable (==)
983 serialOps $ prop "serially monad then" . monadThen S.fromFoldable (==)
921 wSerialOps $ prop "wSerially monad then" . monadThen S.fromFoldable sortEq
984 wSerialOps $ prop "wSerially monad then" . monadThen S.fromFoldable sortEq
@@ -945,43 +1008,6 @@ main = hspec
945 wAsyncOps $ prop "wAsyncly monad bind folded" . monadBind folded sortEq
1008 wAsyncOps $ prop "wAsyncly monad bind folded" . monadBind folded sortEq
946 parallelOps $ prop "parallely monad bind folded" . monadBind folded sortEq
1009 parallelOps $ prop "parallely monad bind folded" . monadBind folded sortEq
947
1010
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−
985 -- These tests won't work with maxBuffer or maxThreads set to 1, so we
1011 -- These tests won't work with maxBuffer or maxThreads set to 1, so we
986 -- exclude those cases from these.
1012 -- exclude those cases from these.
987 let mkOps t =
1013 let mkOps t =
@@ -1017,8 +1043,6 @@ main = hspec
1017 prop "concurrent foldl application" $ withMaxSuccess maxTestCount
1043 prop "concurrent foldl application" $ withMaxSuccess maxTestCount
1018 concurrentFoldlApplication
1044 concurrentFoldlApplication
1019
1045
1020− -- These tests are specifically targeted towards detecting illegal sharing
1021− -- of SVar across conurrent streams. All transform ops must be added here.
1022 describe "Stream transform and combine operations" $ do
1046 describe "Stream transform and combine operations" $ do
1023 serialOps $ transformCombineOpsCommon S.fromFoldable "serially" (==)
1047 serialOps $ transformCombineOpsCommon S.fromFoldable "serially" (==)
1024 wSerialOps $ transformCombineOpsCommon S.fromFoldable "wSerially" sortEq
1048 wSerialOps $ transformCombineOpsCommon S.fromFoldable "wSerially" sortEq
@@ -1067,17 +1091,35 @@ main = hspec
1067 zipSerialOps $ eliminationOps folded "zipSerially folded"
1091 zipSerialOps $ eliminationOps folded "zipSerially folded"
1068 zipAsyncOps $ eliminationOps folded "zipAsyncly folded"
1092 zipAsyncOps $ eliminationOps folded "zipAsyncly folded"
1069
1093
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+
1070 -- XXX Add a test where we chain all transformation APIs and make sure that
1112 -- XXX Add a test where we chain all transformation APIs and make sure that
1071 -- the state is being passed through all of them.
1113 -- the state is being passed through all of them.
1072 describe "Stream serial elimination operations" $ do
1114 describe "Stream serial elimination operations" $ do
1073− serialOps $ serialEliminationOps S.fromFoldable "serially"
1115+ serialOps $ eliminationOpsOrdered S.fromFoldable "serially"
1074− wSerialOps $ serialEliminationOps S.fromFoldable "wSerially"
1116+ wSerialOps $ eliminationOpsOrdered S.fromFoldable "wSerially"
1075− aheadOps $ serialEliminationOps S.fromFoldable "aheadly"
1117+ aheadOps $ eliminationOpsOrdered S.fromFoldable "aheadly"
1076− zipSerialOps $ serialEliminationOps S.fromFoldable "zipSerially"
1118+ zipSerialOps $ eliminationOpsOrdered S.fromFoldable "zipSerially"
1077− zipAsyncOps $ serialEliminationOps S.fromFoldable "zipAsyncly"
1119+ zipAsyncOps $ eliminationOpsOrdered S.fromFoldable "zipAsyncly"
1078−
1120+
1079− serialOps $ serialEliminationOps folded "serially folded"
1121+ serialOps $ eliminationOpsOrdered folded "serially folded"
1080− wSerialOps $ serialEliminationOps folded "wSerially folded"
1122+ wSerialOps $ eliminationOpsOrdered folded "wSerially folded"
1081− aheadOps $ serialEliminationOps folded "aheadly folded"
1123+ aheadOps $ eliminationOpsOrdered folded "aheadly folded"
1082− zipSerialOps $ serialEliminationOps folded "zipSerially folded"
1124+ zipSerialOps $ eliminationOpsOrdered folded "zipSerially folded"
1083− zipAsyncOps $ serialEliminationOps folded "zipAsyncly folded"
1125+ zipAsyncOps $ eliminationOpsOrdered folded "zipAsyncly folded"