on master help sign in
Fix right fold, document folds, add generic folds
0ce4a6dHarendra Kumar
Changelog.md+20
@@ -1,3 +1,23 @@
1+## Unreleased
2+
3+### Breaking changes
4+
5+* Change the signature of `foldrM` to ensure that it is lazy
6+
7+### Enhancements
8+
9+* Add `foldrS` to fold a stream to a stream
10+* Add `foldrT` to fold a stream to an arbitrary transformer monad
11+* Add `toRevList` to fold a stream to a reverse list
12+
13+### Deprecations
14+
15+* Deprecate `foldr` and `foldr1`. Given the signature of `foldr`, its behavior
16+ necessarily depends on the strictness or laziness of the underlying monad.
17+ Therefore, it cannot be lazy enough in a strict monad like IO. For that
18+ reason we no longer support this version of right fold. Please use `foldrM`
19+ instead.
20+
1## 0.6.1
21## 0.6.1
2
22
3### Bug Fixes
23### Bug Fixes
benchmark/BaseStreams.hs+18−14
@@ -52,14 +52,15 @@ main =
52 -- , benchIO "fromFoldableM" D.sourceFromFoldableM
52 -- , benchIO "fromFoldableM" D.sourceFromFoldableM
53 ]
53 ]
54 , bgroup "elimination"
54 , bgroup "elimination"
55− [ benchIO "toNull" D.toNull D.sourceUnfoldrM
55+ [ benchIO "toNull" D.toNull D.sourceUnfoldrM
56+ , benchIO "mapM_" D.mapM_ D.sourceUnfoldrM
56− , benchIO "uncons" D.uncons D.sourceUnfoldrM
57+ , benchIO "uncons" D.uncons D.sourceUnfoldrM
57− , benchFold "tail" D.tail D.sourceUnfoldrM
58+ , benchFold "tail" D.tail D.sourceUnfoldrM
58 , benchIO "nullTail" D.nullTail D.sourceUnfoldrM
59 , benchIO "nullTail" D.nullTail D.sourceUnfoldrM
59 , benchIO "headTail" D.headTail D.sourceUnfoldrM
60 , benchIO "headTail" D.headTail D.sourceUnfoldrM
60− , benchFold "toList" K.toList K.sourceUnfoldrM
61+ , benchFold "toList" D.toList D.sourceUnfoldrM
61− , benchFold "fold" K.foldl K.sourceUnfoldrM
62+ , benchFold "foldl'" D.foldl D.sourceUnfoldrM
62− , benchFold "last" K.last K.sourceUnfoldrM
63+ , benchFold "last" D.last D.sourceUnfoldrM
63 ]
64 ]
64 , bgroup "transformation"
65 , bgroup "transformation"
65 [ benchIO "scan" (D.scan 1) D.sourceUnfoldrM
66 [ benchIO "scan" (D.scan 1) D.sourceUnfoldrM
@@ -68,6 +69,8 @@ main =
68 , benchIO "mapM" (D.mapM 1) D.sourceUnfoldrM
69 , benchIO "mapM" (D.mapM 1) D.sourceUnfoldrM
69 , benchIO "mapMaybe" (D.mapMaybe 1) D.sourceUnfoldrM
70 , benchIO "mapMaybe" (D.mapMaybe 1) D.sourceUnfoldrM
70 , benchIO "mapMaybeM" (D.mapMaybeM 1) D.sourceUnfoldrM
71 , benchIO "mapMaybeM" (D.mapMaybeM 1) D.sourceUnfoldrM
72+ -- This has horrible performance, never finishes
73+ -- , benchIO "foldlS" (D.foldlS 1) D.sourceUnfoldrM
71 ]
74 ]
72 , bgroup "transformationX4"
75 , bgroup "transformationX4"
73 [ benchIO "scan" (D.scan 4) D.sourceUnfoldrM
76 [ benchIO "scan" (D.scan 4) D.sourceUnfoldrM
@@ -164,16 +167,16 @@ main =
164 , benchIO "foldMapWithM" K.toNull K.sourceFoldMapWithM
167 , benchIO "foldMapWithM" K.toNull K.sourceFoldMapWithM
165 ]
168 ]
166 , bgroup "elimination"
169 , bgroup "elimination"
167− [ benchIO "toNull" K.toNull K.sourceUnfoldrM
170+ [ benchIO "toNull" K.toNull K.sourceUnfoldrM
168− , benchIO "mapM_" K.mapM_ K.sourceUnfoldrM
171+ , benchIO "mapM_" K.mapM_ K.sourceUnfoldrM
169− , benchIO "uncons" K.uncons K.sourceUnfoldrM
172+ , benchIO "uncons" K.uncons K.sourceUnfoldrM
170− , benchFold "init" K.init K.sourceUnfoldrM
173+ , benchFold "init" K.init K.sourceUnfoldrM
171− , benchFold "tail" K.tail K.sourceUnfoldrM
174+ , benchFold "tail" K.tail K.sourceUnfoldrM
172 , benchIO "nullTail" K.nullTail K.sourceUnfoldrM
175 , benchIO "nullTail" K.nullTail K.sourceUnfoldrM
173 , benchIO "headTail" K.headTail K.sourceUnfoldrM
176 , benchIO "headTail" K.headTail K.sourceUnfoldrM
174− , benchFold "toList" K.toList K.sourceUnfoldrM
177+ , benchFold "toList" K.toList K.sourceUnfoldrM
175− , benchFold "fold" K.foldl K.sourceUnfoldrM
178+ , benchFold "foldl'" K.foldl K.sourceUnfoldrM
176− , benchFold "last" K.last K.sourceUnfoldrM
179+ , benchFold "last" K.last K.sourceUnfoldrM
177 ]
180 ]
178 , bgroup "transformation"
181 , bgroup "transformation"
179 [ benchIO "scan" (K.scan 1) K.sourceUnfoldrM
182 [ benchIO "scan" (K.scan 1) K.sourceUnfoldrM
@@ -181,6 +184,7 @@ main =
181 , benchIO "fmap" (K.fmap 1) K.sourceUnfoldrM
184 , benchIO "fmap" (K.fmap 1) K.sourceUnfoldrM
182 , benchIO "mapM" (K.mapM 1) K.sourceUnfoldrM
185 , benchIO "mapM" (K.mapM 1) K.sourceUnfoldrM
183 -- , benchIO "concat" K.concat K.sourceUnfoldrM
186 -- , benchIO "concat" K.concat K.sourceUnfoldrM
187+ , benchIO "foldlS" (K.foldlS 1) K.sourceUnfoldrM
184 ]
188 ]
185 , bgroup "transformationX4"
189 , bgroup "transformationX4"
186 [ benchIO "scan" (K.scan 4) K.sourceUnfoldrM
190 [ benchIO "scan" (K.scan 4) K.sourceUnfoldrM
benchmark/Linear.hs+42−26
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1-- |
2-- |
2-- Module : Main
3-- Module : Main
3-- Copyright : (c) 2018 Harendra Kumar
4-- Copyright : (c) 2018 Harendra Kumar
@@ -5,7 +6,7 @@
5-- License : BSD3
6-- License : BSD3
6-- Maintainer : harendra.kumar@gmail.com
7-- Maintainer : harendra.kumar@gmail.com
7
8
8−import Control.DeepSeq (NFData)
9+import Control.DeepSeq (NFData(..))
9import Data.Functor.Identity (Identity, runIdentity)
10import Data.Functor.Identity (Identity, runIdentity)
10import System.Random (randomRIO)
11import System.Random (randomRIO)
11
12
@@ -16,6 +17,10 @@ import Streamly
16import qualified Streamly.Prelude as S
17import qualified Streamly.Prelude as S
17import Gauge
18import Gauge
18
19
20+-------------------------------------------------------------------------------
21+--
22+-------------------------------------------------------------------------------
23+
19-- We need a monadic bind here to make sure that the function f does not get
24-- We need a monadic bind here to make sure that the function f does not get
20-- completely optimized out by the compiler in some cases.
25-- completely optimized out by the compiler in some cases.
21
26
@@ -108,25 +113,15 @@ main =
108 , benchIOSrc serially "foldMapM" Ops.sourceFoldMapM
113 , benchIOSrc serially "foldMapM" Ops.sourceFoldMapM
109 ]
114 ]
110 , bgroup "elimination"
115 , bgroup "elimination"
111− [ benchIOSink "toNull" $ Ops.toNull serially
112− , benchIOSink "uncons" Ops.uncons
113− , benchIOSink "init" Ops.init
114− , benchIOSink "tail" Ops.tail
115− , benchIOSink "nullHeadTail" Ops.nullHeadTail
116− , benchIOSink "mapM_" Ops.mapM_
117− , benchIOSink "toList" Ops.toList
118−
119− , bgroup "reduce"
116+ [ bgroup "reduce"
120 [ bgroup "IO"
117 [ bgroup "IO"
121− [ benchIOSink "foldr" Ops.foldrReduce
118+ [ benchIOSink "foldrM" Ops.foldrMReduce
122− , benchIOSink "foldr1" Ops.foldr1Reduce
123 , benchIOSink "foldl'" Ops.foldl'Reduce
119 , benchIOSink "foldl'" Ops.foldl'Reduce
124 , benchIOSink "foldl1'" Ops.foldl1'Reduce
120 , benchIOSink "foldl1'" Ops.foldl1'Reduce
125 , benchIOSink "foldlM'" Ops.foldlM'Reduce
121 , benchIOSink "foldlM'" Ops.foldlM'Reduce
126 ]
122 ]
127 , bgroup "Identity"
123 , bgroup "Identity"
128− [ benchIdentitySink "foldr" Ops.foldrReduce
124+ [ benchIdentitySink "foldrM" Ops.foldrMReduce
129− , benchIdentitySink "foldr1" Ops.foldr1Reduce
130 , benchIdentitySink "foldl'" Ops.foldl'Reduce
125 , benchIdentitySink "foldl'" Ops.foldl'Reduce
131 , benchIdentitySink "foldl1'" Ops.foldl1'Reduce
126 , benchIdentitySink "foldl1'" Ops.foldl1'Reduce
132 , benchIdentitySink "foldlM'" Ops.foldlM'Reduce
127 , benchIdentitySink "foldlM'" Ops.foldlM'Reduce
@@ -135,36 +130,52 @@ main =
135
130
136 , bgroup "build"
131 , bgroup "build"
137 [ bgroup "IO"
132 [ bgroup "IO"
138− [ benchIOSink "foldr" Ops.foldrBuild
139− , benchIOSink "foldrM" Ops.foldrMBuild
133+ [ benchIOSink "foldrM" Ops.foldrMBuild
140 , benchIOSink "foldl'" Ops.foldl'Build
134 , benchIOSink "foldl'" Ops.foldl'Build
141 , benchIOSink "foldlM'" Ops.foldlM'Build
135 , benchIOSink "foldlM'" Ops.foldlM'Build
142 ]
136 ]
143 , bgroup "Identity"
137 , bgroup "Identity"
144− [ benchIdentitySink "foldr" Ops.foldrBuild
145− , benchIdentitySink "foldrM" Ops.foldrMBuild
138+ [ benchIdentitySink "foldrM" Ops.foldrMBuild
146 , benchIdentitySink "foldl'" Ops.foldl'Build
139 , benchIdentitySink "foldl'" Ops.foldl'Build
147 , benchIdentitySink "foldlM'" Ops.foldlM'Build
140 , benchIdentitySink "foldlM'" Ops.foldlM'Build
148 ]
141 ]
149 ]
142 ]
143+ , benchIOSink "uncons" Ops.uncons
144+ , benchIOSink "toNull" $ Ops.toNull serially
145+ , benchIOSink "mapM_" Ops.mapM_
150
146
147+ , benchIOSink "init" Ops.init
148+ , benchIOSink "tail" Ops.tail
149+ , benchIOSink "nullHeadTail" Ops.nullHeadTail
150+
151+ -- this is too low and causes all benchmarks reported in ns
152+ -- , benchIOSink "head" Ops.head
151 , benchIOSink "last" Ops.last
153 , benchIOSink "last" Ops.last
154+ -- , benchIOSink "lookup" Ops.lookup
155+ , benchIOSink "find" Ops.find
156+ , benchIOSink "findIndex" Ops.findIndex
157+ , benchIOSink "elemIndex" Ops.elemIndex
158+
159+ -- this is too low and causes all benchmarks reported in ns
152− , benchIOSink "length" Ops.length
160+ -- , benchIOSink "null" Ops.null
153 , benchIOSink "elem" Ops.elem
161 , benchIOSink "elem" Ops.elem
154 , benchIOSink "notElem" Ops.notElem
162 , benchIOSink "notElem" Ops.notElem
155 , benchIOSink "all" Ops.all
163 , benchIOSink "all" Ops.all
156 , benchIOSink "any" Ops.any
164 , benchIOSink "any" Ops.any
157 , benchIOSink "and" Ops.and
165 , benchIOSink "and" Ops.and
158 , benchIOSink "or" Ops.or
166 , benchIOSink "or" Ops.or
159− , benchIOSink "find" Ops.find
167+
160− , benchIOSink "findIndex" Ops.findIndex
168+ , benchIOSink "length" Ops.length
161− , benchIOSink "elemIndex" Ops.elemIndex
162− , benchIOSink "maximum" Ops.maximum
163− , benchIOSink "maximumBy" Ops.maximumBy
164− , benchIOSink "minimum" Ops.minimum
165− , benchIOSink "minimumBy" Ops.minimumBy
166 , benchIOSink "sum" Ops.sum
169 , benchIOSink "sum" Ops.sum
167 , benchIOSink "product" Ops.product
170 , benchIOSink "product" Ops.product
171+
172+ , benchIOSink "maximumBy" Ops.maximumBy
173+ , benchIOSink "maximum" Ops.maximum
174+ , benchIOSink "minimumBy" Ops.minimumBy
175+ , benchIOSink "minimum" Ops.minimum
176+
177+ , benchIOSink "toList" Ops.toList
178+ , benchIOSink "toRevList" Ops.toRevList
168 ]
179 ]
169 , bgroup "transformation"
180 , bgroup "transformation"
170 [ benchIOSink "scan" (Ops.scan 1)
181 [ benchIOSink "scan" (Ops.scan 1)
@@ -178,6 +189,11 @@ main =
178 Ops.sequence serially (Ops.sourceUnfoldrMAction n)
189 Ops.sequence serially (Ops.sourceUnfoldrMAction n)
179 , benchIOSink "findIndices" (Ops.findIndices 1)
190 , benchIOSink "findIndices" (Ops.findIndices 1)
180 , benchIOSink "elemIndices" (Ops.elemIndices 1)
191 , benchIOSink "elemIndices" (Ops.elemIndices 1)
192+ , benchIOSink "reverse" (Ops.reverse 1)
193+ , benchIOSink "foldrS" (Ops.foldrS 1)
194+ , benchIOSink "foldrSMap" (Ops.foldrSMap 1)
195+ , benchIOSink "foldrT" (Ops.foldrT 1)
196+ , benchIOSink "foldrTMap" (Ops.foldrTMap 1)
181 ]
197 ]
182 , bgroup "transformationX4"
198 , bgroup "transformationX4"
183 [ benchIOSink "scan" (Ops.scan 4)
199 [ benchIOSink "scan" (Ops.scan 4)
benchmark/LinearOps.hs+27−12
@@ -165,6 +165,7 @@ runStream = S.runStream
165{-# INLINE toList #-}
165{-# INLINE toList #-}
166toList :: Monad m => Stream m Int -> m [Int]
166toList :: Monad m => Stream m Int -> m [Int]
167
167
168+{-# INLINE head #-}
168{-# INLINE last #-}
169{-# INLINE last #-}
169{-# INLINE maximum #-}
170{-# INLINE maximum #-}
170{-# INLINE minimum #-}
171{-# INLINE minimum #-}
@@ -172,8 +173,7 @@ toList :: Monad m => Stream m Int -> m [Int]
172{-# INLINE findIndex #-}
173{-# INLINE findIndex #-}
173{-# INLINE elemIndex #-}
174{-# INLINE elemIndex #-}
174{-# INLINE foldl1'Reduce #-}
175{-# INLINE foldl1'Reduce #-}
175−{-# INLINE foldr1Reduce #-}
176−last, minimum, maximum, find, findIndex, elemIndex, foldl1'Reduce, foldr1Reduce
176+head, last, minimum, maximum, find, findIndex, elemIndex, foldl1'Reduce
177 :: Monad m => Stream m Int -> m (Maybe Int)
177 :: Monad m => Stream m Int -> m (Maybe Int)
178
178
179{-# INLINE minimumBy #-}
179{-# INLINE minimumBy #-}
@@ -182,19 +182,18 @@ minimumBy, maximumBy :: Monad m => Stream m Int -> m (Maybe Int)
182
182
183{-# INLINE foldl'Reduce #-}
183{-# INLINE foldl'Reduce #-}
184{-# INLINE foldlM'Reduce #-}
184{-# INLINE foldlM'Reduce #-}
185−{-# INLINE foldrReduce #-}
185+{-# INLINE foldrMReduce #-}
186{-# INLINE length #-}
186{-# INLINE length #-}
187{-# INLINE sum #-}
187{-# INLINE sum #-}
188{-# INLINE product #-}
188{-# INLINE product #-}
189−foldl'Reduce, foldlM'Reduce, foldrReduce, length, sum, product
189+foldl'Reduce, foldlM'Reduce, foldrMReduce, length, sum, product
190 :: Monad m
190 :: Monad m
191 => Stream m Int -> m Int
191 => Stream m Int -> m Int
192
192
193{-# INLINE foldl'Build #-}
193{-# INLINE foldl'Build #-}
194{-# INLINE foldlM'Build #-}
194{-# INLINE foldlM'Build #-}
195−{-# INLINE foldrBuild #-}
196{-# INLINE foldrMBuild #-}
195{-# INLINE foldrMBuild #-}
197−foldrBuild, foldrMBuild, foldl'Build, foldlM'Build
196+foldrMBuild, foldl'Build, foldlM'Build
198 :: Monad m
197 :: Monad m
199 => Stream m Int -> m [Int]
198 => Stream m Int -> m [Int]
200
199
@@ -202,9 +201,10 @@ foldrBuild, foldrMBuild, foldl'Build, foldlM'Build
202{-# INLINE any #-}
201{-# INLINE any #-}
203{-# INLINE and #-}
202{-# INLINE and #-}
204{-# INLINE or #-}
203{-# INLINE or #-}
204+{-# INLINE null #-}
205{-# INLINE elem #-}
205{-# INLINE elem #-}
206{-# INLINE notElem #-}
206{-# INLINE notElem #-}
207−elem, notElem, all, any, and, or :: Monad m => Stream m Int -> m Bool
207+null, elem, notElem, all, any, and, or :: Monad m => Stream m Int -> m Bool
208
208
209{-# INLINE toNull #-}
209{-# INLINE toNull #-}
210toNull :: Monad m => (t m a -> S.SerialT m a) -> t m a -> m ()
210toNull :: Monad m => (t m a -> S.SerialT m a) -> t m a -> m ()
@@ -240,18 +240,22 @@ mapM_ = S.mapM_ (\_ -> return ())
240
240
241toList = S.toList
241toList = S.toList
242
242
243+{-# INLINE toRevList #-}
244+toRevList :: Monad m => Stream m Int -> m [Int]
245+toRevList = S.toRevList
246+
247+foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
243foldl'Build = S.foldl' (flip (:)) []
248foldl'Build = S.foldl' (flip (:)) []
244−foldrBuild = S.foldr (:) []
245foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
249foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
246−foldrMBuild = S.foldrM (\x xs -> return $ x : xs) []
247
250
248−foldrReduce = S.foldr (+) 0
251+foldrMReduce = S.foldrM (\x xs -> xs >>= return . (x +)) (return 0)
249−foldr1Reduce = S.foldr1 (+)
250foldl'Reduce = S.foldl' (+) 0
252foldl'Reduce = S.foldl' (+) 0
251foldl1'Reduce = S.foldl1' (+)
253foldl1'Reduce = S.foldl1' (+)
252foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) 0
254foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) 0
253
255
254last = S.last
256last = S.last
257+null = S.null
258+head = S.head
255elem = S.elem maxValue
259elem = S.elem maxValue
256notElem = S.notElem maxValue
260notElem = S.notElem maxValue
257length = S.length
261length = S.length
@@ -323,10 +327,16 @@ composeN' n f =
323{-# INLINE elemIndices #-}
327{-# INLINE elemIndices #-}
324{-# INLINE insertBy #-}
328{-# INLINE insertBy #-}
325{-# INLINE deleteBy #-}
329{-# INLINE deleteBy #-}
330+{-# INLINE reverse #-}
331+{-# INLINE foldrS #-}
332+{-# INLINE foldrSMap #-}
333+{-# INLINE foldrT #-}
334+{-# INLINE foldrTMap #-}
326scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
335scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
327 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
336 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
328 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
337 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
329− findIndices, elemIndices, insertBy, deleteBy
338+ findIndices, elemIndices, insertBy, deleteBy, reverse,
339+ foldrS, foldrSMap, foldrT, foldrTMap
330 :: Monad m
340 :: Monad m
331 => Int -> Stream m Int -> m ()
341 => Int -> Stream m Int -> m ()
332
342
@@ -374,6 +384,11 @@ findIndices n = composeN n $ S.findIndices (== maxValue)
374elemIndices n = composeN n $ S.elemIndices maxValue
384elemIndices n = composeN n $ S.elemIndices maxValue
375insertBy n = composeN n $ S.insertBy compare maxValue
385insertBy n = composeN n $ S.insertBy compare maxValue
376deleteBy n = composeN n $ S.deleteBy (>=) maxValue
386deleteBy n = composeN n $ S.deleteBy (>=) maxValue
387+reverse n = composeN n $ S.reverse
388+foldrS n = composeN n $ S.foldrS S.cons S.nil
389+foldrSMap n = composeN n $ S.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
390+foldrT n = composeN n $ S.foldrT S.cons S.nil
391+foldrTMap n = composeN n $ S.foldrT (\x xs -> x + 1 `S.cons` xs) S.nil
377
392
378-------------------------------------------------------------------------------
393-------------------------------------------------------------------------------
379-- Iteration
394-- Iteration
benchmark/StreamDOps.hs+12−6
@@ -14,8 +14,8 @@ import Control.Monad (when)
14import Data.Maybe (isJust)
14import Data.Maybe (isJust)
15import Prelude
15import Prelude
16 (Monad, Int, (+), ($), (.), return, (>), even, (<=), div,
16 (Monad, Int, (+), ($), (.), return, (>), even, (<=), div,
17− subtract, undefined, Maybe(..), not, mapM_, (>>=),
17+ subtract, undefined, Maybe(..), not, (>>=),
18− maxBound, fmap, odd, (==))
18+ maxBound, fmap, odd, (==), flip)
19import qualified Prelude as P
19import qualified Prelude as P
20
20
21import qualified Streamly.Streams.StreamD as S
21import qualified Streamly.Streams.StreamD as S
@@ -77,6 +77,10 @@ source = sourceUnfoldrM
77runStream :: Monad m => Stream m a -> m ()
77runStream :: Monad m => Stream m a -> m ()
78runStream = S.runStream
78runStream = S.runStream
79
79
80+{-# INLINE mapM_ #-}
81+mapM_ :: Monad m => Stream m a -> m ()
82+mapM_ = S.mapM_ (\_ -> return ())
83+
80{-# INLINE toNull #-}
84{-# INLINE toNull #-}
81toNull :: Monad m => Stream m Int -> m ()
85toNull :: Monad m => Stream m Int -> m ()
82toNull = runStream
86toNull = runStream
@@ -96,15 +100,15 @@ uncons s = do
96
100
97{-# INLINE tail #-}
101{-# INLINE tail #-}
98tail :: Monad m => Stream m a -> m ()
102tail :: Monad m => Stream m a -> m ()
99−tail s = S.tail s >>= mapM_ tail
103+tail s = S.tail s >>= P.mapM_ tail
100
104
101nullTail s = do
105nullTail s = do
102 r <- S.null s
106 r <- S.null s
103− when (not r) $ S.tail s >>= mapM_ nullTail
107+ when (not r) $ S.tail s >>= P.mapM_ nullTail
104
108
105headTail s = do
109headTail s = do
106 h <- S.head s
110 h <- S.head s
107− when (isJust h) $ S.tail s >>= mapM_ headTail
111+ when (isJust h) $ S.tail s >>= P.mapM_ headTail
108
112
109{-# INLINE toList #-}
113{-# INLINE toList #-}
110toList :: Monad m => Stream m Int -> m [Int]
114toList :: Monad m => Stream m Int -> m [Int]
@@ -156,9 +160,10 @@ composeN n f =
156{-# INLINE dropWhileTrue #-}
160{-# INLINE dropWhileTrue #-}
157{-# INLINE dropWhileMTrue #-}
161{-# INLINE dropWhileMTrue #-}
158{-# INLINE dropWhileFalse #-}
162{-# INLINE dropWhileFalse #-}
163+{-# INLINE foldlS #-}
159scan, map, fmap, mapM, mapMaybe, mapMaybeM, filterEven, filterAllOut,
164scan, map, fmap, mapM, mapMaybe, mapMaybeM, filterEven, filterAllOut,
160 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
165 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
161− dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse
166+ dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse, foldlS
162 :: Monad m
167 :: Monad m
163 => Int -> Stream m Int -> m ()
168 => Int -> Stream m Int -> m ()
164
169
@@ -182,6 +187,7 @@ dropAll n = composeN n $ S.drop maxValue
182dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
187dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
183dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
188dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
184dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
189dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
190+foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
185
191
186-------------------------------------------------------------------------------
192-------------------------------------------------------------------------------
187-- Iteration
193-- Iteration
benchmark/StreamKOps.hs+4−2
@@ -15,7 +15,7 @@ import Data.Maybe (isJust)
15import Prelude
15import Prelude
16 (Monad, Int, (+), ($), (.), return, even, (>), (<=), div,
16 (Monad, Int, (+), ($), (.), return, even, (>), (<=), div,
17 subtract, undefined, Maybe(..), not, (>>=),
17 subtract, undefined, Maybe(..), not, (>>=),
18− maxBound)
18+ maxBound, flip)
19import qualified Prelude as P
19import qualified Prelude as P
20
20
21import qualified Streamly.Streams.StreamK as S
21import qualified Streamly.Streams.StreamK as S
@@ -182,9 +182,10 @@ composeN n f =
182{-# INLINE dropAll #-}
182{-# INLINE dropAll #-}
183{-# INLINE dropWhileTrue #-}
183{-# INLINE dropWhileTrue #-}
184{-# INLINE dropWhileFalse #-}
184{-# INLINE dropWhileFalse #-}
185+{-# INLINE foldlS #-}
185scan, map, fmap, filterEven, filterAllOut,
186scan, map, fmap, filterEven, filterAllOut,
186 filterAllIn, takeOne, takeAll, takeWhileTrue, dropAll, dropOne,
187 filterAllIn, takeOne, takeAll, takeWhileTrue, dropAll, dropOne,
187− dropWhileTrue, dropWhileFalse
188+ dropWhileTrue, dropWhileFalse, foldlS
188 :: Monad m
189 :: Monad m
189 => Int -> Stream m Int -> m ()
190 => Int -> Stream m Int -> m ()
190
191
@@ -205,6 +206,7 @@ dropOne n = composeN n $ S.drop 1
205dropAll n = composeN n $ S.drop maxValue
206dropAll n = composeN n $ S.drop maxValue
206dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
207dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
207dropWhileFalse n = composeN n $ S.dropWhile (<= 1)
208dropWhileFalse n = composeN n $ S.dropWhile (<= 1)
209+foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
208
210
209-------------------------------------------------------------------------------
211-------------------------------------------------------------------------------
210-- Iteration
212-- Iteration
src/Streamly/Prelude.hs+498−250
@@ -1,4 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE RankNTypes #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3
4
4#if __GLASGOW_HASKELL__ >= 800
5#if __GLASGOW_HASKELL__ >= 800
@@ -109,39 +110,49 @@ module Streamly.Prelude
109
110
110 -- * Elimination
111 -- * Elimination
111
112
112− -- ** Primitives
113+ -- ** Deconstruction
113 -- | It is easy to express all the folds in terms of the 'uncons' primitive,
114 -- | It is easy to express all the folds in terms of the 'uncons' primitive,
114 -- however the specific implementations provided later are generally more
115 -- however the specific implementations provided later are generally more
115− -- efficient. Folds are inherently serial as each step needs to use the
116+ -- efficient.
116− -- result of the previous step.
117+ --
117 , uncons
118 , uncons
119+ , tail
120+ , init
118
121
119− -- ** General Folds
122+ -- ** Folding
120−-- | Right and left folds.
123+-- | In imperative terms a fold can be considered as a loop over the stream
121−-- As a simple rule, always use lazy right fold for construction and strict
124+-- that reduces the stream to a single value.
122−-- left fold for reduction. By construction we mean using a constructor as the
125+-- Left and right folds use a fold function @f@ and an identity element @z@
123−-- outermost operation in the fold function, by reduction we mean using a
126+-- (@zero@) to recursively deconstruct a structure and then combine and reduce
124−-- function as the outermost operation in the fold function.
127+-- the values or transform and reconstruct a new container.
125−--
128+--
126−-- +-----------------------------------+--------------------------------------+
129+-- In general, a right fold is suitable for transforming and reconstructing a
127−-- | Right Fold | Left Fold |
130+-- right associated structure (e.g. cons lists and streamly streams) and a left
128−-- +===================================+======================================+
131+-- fold is suitable for reducing a right associated structure. The behavior of
129−-- | Construction consumes input | Construction consumes all input, |
132+-- right and left folds are described in detail in the individual fold's
130−-- | lazily and streams it in FIFO | and constructs in reverse (LIFO) |
131−-- | order | order |
132−-- +-----------------------------------+--------------------------------------+
133−-- | Reduction ends up buffering all | Strict reduction works |
134−-- | input before it can be reduced | incrementally, without buffering. |
135−-- +-----------------------------------+--------------------------------------+
136−--
137−-- Almost always, we need lazy construction and strict reduction, therefore,
138−-- strict @foldr@ and lazy @foldl@ are rarely useful. If needed, strict @foldr@
139−-- and lazy @foldl@ can be expressed in terms of the available versions. For
140−-- example, a lazy @foldl@ can be replaced by a strict @foldl@ to reverse the
141−-- structure followed by a @foldr@.
142−--
143−-- The following equations may help understand the relation between the two
144−-- folds for lists:
133+-- documentation. To illustrate the two folds for cons lists:
134+--
135+-- > foldr :: (a -> b -> b) -> b -> [a] -> b
136+-- > foldr f z [] = z
137+-- > foldr f z (x:xs) = x `f` foldr f z xs
138+-- >
139+-- > foldl :: (b -> a -> b) -> b -> [a] -> b
140+-- > foldl f z [] = z
141+-- > foldl f z (x:xs) = foldl f (z `f` x) xs
142+--
143+-- @foldr@ is conceptually equivalent to:
144+--
145+-- > foldr f z [] = z
146+-- > foldr f z [x] = f x z
147+-- > foldr f z xs = foldr f (foldr f z (tail xs)) [head xs]
148+--
149+-- @foldl@ is conceptually equivalent to:
150+--
151+-- > foldl f z [] = z
152+-- > foldl f z [x] = f z x
153+-- > foldl f z xs = foldl f (foldl f z (init xs)) [last xs]
154+--
155+-- Left and right folds are duals of each other.
145--
156--
146-- @
157-- @
147-- foldr f z xs = foldl (flip f) z (reverse xs)
158-- foldr f z xs = foldl (flip f) z (reverse xs)
@@ -151,42 +162,78 @@ module Streamly.Prelude
151-- More generally:
162-- More generally:
152--
163--
153-- @
164-- @
154−-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
155-- foldr f z xs = foldl g id xs z where g k x = k . f x
165-- foldr f z xs = foldl g id xs z where g k x = k . f x
166+-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
156-- @
167-- @
168+--
169+
170+-- As a general rule, foldr cannot have state and foldl cannot have control.
171+
172+-- NOTE: Folds are inherently serial as each step needs to use the result of
173+-- the previous step. However, it is possible to fold parts of the stream in
174+-- parallel and then combine the results using a monoid.
157
175
158− , foldr
159− , foldr1
160 , foldrM
176 , foldrM
177+ , foldrS
178+ , foldrT
179+
161 , foldl'
180 , foldl'
162 , foldl1'
181 , foldl1'
163 , foldlM'
182 , foldlM'
164 , foldx
183 , foldx
165 , foldxM
184 , foldxM
166
185
167− -- ** Run Effects
186+ -- ** Strict Full Folds
187+ -- | Folds that are guaranteed to evaluate the whole stream.
188+
189+ -- -- ** To Summary (Full Folds)
190+ -- -- | Folds that summarize the stream to a single value.
168 , runStream
191 , runStream
192+ , last
193+ , length
194+ , sum
195+ , product
196+
197+ -- -- ** To Summary (Maybe) (Full Folds)
198+ -- -- | Folds that summarize a non-empty stream to a 'Just' value and return
199+ -- 'Nothing' for an empty stream.
200+ , maximumBy
201+ , maximum
202+ , minimumBy
203+ , minimum
204+ , the
205+ , toRevList
206+
207+ -- ** Lazy Folds
208+ --
209+ -- | Folds that generate a lazy structure. Note that the generated
210+ -- structure may not be lazy if the underlying monad is strict.
211+
212+ -- -- ** To Containers (Full Folds)
213+ -- -- | Convert or divert a stream into an output structure, container or
214+ -- sink.
215+ , toList
216+ , toHandle
217+
218+ -- ** Partial Folds
219+ -- | Folds that may terminate before evaluating the whole stream. These
220+ -- folds strictly evaluate the stream until the result is determined.
221+
222+ -- -- ** To Elements (Partial Folds)
169 , runN
223 , runN
170 , runWhile
224 , runWhile
171
225
172− -- ** To Elements
173− -- | Folds that extract selected elements of a stream or their properties.
226+ -- -- | Folds that extract selected elements of a stream or their properties.
174 , (!!)
227 , (!!)
175 , head
228 , head
176− , last
177 , findM
229 , findM
178 , find
230 , find
179 , lookup
231 , lookup
180 , findIndex
232 , findIndex
181 , elemIndex
233 , elemIndex
182
234
183− -- ** To Parts
184− -- | Folds that extract selected parts of a stream.
185− , tail
186− , init
187−
188− -- ** To Boolean
235+ -- -- ** To Boolean (Partial Folds)
189− -- | Folds that summarize the stream to a boolean value.
236+ -- -- | Folds that summarize the stream to a boolean value.
190 , null
237 , null
191 , elem
238 , elem
192 , notElem
239 , notElem
@@ -195,46 +242,76 @@ module Streamly.Prelude
195 , and
242 , and
196 , or
243 , or
197
244
198− -- ** To Summary
199− -- | Folds that summarize the stream to a single value.
200− , length
201− , sum
202− , product
203−
204− -- ** To Summary (Maybe)
205− -- | Folds that summarize a non-empty stream to a 'Just' value and return
206− -- 'Nothing' for an empty stream.
207− , maximumBy
208− , maximum
209− , minimumBy
210− , minimum
211− , the
212−
213− -- ** To Containers
214− -- | Convert or divert a stream into an output structure, container or
215− -- sink.
216− , toList
217− , toHandle
218−
219 -- * Transformation
245 -- * Transformation
220
246
247+ -- ** Mapping
248+ -- | In imperative terms a map operation can be considered as a loop over
249+ -- the stream that transforms the stream into another stream by performing
250+ -- an operation on each element of the stream.
251+ --
252+ -- 'map' is the least powerful transformation operation with strictest
253+ -- guarantees. A map, (1) is a stateless loop which means that no state is
254+ -- allowed to be carried from one iteration to another, therefore,
255+ -- operations on different elements are guaranteed to not affect each
256+ -- other, (2) is a strictly one-to-one transformation of stream elements
257+ -- which means it guarantees that no elements can be added or removed from
258+ -- the stream, it can merely transform them.
259+ , Serial.map
260+ , sequence
261+ , mapM
262+ , mapM_
263+
221 -- ** Scanning
264 -- ** Scanning
222− -- | Scans stream all the intermediate reduction steps of the corresponding
265+ --
266+ -- | A scan is more powerful than map. While a 'map' is a stateless loop, a
267+ -- @scan@ is a stateful loop which means that a state can be shared across
268+ -- all the loop iterations, therefore, future iterations can be impacted by
269+ -- the state changes made by the past iterations. A scan yields the state
270+ -- of the loop after each iteration. Like a map, a @postscan@ or @prescan@
271+ -- does not add or remove elements in the stream, it just transforms them.
272+ -- However, a @scan@ adds one extra element to the stream.
273+ --
274+ -- A left associative scan, also known as a prefix sum, can be thought of
275+ -- as a stream transformation consisting of left folds of all prefixes of a
276+ -- stream. Another way of thinking about it is that it streams all the
277+ -- intermediate values of the accumulator while applying a left fold on the
278+ -- input stream. A right associative scan, on the other hand, can be
279+ -- thought of as a stream consisting of right folds of all the suffixes of
280+ -- a stream.
281+ --
223− -- folds. The following equations hold for lists:
282+ -- The following equations hold for lists:
224 --
283 --
225 -- > scanl f z xs == map (foldl f z) $ inits xs
284 -- > scanl f z xs == map (foldl f z) $ inits xs
226− -- > scanr f z xs == map (foldr f z) $ tails
285+ -- > scanr f z xs == map (foldr f z) $ tails xs
227 --
286 --
228− -- We do not provide a right associative scan, it can be recovered from a
287+ -- @
229− -- 'scanl'' as follows:
288+ -- > scanl (+) 0 [1,2,3,4]
289+ -- 0 = 0
290+ -- 0 + 1 = 1
291+ -- 0 + 1 + 2 = 3
292+ -- 0 + 1 + 2 + 3 = 6
293+ -- 0 + 1 + 2 + 3 + 4 = 10
230 --
294 --
231− -- > scanr f z xs == reverse $ scanl' (flip f) z (reverse xs)
295+ -- > scanr (+) 0 [1,2,3,4]
296+ -- 1 + 2 + 3 + 4 + 0 = 10
297+ -- 2 + 3 + 4 + 0 = 9
298+ -- 3 + 4 + 0 = 7
299+ -- 4 + 0 = 4
300+ -- 0 = 0
301+ -- @
232 --
302 --
233− -- Scan is like a stateful map. If we discard the state, we get the map:
303+ -- Left and right scans are duals:
234 --
304 --
305+ -- > scanr f z xs == reverse $ scanl (flip f) z (reverse xs)
306+ -- > scanl f z xs == reverse $ scanr (flip f) z (reverse xs)
307+ --
308+ -- A scan is a stateful map i.e. a combination of map and fold:
309+ --
235− -- > S.drop 1 $ S.scanl' (\_ x -> f x) z xs == map f xs
310+ -- > map f xs = tail $ scanl (\_ x -> f x) z xs
236−
311+ -- > map f xs = reverse $ head $ scanr (\_ x -> f x) z xs
237− -- > S.postscanl' (\_ x -> f x) z xs == map f xs
312+ --
313+ -- > foldl f z xs = last $ scanl f z xs
314+ -- > foldr f z xs = head $ scanr f z xs
238
315
239 , scanl'
316 , scanl'
240 , scanlM'
317 , scanlM'
@@ -246,20 +323,18 @@ module Streamly.Prelude
246 , scanl1M'
323 , scanl1M'
247 , scanx
324 , scanx
248
325
249− -- ** Mapping
326+ , indexed
250− -- | Map is a strictly one-to-one transformation of stream elements. It
327+ , indexedR
251− -- cannot add or remove elements from the stream, just transforms them.
252− , Serial.map
253−
254− -- ** Flattening
255− , sequence
256− , mapM
257
328
258 -- ** Filtering
329 -- ** Filtering
259− -- | Filtering may remove some elements from the stream.
330+ -- | Remove some elements from the stream based on a predicate. In
331+ -- imperative terms a filter over a stream corresponds to a loop with a
332+ -- @continue@ clause for the cases when the predicate fails.
260
333
261 , filter
334 , filter
262 , filterM
335 , filterM
336+
337+ -- ** Stateful Filters
263 , take
338 , take
264 , takeWhile
339 , takeWhile
265 , takeWhileM
340 , takeWhileM
@@ -269,6 +344,18 @@ module Streamly.Prelude
269 , deleteBy
344 , deleteBy
270 , uniq
345 , uniq
271
346
347+ -- ** Mapping Filters
348+ -- | Mapping along with filtering
349+
350+ , mapMaybe
351+ , mapMaybeM
352+
353+ -- ** Scanning Filters
354+ -- | Stateful transformation along with filtering
355+
356+ , findIndices
357+ , elemIndices
358+
272 -- ** Insertion
359 -- ** Insertion
273 -- | Insertion adds more elements to the stream.
360 -- | Insertion adds more elements to the stream.
274
361
@@ -278,17 +365,31 @@ module Streamly.Prelude
278 -- ** Reordering
365 -- ** Reordering
279 , reverse
366 , reverse
280
367
281− -- * Hybrid Operations
368+ -- ** Nested Producer Loops
282− -- ** Map and Fold
369+ -- | Map each element to a stream and then flatten the results into a
283− , mapM_
370+ -- single stream. In imperative terms, a 'concatMap' corresponds to nested
284−
371+ -- loops. We loop over the outer stream and then for each element of the
285− -- ** Map and Filter
372+ -- outer stream an inner stream is generated and then we loop over the
286− , mapMaybe
373+ -- inner stream as well to generate a single output stream.
287− , mapMaybeM
374+ --
375+ -- @
376+ --
377+ -- ----Stream m a----|-Stream m b-|-Stream m b-|-...-|----Stream m b
378+ --
379+ -- @
380+ --
381+ -- 'concatMap' can perform filtering by mapping an element to a 'nil'
382+ -- stream. Its a map, therefore it can degenerate to a simple map
383+ -- operation as well:
384+ --
385+ -- > filter p m = S.concatMap (\x -> if p x then S.yield x else S.nil) m
386+ -- > map f m = S.concatMap (\x -> S.yield (f x)) m
387+ --
388+ -- Though 'concatMap' is not inherently stateful, it can be combined with a
389+ -- scan to perform stateful operations.
288
390
289− -- ** Scan and filter
391+ , concatMapM
290− , findIndices
392+ , concatMap
291− , elemIndices
292
393
293 -- * Multi-Stream Operations
394 -- * Multi-Stream Operations
294 -- | New streams can be constructed by appending, merging or zipping
395 -- | New streams can be constructed by appending, merging or zipping
@@ -299,6 +400,12 @@ module Streamly.Prelude
299 -- operation.
400 -- operation.
300 --
401 --
301 -- @
402 -- @
403+ --
404+ -- -------Stream m a------|-------Stream m a------|=>----Stream m a---
405+ --
406+ -- @
407+ --
408+ -- @
302 -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
409 -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
303 -- [1,2,3,4]
410 -- [1,2,3,4]
304 -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
411 -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
@@ -308,6 +415,14 @@ module Streamly.Prelude
308 -- ** Merging
415 -- ** Merging
309 -- | Streams form a commutative semigroup under the merge
416 -- | Streams form a commutative semigroup under the merge
310 -- operation.
417 -- operation.
418+ --
419+ -- @
420+ --
421+ -- -------Stream m a------|
422+ -- |=>----Stream m a---
423+ -- -------Stream m a------|
424+ -- @
425+ --
311
426
312 -- , merge
427 -- , merge
313 , mergeBy
428 , mergeBy
@@ -316,20 +431,20 @@ module Streamly.Prelude
316 , mergeAsyncByM
431 , mergeAsyncByM
317
432
318 -- ** Zipping
433 -- ** Zipping
434+ -- |
435+ -- @
436+ --
437+ -- -------Stream m a------|
438+ -- |=>----Stream m c---
439+ -- -------Stream m b------|
440+ -- @
441+ --
319 , zipWith
442 , zipWith
320 , zipWithM
443 , zipWithM
321 , Z.zipAsyncWith
444 , Z.zipAsyncWith
322 , Z.zipAsyncWithM
445 , Z.zipAsyncWithM
323
446
324− -- Special zips
325− , indexed
326− , indexedR
327−
328− -- ** Flattening
329− , concatMapM
330− , concatMap
331−
332− -- ** Folds
447+ -- ** Stream Level Folds
333 , eqBy
448 , eqBy
334 , cmpBy
449 , cmpBy
335 , isPrefixOf
450 , isPrefixOf
@@ -340,12 +455,15 @@ module Streamly.Prelude
340 , K.once
455 , K.once
341 , each
456 , each
342 , scan
457 , scan
458+ , foldr
459+ , foldr1
343 , foldl
460 , foldl
344 , foldlM
461 , foldlM
345 )
462 )
346where
463where
347
464
348import Control.Monad.IO.Class (MonadIO(..))
465import Control.Monad.IO.Class (MonadIO(..))
466+import Control.Monad.Trans (MonadTrans(..))
349import Data.Maybe (isJust, fromJust)
467import Data.Maybe (isJust, fromJust)
350import Prelude
468import Prelude
351 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
469 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
@@ -388,6 +506,12 @@ import qualified Streamly.Streams.Serial as Serial
388-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
506-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
389-- the head of the stream and @ma@ its tail.
507-- the head of the stream and @ma@ its tail.
390--
508--
509+-- This is a brute force primitive. Avoid using it as long as possible, use it
510+-- when no other combinator can do the job. This can be used to do pretty much
511+-- anything in an imperative manner, as it just breaks down the stream into
512+-- individual elements and we can loop over them as we deem fit. For example,
513+-- this can be used to convert a streamly stream into other stream types.
514+--
391-- @since 0.1.0
515-- @since 0.1.0
392{-# INLINE uncons #-}
516{-# INLINE uncons #-}
393uncons :: (IsStream t, Monad m) => SerialT m a -> m (Maybe (a, t m a))
517uncons :: (IsStream t, Monad m) => SerialT m a -> m (Maybe (a, t m a))
@@ -720,92 +844,158 @@ fromHandle h = go
720-- Elimination by Folding
844-- Elimination by Folding
721------------------------------------------------------------------------------
845------------------------------------------------------------------------------
722
846
723−-- | Lazy right fold with a monadic step function. For example, to fold a
847+-- | Right associative/pull fold.
724−-- stream into a list:
725--
848--
726−-- @
849+-- Example, determine if any element is 'odd' in a stream:
727−-- >> S.foldrM (\\x xs -> return (x : xs)) [] $ fromList [1,2,3]
728−-- [1,2,3]
729−-- @
730--
850--
731−-- @since 0.2.0
851+-- >>> S.foldrM (\x xs -> if odd x then return True else xs) (return False) $ S.fromList (2:4:5:undefined)
732−{-# INLINE foldrM #-}
852+-- > True
733−foldrM :: Monad m => (a -> b -> m b) -> b -> SerialT m a -> m b
734−foldrM = P.foldrM
735−
736−-- | Lazy right associative fold.
737--
853--
738−-- For lists a @foldr@ looks like:
854+-- Let's take a closer look at the @foldr@ definition for lists, as given
855+-- earlier:
739--
856--
740-- @
857-- @
741−-- foldr f z [] = z
742-- foldr f z (x:xs) = x \`f` foldr f z xs
858-- foldr f z (x:xs) = x \`f` foldr f z xs
743-- @
859-- @
744--
860--
745−-- The recursive expression is the second argument of the fold step `f`.
861+-- @foldr@ invokes the fold step function @f@ as @f x (foldr f z xs)@. At each
746−-- Therefore, the evaluation of the recursive call depends on `f`. It can
862+-- invocation of @f@ @foldr@ gives us the next element in the input container
863+-- @x@ and a recursive expression @foldr f z xs@ representing the yet unbuilt
864+-- (lazy thunk) part of the output. Therefore, when @f x xs@ is lazy in @xs@
865+-- it can consume the input one element at a time in FIFO order to build a lazy
866+-- output expression. For example,
867+--
868+-- > f x remaining = show x : remaining
869+--
870+-- @take 2 $ foldr f [] (1:2:undefined)@ would consume the input lazily on
871+-- demand, consuming only first two elements and resulting in ["1", "2"]. @f@
747−-- terminate recursion by not inspecting the second argument based on a
872+-- can terminate recursion by not evaluating the @remaining@ part:
748−-- condition. When expanded fully, it results in the following right associated
873+--
874+-- > f 2 remaining = show 2 : []
875+-- > f x remaining = show x : remaining
876+--
877+-- @f@ would terminate recursion whenever it sees element @2@ in the input.
878+-- Therefore, @foldr f [] (1:2:undefined)@ would work as before. If @f a b@ is
879+-- strict in @b@ it would end up consuming the whole input right away and
880+-- expanding the recursive expression @b@ (i.e. @foldr f z xs@) fully before
881+-- it yields an output expression, resulting in the following /right
749−-- expression:
882+-- associated expression/:
750--
883--
751-- @
884-- @
752-- foldr f z xs == x1 \`f` (x2 \`f` ...(xn \`f` z))
885-- foldr f z xs == x1 \`f` (x2 \`f` ...(xn \`f` z))
753-- @
886-- @
754--
887--
755−-- When `f` is a constructor, we can see that the first deconstruction of this
756−-- expression would be @x1@ on the left and the recursive expression on the
757−-- right. Therefore, we can deconstruct it to access the input elements in the
758−-- first-in-first-out (FIFO) order and consume the reconstructed structure
759−-- lazily. The recursive expression on the right gets evaluated incrementall
760−-- as demanded by the consumer. For example:
888+-- For example,
761--
889--
762−-- @
890+-- > f x remaining = x + remaining
763−-- > S.foldr (:) [] $ S.fromList [1,2,3,4]
891+--
764−-- [1,2,3,4]
892+-- With this definition, @foldr f 0 [1..1000]@, would recurse completely until
765−-- @
893+-- it reaches the recursion terminating case @... `f` (1000 `f` 0)@, and then
894+-- start reducing the whole expression from right to left, therefore, consuming
895+-- the input elements in LIFO order. Thus, such an evaluation would require
896+-- memory proportional to the size of input. Try out @foldr (+) 0 (map (\\x ->
897+-- trace (show x) x) [1..10])@.
898+--
899+-- Notice, the order of the arguments to the step function @f a b@. It follows
900+-- the order of @a@ and @b@ in the right associative recursive expression
901+-- generated by expanding @a \`f` b@.
902+--
903+-- A right fold is a pull fold, the step function is the puller, it can pull
904+-- more data from the input container by using its second argument in the
905+-- output expression or terminate pulling by not using it. As a corollary:
906+--
907+-- 1. a step function which is lazy in its second argument (usually functions
908+-- or constructors that build a lazy structure e.g. @(:)@) can pull lazily on
909+-- demand.
910+-- 2. a step function strict in its second argument (usually reducers e.g.
911+-- (+)) would end up pulling all of its input and buffer it in memory before
912+-- potentially reducing it.
913+--
914+-- A right fold is suitable for lazy reconstructions e.g. transformation,
915+-- mapping, filtering of /right associated input structures/ (e.g. cons lists).
916+-- Whereas a left fold is suitable for reductions (e.g. summing a stream of
917+-- numbers) of right associated structures. Note that these roles will reverse
918+-- for left associated structures (e.g. snoc lists). Most of our observations
919+-- here assume right associated structures, lists being the canonical example.
920+--
921+-- 1. A lazy FIFO style pull using a right fold allows pulling a potentially
922+-- /infinite/ input stream lazily, perform transformations on it, and
923+-- reconstruct a new structure without having to buffer the whole structure. In
924+-- contrast, a left fold would buffer the entire structure before the
925+-- reconstructed structure can be consumed.
926+-- 2. Even if buffering the entire input structure is ok, we need to keep in
927+-- mind that a right fold reconstructs structures in a FIFO style, whereas a
928+-- left fold reconstructs in a LIFO style, thereby reversing the order of
929+-- elements..
930+-- 3. A right fold has termination control and therefore can terminate early
931+-- without going throught the entire input, a left fold cannot terminate
932+-- without consuming all of its input. For example, a right fold
933+-- implementation of 'or' can terminate as soon as it finds the first 'True'
934+-- element, whereas a left fold would necessarily go through the entire input
935+-- irrespective of that.
936+-- 4. Reduction (e.g. using (+) on a stream of numbers) using a right fold
937+-- occurs in a LIFO style, which means that the entire input gets buffered
938+-- before reduction starts. Whereas with a strict left fold reductions occur
939+-- incrementally in FIFO style. Therefore, a strict left fold is more suitable
940+-- for reductions.
941+--
942+-- @since 0.7.0
943+{-# INLINE foldrM #-}
944+foldrM :: Monad m => (a -> m b -> m b) -> m b -> SerialT m a -> m b
945+foldrM = P.foldrM
946+
947+-- | Right fold to a streaming monad.
766--
948--
767−-- When `f` is a function strict in its second argument, the right side of the
949+-- 'foldrS' can be used to perform stateless stream to stream transformations
768−-- expression gets evaluated as follows:
950+-- like map and filter in general. It can be coupled with a scan to perform
951+-- stateful transformations. However, note that the custom map and filter
952+-- routines can be much more efficient than this due to better stream fusion.
769--
953--
770−-- @
954+-- Find if any element in the stream is 'True':
771−-- foldr f z xs == x1 \`f` tail1
772−-- tail1 == x2 \`f` tail2
773−-- tail2 == x3 \`f` tail3
774−-- ...
775−-- tailn == xn \`f` z
776−-- @
777--
955--
778−-- In @foldl'@ we have both the arguments of `f` available at each step,
956+-- >>> S.toList $ S.foldrS (\x xs -> if odd x then return True else xs) (return False) $ (S.fromList (2:4:5:undefined) :: SerialT IO Int)
779−-- therefore, each step can be reduced immediately. However, in @foldr@ the
957+-- > [True]
780−-- second argument to `f` is a recursive call, therefore, it ends up building
781−-- the whole expression in memory before it can be reduced, consuming the whole
782−-- input. This makes @foldr@ much less efficient for reduction compared to
783−-- @foldl'@. For example:
784--
958--
785−-- @
959+-- Map (+2) on odd elements and filter out the even elements:
786−-- > S.foldr (+) 0 $ S.fromList [1,2,3,4]
787−-- 10
788−-- @
789--
960--
790−-- When the underlying monad @m@ is strict (e.g. IO), then @foldr@ ends up
961+-- >>> S.toList $ S.foldrS (\x xs -> if odd x then (x + 2) `S.cons` xs else xs) S.nil $ (S.fromList [1..5] :: SerialT IO Int)
791−-- evaluating all of its input because of strict evaluation of the recursive
962+-- > [3,5,7]
792−-- call:
793--
963--
794−-- >> S.foldr (\_ _ -> []) [] $ S.fromList (1:undefined)
964+-- 'foldrM' can also be represented in terms of 'foldrS', however, the former
795−-- >*** Exception: Prelude.undefined
965+-- is much more efficient:
796--
966--
797−-- In a lazy monad, we can consume the input lazily, and terminate the fold
967+-- > foldrM f z s = runIdentityT $ foldrS (\x xs -> lift $ f x (runIdentityT xs)) (lift z) s
798−-- by conditionally not inspecting the recursive expression.
799--
968--
800−-- >> runIdentity $ S.foldr (\x rest -> if x == 3 then [] else x : rest) [] $ S.fromList (4:1:3:undefined)
969+-- @since 0.7.0
801−-- >[4,1]
970+{-# INLINE foldrS #-}
971+foldrS :: IsStream t => (a -> t m b -> t m b) -> t m b -> t m a -> t m b
972+foldrS = K.foldrS
973+
974+-- | Right fold to a transformer monad. This is the most general right fold
975+-- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'
976+-- implementation can be more efficient:
977+--
978+-- > foldrS = foldrT
979+-- > foldrM f z s = runIdentityT $ foldrT (\x xs -> lift $ f x (runIdentityT xs)) (lift z) s
802--
980--
803−-- The arguments to the folding function (@a -> b -> b@) are in the head and
981+-- 'foldrT' can be used to translate streamly streams to other transformer
804−-- tail order of the output, @a@ is the head and @b@ is the tail. Remember, in
982+-- monads e.g. to a different streaming type.
805−-- a right fold the zero is on the right, it is the tail end.
983+--
984+-- @since 0.7.0
985+{-# INLINE foldrT #-}
986+foldrT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
987+ => (a -> s m b -> s m b) -> s m b -> t m a -> s m b
988+foldrT f z s = S.foldrT f z (toStreamS s)
989+
990+-- | Note that with this signature we cannot implement a lazy foldr when the
991+-- monad @m@ is strict. In that case it would be strict in its accumulator and
992+-- therefore would necessarily consume all its input. For this reason we have
993+-- deprecated this API. Though this can be useful for lazy monads, but we can
994+-- always achieve the same thing using foldrM directly.
806--
995--
807-- @since 0.1.0
996-- @since 0.1.0
808{-# INLINE foldr #-}
997{-# INLINE foldr #-}
998+{-# DEPRECATED foldr "This is unnecessarily strict for strict monads. Use foldrM instead." #-}
809foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
999foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
810foldr = P.foldr
1000foldr = P.foldr
811
1001
@@ -817,6 +1007,7 @@ foldr = P.foldr
817--
1007--
818-- @since 0.5.0
1008-- @since 0.5.0
819{-# INLINE foldr1 #-}
1009{-# INLINE foldr1 #-}
1010+{-# DEPRECATED foldr1 "This is unnecessarily strict for strict monads. Use foldrM instead." #-}
820foldr1 :: Monad m => (a -> a -> a) -> SerialT m a -> m (Maybe a)
1011foldr1 :: Monad m => (a -> a -> a) -> SerialT m a -> m (Maybe a)
821foldr1 f m = S.foldr1 f (toStreamS m)
1012foldr1 f m = S.foldr1 f (toStreamS m)
822
1013
@@ -828,48 +1019,59 @@ foldr1 f m = S.foldr1 f (toStreamS m)
828-- @since 0.2.0
1019-- @since 0.2.0
829{-# INLINE foldx #-}
1020{-# INLINE foldx #-}
830foldx :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
1021foldx :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
831−foldx = K.foldx
1022+foldx = P.foldlx'
832
1023
833-- |
1024-- |
834-- @since 0.1.0
1025-- @since 0.1.0
835{-# DEPRECATED foldl "Please use foldx instead." #-}
1026{-# DEPRECATED foldl "Please use foldx instead." #-}
836foldl :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
1027foldl :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
837−foldl = foldx
1028+foldl = P.foldlx'
838
1029
839−-- | Strict left associative fold.
1030+-- | Strict left associative/push fold. Note that the observations below about
1031+-- the behavior of a left fold assume that we are working on a right associated
1032+-- structure like cons lists and streamly streams. If we are working on a left
1033+-- associated structure (e.g. snoc lists) the roles of right and left folds
1034+-- would reverse.
840--
1035--
841−-- For lists a @foldl@ looks like:
1036+-- Let's take a closer look at the @foldl@ definition for lists given above:
842--
1037--
843-- @
1038-- @
844−-- foldl f z [] = z
845-- foldl f z (x:xs) = foldl f (z \`f` x) xs
1039-- foldl f z (x:xs) = foldl f (z \`f` x) xs
846-- @
1040-- @
847--
1041--
1042+-- @foldl@ calls itself recursively, in each call it invokes @f@ as @f z x@
1043+-- providing it with the result accumulated till now @z@ (the state) and the
1044+-- next element from the input container. First call to @f@ is supplied with
1045+-- the initial value of the accumulator @z@ and each subsequent call uses the
1046+-- output of the previous call to @f z x@.
1047+--
1048+-- >> foldl' (+) 0 [1,2,3]
1049+-- > 6
1050+--
848-- The recursive call at the head of the output expression is bound to be
1051-- The recursive call at the head of the output expression is bound to be
849−-- evaluated until recursion terminates,
1052+-- evaluated until recursion terminates, therefore, a left fold always
850−-- /deconstructing the whole input container/ and building the following left
1053+-- /consumes the whole input container/. The following would result in an
851−-- associated expression:
1054+-- error, even though the fold is not using the values at all:
852--
1055--
853−-- @
1056+-- >> foldl' (\_ _ -> 0) 0 (1:undefined)
854−-- foldl f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
1057+-- > *** Exception: Prelude.undefined
855−-- @
856--
1058--
857−-- When `f` is a constructor, we can see that the first deconstruction of this
1059+-- As @foldl@ recurses, it builds the left associated expression shown below.
858−-- expression would be the recursive expression on the left and `xn` on the
1060+-- Notice, the order of the arguments to the step function @f b a@. It follows
859−-- right. Therefore, it can access the input elements only in the reverse
1061+-- the left associative recursive expression generated by expanding @b \`f` a@.
860−-- (LIFO) order. For example:
861--
1062--
862-- @
1063-- @
863−-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
1064+-- foldl f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
864−-- [4,3,2,1]
865-- @
1065-- @
866--
1066--
1067+--
867-- The strict left fold @foldl'@ forces the reduction of its argument @z \`f`
1068-- The strict left fold @foldl'@ forces the reduction of its argument @z \`f`
868-- x@ before using it, therefore it never builds the whole expression in
1069-- x@ before using it, therefore it never builds the whole expression in
869-- memory. Thus, @z \`f` x1@ would get reduced to @z1@ and then @z1 \`f` x2@
1070-- memory. Thus, @z \`f` x1@ would get reduced to @z1@ and then @z1 \`f` x2@
870-- would get reduced to @z2@ and so on, incrementally reducing the expression
1071-- would get reduced to @z2@ and so on, incrementally reducing the expression
871−-- as it recurses. However, it evaluates the accumulator only to WHNF, it may
1072+-- from left to right as it recurses, consuming the input in FIFO order. Try
872−-- further help to use a strict data structure as accumulator. For example:
1073+-- out @foldl' (+) 0 (map (\\x -> trace (show x) x) [1..10])@ to see how it
1074+-- works. For example:
873--
1075--
874-- @
1076-- @
875-- > S.foldl' (+) 0 $ S.fromList [1,2,3,4]
1077-- > S.foldl' (+) 0 $ S.fromList [1,2,3,4]
@@ -877,36 +1079,44 @@ foldl = foldx
877-- @
1079-- @
878--
1080--
879-- @
1081-- @
880−-- 0 + 1
1082+-- 0 + 1 = 1
881−-- (0 + 1) + 2
882−-- ((0 + 1) + 2) + 3
1083+-- 1 + 2 = 3
883−-- (((0 + 1) + 2) + 3) + 4
1084+-- 3 + 3 = 6
1085+-- 6 + 4 = 10
884-- @
1086-- @
885--
1087--
886−-- @foldl@ strictly deconstructs the whole input container irrespective of
1088+-- However, @foldl'@ evaluates the accumulator only to WHNF. It may further
887−-- whether it needs it or not:
1089+-- help if the step function uses a strict data structure as accumulator to
1090+-- improve performance and to keep the expression fully reduced at all times
1091+-- during the fold.
888--
1092--
889−-- >> S.foldl' (\acc x -> if x == 3 then acc else x : acc) [] $ S.fromList (4:1:3:undefined)
1093+-- A left fold can also build a new structure instead of reducing one if a
890−-- >*** Exception: Prelude.undefined
1094+-- constructor is used as a fold step. However, it may not be very useful
1095+-- because it will consume the whole input and construct the new structure in
1096+-- memory before we can consume it. Thus the whole structure gets buffered in
1097+-- memory. When the list constructor is used it would build a new list in
1098+-- reverse (LIFO) order:
891--
1099--
892−-- However, evaluation of the items contained in the input container is lazy as
1100+-- @
893−-- demanded by the fold step function:
894−--
895−-- >> S.foldl' (\acc x -> if x == 3 then acc else x : acc) [] $ S.fromList [4,1,3,undefined]
1101+-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
896−-- >[4,1]
1102+-- [4,3,2,1]
1103+-- @
897--
1104--
898−-- To perform a left fold without consuming all the input one can use @scanl@
1105+-- A left fold is a push fold. The producer pushes its contents to the step
899−-- to stream the intermediate results of the fold and use them lazily.
1106+-- function of the fold. The step function therefore has no control to stop the
1107+-- input, it can only discard it if it does not need it. We can also consider a
1108+-- left fold as a state machine where the state is store in the accumulator,
1109+-- the state can be modified based on new inputs that are pushed to the fold.
900--
1110--
901−-- In stateful or event-driven programming, we can consider @z@ as the initial
1111+-- In general, a strict left fold is a reducing fold, whereas a right fold is a
902−-- state and the stream being folded as a stream of events, thus @foldl'@
1112+-- constructing fold. A strict left fold reduces in a FIFO order whereas it
903−-- processes all the events in the stream updating the state on each event and
1113+-- constructs in a LIFO order, and vice-versa for the right fold. See the
904−-- then ultimately returning the final state.
1114+-- documentation of 'foldrM' for a discussion on where a left or right fold is
1115+-- suitable.
905--
1116--
906−-- The arguments to the folding function (@b -> a -> b@) are in the head and
1117+-- To perform a left fold lazily without having to consume all the input one
907−-- tail order of the output expression, @b@ is the head and @a@ is the tail.
1118+-- can use @scanl@ to stream the intermediate results of the fold and consume
908−-- Remember, in a left fold the zero is on the left, at the head of the
1119+-- the resulting stream lazily.
909−-- expression.
910--
1120--
911-- @since 0.2.0
1121-- @since 0.2.0
912{-# INLINE foldl' #-}
1122{-# INLINE foldl' #-}
@@ -932,13 +1142,13 @@ foldl1' step m = do
932-- @since 0.2.0
1142-- @since 0.2.0
933{-# INLINE foldxM #-}
1143{-# INLINE foldxM #-}
934foldxM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> SerialT m a -> m b
1144foldxM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> SerialT m a -> m b
935−foldxM = K.foldxM
1145+foldxM = P.foldlMx'
936
1146
937-- |
1147-- |
938-- @since 0.1.0
1148-- @since 0.1.0
939{-# DEPRECATED foldlM "Please use foldxM instead." #-}
1149{-# DEPRECATED foldlM "Please use foldxM instead." #-}
940foldlM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> SerialT m a -> m b
1150foldlM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> SerialT m a -> m b
941−foldlM = foldxM
1151+foldlM = P.foldlMx'
942
1152
943-- | Like 'foldl'' but with a monadic step function.
1153-- | Like 'foldl'' but with a monadic step function.
944--
1154--
@@ -951,7 +1161,10 @@ foldlM' step begin m = S.foldlM' step begin $ toStreamS m
951-- Specialized folds
1161-- Specialized folds
952------------------------------------------------------------------------------
1162------------------------------------------------------------------------------
953
1163
1164+-- |
1165+-- > runStream = mapM_ (\_ -> return ())
1166+--
954−-- | Run a stream, discarding the results. By default it interprets the stream
1167+-- Run a stream, discarding the results. By default it interprets the stream
955-- as 'SerialT', to run other types of streams use the type adapting
1168-- as 'SerialT', to run other types of streams use the type adapting
956-- combinators for example @runStream . 'asyncly'@.
1169-- combinators for example @runStream . 'asyncly'@.
957--
1170--
@@ -996,7 +1209,10 @@ null = K.null
996head :: Monad m => SerialT m a -> m (Maybe a)
1209head :: Monad m => SerialT m a -> m (Maybe a)
997head = K.head
1210head = K.head
998
1211
1212+-- |
1213+-- > tail = fmap (fmap snd) . uncons
1214+--
999−-- | Extract all but the first element of the stream, if any.
1215+-- Extract all but the first element of the stream, if any.
1000--
1216--
1001-- @since 0.1.1
1217-- @since 0.1.1
1002{-# INLINE tail #-}
1218{-# INLINE tail #-}
@@ -1190,24 +1406,6 @@ elemIndices a = findIndices (==a)
1190elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
1406elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
1191elemIndex a = findIndex (== a)
1407elemIndex a = findIndex (== a)
1192
1408
1193−-- | Map each element to a stream and then flatten the results into a single
1194−-- stream.
1195−--
1196−-- > concatMap f = concatMapM (return . f)
1197−--
1198−-- @since 0.6.0
1199−{-# INLINE concatMap #-}
1200−concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
1201−concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
1202−
1203−-- | Map each element to a stream using a monadic function and then flatten the
1204−-- results into a single stream.
1205−--
1206−-- @since 0.6.0
1207−{-# INLINE concatMapM #-}
1208−concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
1209−concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
1210−
1211------------------------------------------------------------------------------
1409------------------------------------------------------------------------------
1212-- Substreams
1410-- Substreams
1213------------------------------------------------------------------------------
1411------------------------------------------------------------------------------
@@ -1256,8 +1454,12 @@ stripPrefix m1 m2 = fmap fromStreamD <$>
1256------------------------------------------------------------------------------
1454------------------------------------------------------------------------------
1257
1455
1258-- XXX this can utilize parallel mapping if we implement it as runStream . mapM
1456-- XXX this can utilize parallel mapping if we implement it as runStream . mapM
1457+-- |
1458+-- > mapM_ = runStream . mapM
1459+--
1259−-- | Apply a monadic action to each element of the stream and discard the
1460+-- Apply a monadic action to each element of the stream and discard the output
1260−-- output of the action.
1461+-- of the action. This is not really a pure transformation operation but a
1462+-- transformation followed by fold.
1261--
1463--
1262-- @since 0.1.0
1464-- @since 0.1.0
1263{-# INLINE mapM_ #-}
1465{-# INLINE mapM_ #-}
@@ -1273,13 +1475,27 @@ mapM_ f m = S.mapM_ f $ toStreamS m
1273-- toList = S.foldr (:) []
1475-- toList = S.foldr (:) []
1274-- @
1476-- @
1275--
1477--
1276−-- Convert a stream into a list in the underlying monad. Same as:
1478+-- Convert a stream into a list in the underlying monad. The list can be
1479+-- consumed lazily in a lazy monad (e.g. 'Identity'). In a strict monad (e.g.
1480+-- IO) the whole list is generated before it can be consumed.
1277--
1481--
1278-- @since 0.1.0
1482-- @since 0.1.0
1279{-# INLINE toList #-}
1483{-# INLINE toList #-}
1280toList :: Monad m => SerialT m a -> m [a]
1484toList :: Monad m => SerialT m a -> m [a]
1281toList = P.toList
1485toList = P.toList
1282
1486
1487+-- |
1488+-- @
1489+-- toRevList = S.foldl' (flip (:)) []
1490+-- @
1491+--
1492+-- Convert a stream into a list in reverse order in the underlying monad.
1493+--
1494+-- @since 0.7.0
1495+{-# INLINE toRevList #-}
1496+toRevList :: Monad m => SerialT m a -> m [a]
1497+toRevList = D.toRevList . toStreamD
1498+
1283-- |
1499-- |
1284-- @
1500-- @
1285-- toHandle h = S.mapM_ $ hPutStrLn h
1501-- toHandle h = S.mapM_ $ hPutStrLn h
@@ -1301,6 +1517,16 @@ toHandle h m = go m
1301-- Transformation by Folding (Scans)
1517-- Transformation by Folding (Scans)
1302------------------------------------------------------------------------------
1518------------------------------------------------------------------------------
1303
1519
1520+-- XXX It may be useful to have a version of scan where we can keep the
1521+-- accumulator independent of the value emitted. So that we do not necessarily
1522+-- have to keep a value in the accumulator which we are not using. We can pass
1523+-- an extraction function that will take the accumulator and the current value
1524+-- of the element and emit the next value in the stream. That will also make it
1525+-- possible to modify the accumulator after using it. In fact, the step function
1526+-- can return new accumulator and the value to be emitted. The signature would
1527+-- be more like mapAccumL. Or we can change the signature of scanx to
1528+-- accommodate this.
1529+--
1304-- | Strict left scan with an extraction function. Like 'scanl'', but applies a
1530-- | Strict left scan with an extraction function. Like 'scanl'', but applies a
1305-- user supplied extraction function (the third argument) at each step. This is
1531-- user supplied extraction function (the third argument) at each step. This is
1306-- designed to work with the @foldl@ library. The suffix @x@ is a mnemonic for
1532-- designed to work with the @foldl@ library. The suffix @x@ is a mnemonic for
@@ -1309,7 +1535,7 @@ toHandle h m = go m
1309-- @since 0.2.0
1535-- @since 0.2.0
1310{-# INLINE scanx #-}
1536{-# INLINE scanx #-}
1311scanx :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
1537scanx :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
1312−scanx = K.scanx
1538+scanx = K.scanx'
1313
1539
1314-- |
1540-- |
1315-- @since 0.1.1
1541-- @since 0.1.1
@@ -1325,7 +1551,8 @@ scan = scanx
1325scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
1551scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
1326scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
1552scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
1327
1553
1328−-- | Strict left scan.
1554+-- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,
1555+-- however it adds an extra element.
1329--
1556--
1330-- @
1557-- @
1331-- > S.toList $ S.scanl' (+) 0 $ fromList [1,2,3,4]
1558-- > S.toList $ S.scanl' (+) 0 $ fromList [1,2,3,4]
@@ -1346,16 +1573,16 @@ scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
1346-- thus modularizing the stream processing. This can be useful in
1573-- thus modularizing the stream processing. This can be useful in
1347-- stateful or event-driven programming.
1574-- stateful or event-driven programming.
1348--
1575--
1349−-- Consider the following example, computing the sum and the product of the
1576+-- Consider the following monolothic example, computing the sum and the product
1350−-- elements in a stream in one go using a @foldl'@:
1577+-- of the elements in a stream in one go using a @foldl'@:
1351--
1578--
1352-- @
1579-- @
1353-- > S.foldl' (\\(s, p) x -> (s + x, p * x)) (0,1) $ S.fromList \[1,2,3,4]
1580-- > S.foldl' (\\(s, p) x -> (s + x, p * x)) (0,1) $ S.fromList \[1,2,3,4]
1354-- (10,24)
1581-- (10,24)
1355-- @
1582-- @
1356--
1583--
1357−-- Using @scanl'@ we can compute the sum in the first stage and pass it down to
1584+-- Using @scanl'@ we can make it modular by computing the sum in the first
1358−-- the next stage for computing the product:
1585+-- stage and passing it down to the next stage for computing the product:
1359--
1586--
1360-- @
1587-- @
1361-- > S.foldl' (\\(_, p) (s, x) -> (s, p * x)) (0,1)
1588-- > S.foldl' (\\(_, p) (s, x) -> (s, p * x)) (0,1)
@@ -1609,19 +1836,16 @@ mapMaybeMSerial f m = fromStreamD $ D.mapMaybeM f $ toStreamD m
1609-- XXX to scale this we need to use a slab allocated array backed
1836-- XXX to scale this we need to use a slab allocated array backed
1610-- representation for temporary storage.
1837-- representation for temporary storage.
1611--
1838--
1839+-- |
1840+-- > reverse = S.foldlT (flip S.cons) S.nil
1841+--
1612−-- | Returns the elements of the stream in reverse order.
1842+-- Returns the elements of the stream in reverse order. The stream must be
1613−-- The stream must be finite.
1843+-- finite.
1614--
1844--
1615-- @since 0.1.1
1845-- @since 0.1.1
1846+{-# INLINE reverse #-}
1616−reverse :: (IsStream t) => t m a -> t m a
1847+reverse :: (IsStream t, Monad m) => t m a -> t m a
1617−reverse m = go K.nil m
1848+reverse s = fromStreamS $ S.reverse $ toStreamS s
1618− where
1619− go rev rest = K.mkStream $ \st yld sng stp ->
1620− let runIt x = K.foldStream st yld sng stp x
1621− stop = runIt rev
1622− single a = runIt $ a `K.cons` rev
1623− yieldk a r = runIt $ go (a `K.cons` rev) r
1624− in K.foldStream st yieldk single stop rest
1625
1849
1626------------------------------------------------------------------------------
1850------------------------------------------------------------------------------
1627-- Transformation by Inserting
1851-- Transformation by Inserting
@@ -1682,9 +1906,10 @@ deleteBy cmp x m = fromStreamS $ S.deleteBy cmp x (toStreamS m)
1682------------------------------------------------------------------------------
1906------------------------------------------------------------------------------
1683
1907
1684-- |
1908-- |
1685−-- > indexed = S.zipWith (,) (S.intFrom 0)
1909+-- > indexed = S.postscanl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
1910+-- > indexed = S.zipWith (,) (S.enumerateFrom 0)
1686--
1911--
1687−-- Pair each element in a stream with its index.
1912+-- Pair each element in a stream with its index, starting from index 0.
1688--
1913--
1689-- @
1914-- @
1690-- > S.toList $ S.indexed $ S.fromList "hello"
1915-- > S.toList $ S.indexed $ S.fromList "hello"
@@ -1697,14 +1922,15 @@ indexed :: (IsStream t, Monad m) => t m a -> t m (Int, a)
1697indexed = fromStreamD . D.indexed . toStreamD
1922indexed = fromStreamD . D.indexed . toStreamD
1698
1923
1699-- |
1924-- |
1700−-- > indexedR n = S.zipWith (,) (S.intFromThen n (n - 1))
1925+-- > indexedR n = S.postscanl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
1926+-- > indexedR n = S.zipWith (,) (S.enumerateFromThen n (n - 1))
1701--
1927--
1702-- Pair each element in a stream with its index, starting from the
1928-- Pair each element in a stream with its index, starting from the
1703-- given index @n@ and counting down.
1929-- given index @n@ and counting down.
1704--
1930--
1705-- @
1931-- @
1706-- > S.toList $ S.indexedR 10 $ S.fromList "hello"
1932-- > S.toList $ S.indexedR 10 $ S.fromList "hello"
1707−-- [(9,'h'),(8,'e'),(7,'l'),(6,'l'),(5,'o')]
1933+-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
1708-- @
1934-- @
1709--
1935--
1710-- @since 0.6.0
1936-- @since 0.6.0
@@ -1847,3 +2073,25 @@ mergeAsyncByM f m1 m2 = K.mkStream $ \st stp sng yld -> do
1847 ma <- mkAsync' st m1
2073 ma <- mkAsync' st m1
1848 mb <- mkAsync' st m2
2074 mb <- mkAsync' st m2
1849 K.foldStream st stp sng yld (K.mergeByM f ma mb)
2075 K.foldStream st stp sng yld (K.mergeByM f ma mb)
2076+
2077+------------------------------------------------------------------------------
2078+-- Nesting
2079+------------------------------------------------------------------------------
2080+
2081+-- | Map each element of the stream to produce a stream and then flatten the
2082+-- results into a single stream.
2083+--
2084+-- > concatMap f = concatMapM (return . f)
2085+--
2086+-- @since 0.6.0
2087+{-# INLINE concatMap #-}
2088+concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
2089+concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
2090+
2091+-- | Map each element to a stream to produce a stream using a monadic function
2092+-- and then flatten the results into a single stream.
2093+--
2094+-- @since 0.6.0
2095+{-# INLINE concatMapM #-}
2096+concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
2097+concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
src/Streamly/Streams/Prelude.hs+50−2
@@ -31,9 +31,17 @@ module Streamly.Streams.Prelude
31
31
32 -- * Fold operations
32 -- * Fold operations
33 , foldrM
33 , foldrM
34+ , foldrMx
34 , foldr
35 , foldr
36+
37+ , foldlx'
38+ , foldlMx'
35 , foldl'
39 , foldl'
36
40
41+ -- Lazy left folds are useful only for reversing the stream
42+ , foldlS
43+ , foldlT
44+
37 -- * Zip style operations
45 -- * Zip style operations
38 , eqBy
46 , eqBy
39 , cmpBy
47 , cmpBy
@@ -45,6 +53,7 @@ module Streamly.Streams.Prelude
45 )
53 )
46where
54where
47
55
56+import Control.Monad.Trans (MonadTrans(..))
48import Prelude hiding (foldr)
57import Prelude hiding (foldr)
49import qualified Prelude
58import qualified Prelude
50
59
@@ -112,12 +121,36 @@ toList m = S.toList $ toStreamS m
112------------------------------------------------------------------------------
121------------------------------------------------------------------------------
113
122
114{-# INLINE foldrM #-}
123{-# INLINE foldrM #-}
115−foldrM :: (Monad m, IsStream t) => (a -> b -> m b) -> b -> t m a -> m b
124+foldrM :: (Monad m, IsStream t) => (a -> m b -> m b) -> m b -> t m a -> m b
116foldrM step acc m = S.foldrM step acc $ toStreamS m
125foldrM step acc m = S.foldrM step acc $ toStreamS m
117
126
127+{-# INLINE foldrMx #-}
128+foldrMx :: (Monad m, IsStream t)
129+ => (a -> m x -> m x) -> m x -> (m x -> m b) -> t m a -> m b
130+foldrMx step final project m = S.foldrMx step final project $ toStreamS m
131+
118{-# INLINE foldr #-}
132{-# INLINE foldr #-}
119foldr :: (Monad m, IsStream t) => (a -> b -> b) -> b -> t m a -> m b
133foldr :: (Monad m, IsStream t) => (a -> b -> b) -> b -> t m a -> m b
120−foldr f = foldrM (\a b -> return (f a b))
134+foldr f z = foldrM (\a b -> b >>= return . f a) (return z)
135+
136+-- | Like 'foldlx'', but with a monadic step function.
137+--
138+-- @since 0.7.0
139+{-# INLINE foldlMx' #-}
140+foldlMx' :: (IsStream t, Monad m)
141+ => (x -> a -> m x) -> m x -> (x -> m b) -> t m a -> m b
142+foldlMx' step begin done m = S.foldlMx' step begin done $ toStreamS m
143+
144+-- | Strict left fold with an extraction function. Like the standard strict
145+-- left fold, but applies a user supplied extraction function (the third
146+-- argument) to the folded value at the end. This is designed to work with the
147+-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
148+--
149+-- @since 0.7.0
150+{-# INLINE foldlx' #-}
151+foldlx' :: (IsStream t, Monad m)
152+ => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
153+foldlx' step begin done m = S.foldlx' step begin done $ toStreamS m
121
154
122-- | Strict left associative fold.
155-- | Strict left associative fold.
123--
156--
@@ -126,6 +159,21 @@ foldr f = foldrM (\a b -> return (f a b))
126foldl' :: (Monad m, IsStream t) => (b -> a -> b) -> b -> t m a -> m b
159foldl' :: (Monad m, IsStream t) => (b -> a -> b) -> b -> t m a -> m b
127foldl' step begin m = S.foldl' step begin $ toStreamS m
160foldl' step begin m = S.foldl' step begin $ toStreamS m
128
161
162+{-# INLINE foldlS #-}
163+foldlS :: IsStream t => (t m b -> a -> t m b) -> t m b -> t m a -> t m b
164+foldlS = K.foldlS
165+
166+-- | Lazy left fold to a transformer monad.
167+--
168+-- For example, to reverse a stream:
169+--
170+-- > S.toList $ S.foldlT (flip S.cons) S.nil $ (S.fromList [1..5] :: SerialT IO Int)
171+--
172+{-# INLINE foldlT #-}
173+foldlT :: (Monad m, IsStream t, Monad (s m), MonadTrans s)
174+ => (s m b -> a -> s m b) -> s m b -> t m a -> s m b
175+foldlT f z s = S.foldlT f z (toStreamS s)
176+
129------------------------------------------------------------------------------
177------------------------------------------------------------------------------
130-- Comparison
178-- Comparison
131------------------------------------------------------------------------------
179------------------------------------------------------------------------------
src/Streamly/Streams/SVar.hs+1−1
@@ -28,7 +28,7 @@ import Streamly.Time.Clock (Clock(Monotonic), getTime)
28import System.Mem (performMajorGC)
28import System.Mem (performMajorGC)
29
29
30import Streamly.SVar
30import Streamly.SVar
31−import Streamly.Streams.StreamK
31+import Streamly.Streams.StreamK hiding (reverse)
32
32
33printSVar :: SVar t m a -> String -> IO ()
33printSVar :: SVar t m a -> String -> IO ()
34printSVar sv how = do
34printSVar sv how = do
src/Streamly/Streams/StreamD.hs+119−55
@@ -6,6 +6,8 @@
6{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE PatternSynonyms #-}
8{-# LANGUAGE PatternSynonyms #-}
9+{-# LANGUAGE RecordWildCards #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ViewPatterns #-}
11{-# LANGUAGE ViewPatterns #-}
10{-# LANGUAGE RankNTypes #-}
12{-# LANGUAGE RankNTypes #-}
11
13
@@ -86,11 +88,20 @@ module Streamly.Streams.StreamD
86
88
87 -- * Elimination
89 -- * Elimination
88 -- ** General Folds
90 -- ** General Folds
89− , foldr
91+ , foldrT
90 , foldrM
92 , foldrM
93+ , foldrMx
94+ , foldr
91 , foldr1
95 , foldr1
96+
92 , foldl'
97 , foldl'
93 , foldlM'
98 , foldlM'
99+ , foldlS
100+ , foldlT
101+ , reverse
102+
103+ , foldlx'
104+ , foldlMx'
94
105
95 -- ** Specialized Folds
106 -- ** Specialized Folds
96 , runStream
107 , runStream
@@ -111,6 +122,8 @@ module Streamly.Streams.StreamD
111 , findM
122 , findM
112 , find
123 , find
113 , (!!)
124 , (!!)
125+
126+ -- ** Flattening nested streams
114 , concatMapM
127 , concatMapM
115 , concatMap
128 , concatMap
116
129
@@ -125,6 +138,7 @@ module Streamly.Streams.StreamD
125 -- ** Conversions
138 -- ** Conversions
126 -- | Transform a stream into another type.
139 -- | Transform a stream into another type.
127 , toList
140 , toList
141+ , toRevList
128 , toStreamK
142 , toStreamK
129 , toStreamD
143 , toStreamD
130
144
@@ -191,13 +205,15 @@ module Streamly.Streams.StreamD
191 )
205 )
192where
206where
193
207
208+import Control.Monad.Trans (MonadTrans(lift))
194import Data.Maybe (fromJust, isJust)
209import Data.Maybe (fromJust, isJust)
195−import GHC.Types ( SPEC(..) )
210+import GHC.Types (SPEC(..))
196import Prelude
211import Prelude
197 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
212 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
198 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
213 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
199 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
214 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
200− (!!), scanl, scanl1, concatMap, replicate, enumFromTo)
215+ (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
216+ reverse)
201
217
202import Streamly.SVar (MonadAsync, defState, adaptState)
218import Streamly.SVar (MonadAsync, defState, adaptState)
203
219
@@ -508,20 +524,9 @@ toStreamD = fromStreamK . K.toStream
508-- Elimination by Folds
524-- Elimination by Folds
509------------------------------------------------------------------------------
525------------------------------------------------------------------------------
510
526
511−{-# INLINE_NORMAL foldrM #-}
527+------------------------------------------------------------------------------
512−foldrM :: Monad m => (a -> b -> m b) -> b -> Stream m a -> m b
528+-- Right Folds
513−foldrM f z (Stream step state) = go SPEC state
529+------------------------------------------------------------------------------
514− where
515− go !_ st = do
516− r <- step defState st
517− case r of
518− Yield x s -> go SPEC s >>= f x
519− Skip s -> go SPEC s
520− Stop -> return z
521−
522−{-# INLINE_NORMAL foldr #-}
523−foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
524−foldr f = foldrM (\a b -> return (f a b))
525
530
526{-# INLINE_NORMAL foldr1 #-}
531{-# INLINE_NORMAL foldr1 #-}
527foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)
532foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)
@@ -531,6 +536,32 @@ foldr1 f m = do
531 Nothing -> return Nothing
536 Nothing -> return Nothing
532 Just (h, t) -> fmap Just (foldr f h t)
537 Just (h, t) -> fmap Just (foldr f h t)
533
538
539+------------------------------------------------------------------------------
540+-- Left Folds
541+------------------------------------------------------------------------------
542+
543+-- XXX run begin action only if the stream is not empty.
544+{-# INLINE_NORMAL foldlMx' #-}
545+foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
546+foldlMx' fstep begin done (Stream step state) =
547+ begin >>= \x -> go SPEC x state
548+ where
549+ -- XXX !acc?
550+ go !_ acc st = acc `seq` do
551+ r <- step defState st
552+ case r of
553+ Yield x s -> do
554+ acc' <- fstep acc x
555+ go SPEC acc' s
556+ Skip s -> go SPEC acc s
557+ Stop -> done acc
558+
559+{-# INLINE foldlx' #-}
560+foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
561+foldlx' fstep begin done m =
562+ foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
563+
564+-- XXX implement in terms of foldlMx'
534{-# INLINE_NORMAL foldlM' #-}
565{-# INLINE_NORMAL foldlM' #-}
535foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
566foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
536foldlM' fstep begin (Stream step state) = go SPEC begin state
567foldlM' fstep begin (Stream step state) = go SPEC begin state
@@ -544,6 +575,40 @@ foldlM' fstep begin (Stream step state) = go SPEC begin state
544 Skip s -> go SPEC acc s
575 Skip s -> go SPEC acc s
545 Stop -> return acc
576 Stop -> return acc
546
577
578+{-# INLINE_NORMAL foldlT #-}
579+foldlT :: (Monad m, Monad (s m), MonadTrans s)
580+ => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
581+foldlT fstep begin (Stream step state) = go SPEC begin state
582+ where
583+ go !_ acc st = do
584+ r <- lift $ step defState st
585+ case r of
586+ Yield x s -> go SPEC (fstep acc x) s
587+ Skip s -> go SPEC acc s
588+ Stop -> acc
589+
590+-- Note, this is going to have horrible performance, because of the nature of
591+-- the stream type (i.e. direct stream vs CPS). Its only for reference, it is
592+-- likely be practically unusable.
593+{-# INLINE_NORMAL foldlS #-}
594+foldlS :: Monad m
595+ => (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b
596+foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin))
597+ where
598+ step' gst (Left (st, acc)) = do
599+ r <- step (adaptState gst) st
600+ return $ case r of
601+ Yield x s -> Skip (Left (s, fstep acc x))
602+ Skip s -> Skip (Left (s, acc))
603+ Stop -> Skip (Right acc)
604+
605+ step' gst (Right (Stream stp stt)) = do
606+ r <- stp (adaptState gst) stt
607+ return $ case r of
608+ Yield x s -> Yield x (Right (Stream stp s))
609+ Skip s -> Skip (Right (Stream stp s))
610+ Stop -> Stop
611+
547{-# INLINE foldl' #-}
612{-# INLINE foldl' #-}
548foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
613foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
549foldl' fstep = foldlM' (\b a -> return (fstep b a))
614foldl' fstep = foldlM' (\b a -> return (fstep b a))
@@ -555,6 +620,7 @@ foldl' fstep = foldlM' (\b a -> return (fstep b a))
555-- | Run a streaming composition, discard the results.
620-- | Run a streaming composition, discard the results.
556{-# INLINE_LATE runStream #-}
621{-# INLINE_LATE runStream #-}
557runStream :: Monad m => Stream m a -> m ()
622runStream :: Monad m => Stream m a -> m ()
623+-- runStream m = foldrM (\x xs -> x `seq` xs) (return ()) m
558runStream (Stream step state) = go SPEC state
624runStream (Stream step state) = go SPEC state
559 where
625 where
560 go !_ st = do
626 go !_ st = do
@@ -566,26 +632,12 @@ runStream (Stream step state) = go SPEC state
566
632
567{-# INLINE_NORMAL null #-}
633{-# INLINE_NORMAL null #-}
568null :: Monad m => Stream m a -> m Bool
634null :: Monad m => Stream m a -> m Bool
569−null (Stream step state) = go state
570− where
571− go st = do
572− r <- step defState st
573− case r of
574− Yield _ _ -> return False
635+null m = foldrM (\_ _ -> return False) (return True) m
575− Skip s -> go s
576− Stop -> return True
577
636
578-- XXX SPEC?
637-- XXX SPEC?
579{-# INLINE_NORMAL head #-}
638{-# INLINE_NORMAL head #-}
580head :: Monad m => Stream m a -> m (Maybe a)
639head :: Monad m => Stream m a -> m (Maybe a)
581−head (Stream step state) = go state
582− where
583− go st = do
584− r <- step defState st
585− case r of
586− Yield x _ -> return (Just x)
587− Skip s -> go s
588− Stop -> return Nothing
640+head m = foldrM (\x _ -> return (Just x)) (return Nothing) m
589
641
590-- Does not fuse, has the same performance as the StreamK version.
642-- Does not fuse, has the same performance as the StreamK version.
591{-# INLINE_NORMAL tail #-}
643{-# INLINE_NORMAL tail #-}
@@ -606,6 +658,7 @@ last = foldl' (\_ y -> Just y) Nothing
606
658
607{-# INLINE_NORMAL elem #-}
659{-# INLINE_NORMAL elem #-}
608elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool
660elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool
661+-- elem e m = foldrM (\x xs -> if x == e then return True else xs) (return False) m
609elem e (Stream step state) = go state
662elem e (Stream step state) = go state
610 where
663 where
611 go st = do
664 go st = do
@@ -623,6 +676,7 @@ notElem e s = fmap not (elem e s)
623
676
624{-# INLINE_NORMAL all #-}
677{-# INLINE_NORMAL all #-}
625all :: Monad m => (a -> Bool) -> Stream m a -> m Bool
678all :: Monad m => (a -> Bool) -> Stream m a -> m Bool
679+-- all p m = foldrM (\x xs -> if p x then xs else return False) (return True) m
626all p (Stream step state) = go state
680all p (Stream step state) = go state
627 where
681 where
628 go st = do
682 go st = do
@@ -636,6 +690,7 @@ all p (Stream step state) = go state
636
690
637{-# INLINE_NORMAL any #-}
691{-# INLINE_NORMAL any #-}
638any :: Monad m => (a -> Bool) -> Stream m a -> m Bool
692any :: Monad m => (a -> Bool) -> Stream m a -> m Bool
693+-- any p m = foldrM (\x xs -> if p x then return True else xs) (return False) m
639any p (Stream step state) = go state
694any p (Stream step state) = go state
640 where
695 where
641 go st = do
696 go st = do
@@ -738,27 +793,13 @@ minimumBy cmp (Stream step state) = go Nothing state
738
793
739{-# INLINE_NORMAL lookup #-}
794{-# INLINE_NORMAL lookup #-}
740lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b)
795lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b)
741−lookup e (Stream step state) = go state
742− where
743− go st = do
744− r <- step defState st
745− case r of
746− Yield (a, b) s -> if e == a then return (Just b) else go s
796+lookup e m = foldrM (\(a, b) xs -> if e == a then return (Just b) else xs)
747− Skip s -> go s
748− Stop -> return Nothing
797+ (return Nothing) m
749
798
750{-# INLINE_NORMAL findM #-}
799{-# INLINE_NORMAL findM #-}
751findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a)
800findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a)
752−findM p (Stream step state) = go SPEC state
753− where
754− go !_ st = do
755− r <- step defState st
756− case r of
757− Yield x s -> do
758− b <- p x
759− if b then return (Just x) else go SPEC s
801+findM p m = foldrM (\x xs -> p x >>= \r -> if r then return (Just x) else xs)
760− Skip s -> go SPEC s
761− Stop -> return Nothing
802+ (return Nothing) m
762
803
763{-# INLINE find #-}
804{-# INLINE find #-}
764find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)
805find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)
@@ -776,6 +817,33 @@ findIndices p (Stream step state) = Stream step' (state, 0)
776 Skip s -> Skip (s, i+1)
817 Skip s -> Skip (s, i+1)
777 Stop -> Stop
818 Stop -> Stop
778
819
820+{-# INLINE toRevList #-}
821+toRevList :: Monad m => Stream m a -> m [a]
822+toRevList = foldl' (flip (:)) []
823+
824+-- We can implement reverse as:
825+--
826+-- > reverse = foldlS (flip cons) nil
827+--
828+-- However, this implementation is unusable because of the horrible performance
829+-- of cons. So we just convert it to a list first and then stream from the
830+-- list.
831+--
832+-- XXX Maybe we can use an Array instead of a list here?
833+{-# INLINE_NORMAL reverse #-}
834+reverse :: Monad m => Stream m a -> Stream m a
835+reverse m = Stream step Nothing
836+ where
837+ step _ Nothing = do
838+ xs <- toRevList m
839+ return $ Skip (Just xs)
840+ step _ (Just (x:xs)) = return $ Yield x (Just xs)
841+ step _ (Just []) = return Stop
842+
843+------------------------------------------------------------------------------
844+-- concatMap
845+------------------------------------------------------------------------------
846+
779{-# INLINE_NORMAL concatMapM #-}
847{-# INLINE_NORMAL concatMapM #-}
780concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
848concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
781concatMapM f (Stream step state) = Stream step' (Left state)
849concatMapM f (Stream step state) = Stream step' (Left state)
@@ -888,10 +956,6 @@ mapM_ m = runStream . mapM m
888-- Converting folds
956-- Converting folds
889------------------------------------------------------------------------------
957------------------------------------------------------------------------------
890
958
891−{-# INLINE toList #-}
892−toList :: Monad m => Stream m a -> m [a]
893−toList = foldr (:) []
894−
895-- Convert a direct stream to and from CPS encoded stream
959-- Convert a direct stream to and from CPS encoded stream
896{-# INLINE_LATE toStreamK #-}
960{-# INLINE_LATE toStreamK #-}
897toStreamK :: Monad m => Stream m a -> K.Stream m a
961toStreamK :: Monad m => Stream m a -> K.Stream m a
src/Streamly/Streams/StreamD/Type.hs+83−2
@@ -34,12 +34,21 @@ module Streamly.Streams.StreamD.Type
34#endif
34#endif
35 , map
35 , map
36 , mapM
36 , mapM
37+ , foldrT
38+ , foldrM
39+ , foldrMx
40+ , foldr
41+ , toList
37 )
42 )
38where
43where
39
44
45+import Control.Applicative (liftA2)
46+import Control.Monad.Trans (lift, MonadTrans)
47+import GHC.Types (SPEC(..))
48+import Prelude hiding (map, mapM, foldr)
40−import Streamly.SVar (State(..), adaptState)
49+import Streamly.SVar (State(..), adaptState, defState)
50+
41import qualified Streamly.Streams.StreamK as K
51import qualified Streamly.Streams.StreamK as K
42−import Prelude hiding (map, mapM)
43
52
44------------------------------------------------------------------------------
53------------------------------------------------------------------------------
45-- The direct style stream type
54-- The direct style stream type
@@ -98,3 +107,75 @@ map f = mapM (return . f)
98instance Monad m => Functor (Stream m) where
107instance Monad m => Functor (Stream m) where
99 {-# INLINE fmap #-}
108 {-# INLINE fmap #-}
100 fmap = map
109 fmap = map
110+
111+-- XXX Use of SPEC constructor in folds causes 2x performance degradation in
112+-- one shot operations, but helps immensely in operations composed of multiple
113+-- combinators or the same combinator many times. There seems to be an
114+-- opportunity to optimize here, can we get both, better perf for single ops
115+-- as well as composed ops? Without SPEC, all single operation benchmarks
116+-- become 2x faster.
117+
118+-- The way we want a left fold to be strict, dually we want the right fold to
119+-- be lazy. The correct signature of the fold function to keep it lazy must be
120+-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter
121+-- earlier, which is incorrect. In the latter signature we have to feed the
122+-- value to the fold function after evaluating the monadic action, depending on
123+-- the bind behavior of the monad, the action may get evaluated immediately
124+-- introducing unnecessary strictness to the fold. If the implementation is
125+-- lazy the following example, must work:
126+--
127+-- S.foldrM (\x t -> if x then return t else return False) (return True)
128+-- (S.fromList [False,undefined] :: SerialT IO Bool)
129+--
130+{-# INLINE_NORMAL foldrM #-}
131+foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
132+foldrM f z (Stream step state) = go SPEC state
133+ where
134+ go !_ st = do
135+ r <- step defState st
136+ case r of
137+ Yield x s -> f x (go SPEC s)
138+ Skip s -> go SPEC s
139+ Stop -> z
140+
141+{-# INLINE_NORMAL foldrMx #-}
142+foldrMx :: Monad m
143+ => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
144+foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
145+ where
146+ go !_ st = do
147+ r <- step defState st
148+ case r of
149+ Yield x s -> fstep x (go SPEC s)
150+ Skip s -> go SPEC s
151+ Stop -> final
152+
153+-- Note that foldr works on pure values, therefore it becomes necessarily
154+-- strict when the monad m is strict. In that case it cannot terminate early,
155+-- it would evaluate all of its input. Though, this should work fine with lazy
156+-- monads. For example, if "any" is implemented using "foldr" instead of
157+-- "foldrM" it performs the same with Identity monad but performs 1000x slower
158+-- with IO monad.
159+--
160+{-# INLINE_NORMAL foldr #-}
161+foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
162+foldr f z = foldrM (\a b -> liftA2 f (return a) b) (return z)
163+
164+-- Right fold to some transformer (T) monad. This can be useful to implement
165+-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
166+--
167+{-# INLINE_NORMAL foldrT #-}
168+foldrT :: (Monad m, Monad (t m), MonadTrans t)
169+ => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
170+foldrT f final (Stream step state) = go SPEC state
171+ where
172+ go !_ st = do
173+ r <- lift $ step defState st
174+ case r of
175+ Yield x s -> f x (go SPEC s)
176+ Skip s -> go SPEC s
177+ Stop -> final
178+
179+{-# INLINE toList #-}
180+toList :: Monad m => Stream m a -> m [a]
181+toList = foldr (:) []
src/Streamly/Streams/StreamK.hs+84−20
@@ -75,11 +75,15 @@ module Streamly.Streams.StreamK
75 -- ** General Folds
75 -- ** General Folds
76 , foldr
76 , foldr
77 , foldrM
77 , foldrM
78+ , foldrS
79+ , foldrT
78 , foldr1
80 , foldr1
79 , foldl'
81 , foldl'
80 , foldlM'
82 , foldlM'
83+ , foldlS
84+ , foldlT
81− , foldx
85+ , foldx'
82− , foldxM
86+ , foldxM'
83
87
84 -- ** Specialized Folds
88 -- ** Specialized Folds
85 , runStream
89 , runStream
@@ -112,7 +116,7 @@ module Streamly.Streams.StreamK
112 -- * Transformation
116 -- * Transformation
113 -- ** By folding (scans)
117 -- ** By folding (scans)
114 , scanl'
118 , scanl'
115− , scanx
119+ , scanx'
116
120
117 -- ** Filtering
121 -- ** Filtering
118 , filter
122 , filter
@@ -133,6 +137,9 @@ module Streamly.Streams.StreamK
133 -- ** Deleting
137 -- ** Deleting
134 , deleteBy
138 , deleteBy
135
139
140+ -- ** Reordering
141+ , reverse
142+
136 -- ** Map and Filter
143 -- ** Map and Filter
137 , mapMaybe
144 , mapMaybe
138
145
@@ -161,13 +168,14 @@ module Streamly.Streams.StreamK
161 )
168 )
162where
169where
163
170
171+import Control.Monad.Trans (MonadTrans(lift))
164import Control.Monad (void)
172import Control.Monad (void)
165import Control.Monad.Reader.Class (MonadReader(..))
173import Control.Monad.Reader.Class (MonadReader(..))
166import Prelude
174import Prelude
167 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
175 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
168 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
176 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
169 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
177 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
170− foldr1, (!!), replicate)
178+ foldr1, (!!), replicate, reverse)
171import qualified Prelude
179import qualified Prelude
172
180
173import Streamly.SVar
181import Streamly.SVar
@@ -349,15 +357,39 @@ foldr step acc m = go m
349
357
350-- | Lazy right fold with a monadic step function.
358-- | Lazy right fold with a monadic step function.
351{-# INLINE foldrM #-}
359{-# INLINE foldrM #-}
352−foldrM :: (IsStream t, Monad m) => (a -> b -> m b) -> b -> t m a -> m b
360+foldrM :: IsStream t => (a -> m b -> m b) -> m b -> t m a -> m b
353foldrM step acc m = go m
361foldrM step acc m = go m
354 where
362 where
355 go m1 =
363 go m1 =
356− let stop = return acc
364+ let stop = acc
357 single a = step a acc
365 single a = step a acc
358− yieldk a r = go r >>= step a
366+ yieldk a r = step a (go r)
359 in foldStream defState yieldk single stop m1
367 in foldStream defState yieldk single stop m1
360
368
369+-- | Lazy right associative fold to a stream.
370+{-# INLINE foldrS #-}
371+foldrS :: IsStream t => (a -> t m b -> t m b) -> t m b -> t m a -> t m b
372+foldrS step final m = go m
373+ where
374+ go m1 = mkStream $ \st yld sng stp ->
375+ let run x = foldStream st yld sng stp x
376+ stop = run final
377+ single a = run $ step a final
378+ yieldk a r = run $ step a (go r)
379+ in foldStream (adaptState st) yieldk single stop m1
380+
381+-- | Right associative fold to an arbitrary transformer monad.
382+{-# INLINE foldrT #-}
383+foldrT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
384+ => (a -> s m b -> s m b) -> s m b -> t m a -> s m b
385+foldrT step final m = go m
386+ where
387+ go m1 = do
388+ res <- lift $ uncons m1
389+ case res of
390+ Just (h, t) -> step h (go t)
391+ Nothing -> final
392+
361{-# INLINE foldr1 #-}
393{-# INLINE foldr1 #-}
362foldr1 :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> m (Maybe a)
394foldr1 :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> m (Maybe a)
363foldr1 step m = do
395foldr1 step m = do
@@ -378,10 +410,10 @@ foldr1 step m = do
378-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
410-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
379--
411--
380-- Note that the accumulator is always evaluated including the initial value.
412-- Note that the accumulator is always evaluated including the initial value.
381−{-# INLINE foldx #-}
413+{-# INLINE foldx' #-}
382−foldx :: forall t m a b x. (IsStream t, Monad m)
414+foldx' :: forall t m a b x. (IsStream t, Monad m)
383 => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
415 => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b
384−foldx step begin done m = get $ go m begin
416+foldx' step begin done m = get $ go m begin
385 where
417 where
386 {-# NOINLINE get #-}
418 {-# NOINLINE get #-}
387 get :: t m x -> m b
419 get :: t m x -> m b
@@ -408,14 +440,14 @@ foldx step begin done m = get $ go m begin
408-- | Strict left associative fold.
440-- | Strict left associative fold.
409{-# INLINE foldl' #-}
441{-# INLINE foldl' #-}
410foldl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> m b
442foldl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> m b
411−foldl' step begin = foldx step begin id
443+foldl' step begin = foldx' step begin id
412
444
413-- XXX replace the recursive "go" with explicit continuations.
445-- XXX replace the recursive "go" with explicit continuations.
414-- | Like 'foldx', but with a monadic step function.
446-- | Like 'foldx', but with a monadic step function.
415−{-# INLINABLE foldxM #-}
447+{-# INLINABLE foldxM' #-}
416−foldxM :: (IsStream t, Monad m)
448+foldxM' :: (IsStream t, Monad m)
417 => (x -> a -> m x) -> m x -> (x -> m b) -> t m a -> m b
449 => (x -> a -> m x) -> m x -> (x -> m b) -> t m a -> m b
418−foldxM step begin done m = go begin m
450+foldxM' step begin done m = go begin m
419 where
451 where
420 go !acc m1 =
452 go !acc m1 =
421 let stop = acc >>= done
453 let stop = acc >>= done
@@ -426,7 +458,31 @@ foldxM step begin done m = go begin m
426-- | Like 'foldl'' but with a monadic step function.
458-- | Like 'foldl'' but with a monadic step function.
427{-# INLINE foldlM' #-}
459{-# INLINE foldlM' #-}
428foldlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> m b
460foldlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> m b
429−foldlM' step begin = foldxM step (return begin) return
461+foldlM' step begin = foldxM' step (return begin) return
462+
463+-- | Lazy left fold to a stream.
464+{-# INLINE foldlS #-}
465+foldlS :: IsStream t => (t m b -> a -> t m b) -> t m b -> t m a -> t m b
466+foldlS step begin m = go begin m
467+ where
468+ go acc rest = mkStream $ \st yld sng stp ->
469+ let run x = foldStream st yld sng stp x
470+ stop = run acc
471+ single a = run $ step acc a
472+ yieldk a r = run $ go (step acc a) r
473+ in foldStream (adaptState st) yieldk single stop rest
474+
475+-- | Lazy left fold to an arbitrary transformer monad.
476+{-# INLINE foldlT #-}
477+foldlT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
478+ => (s m b -> a -> s m b) -> s m b -> t m a -> s m b
479+foldlT step begin m = go begin m
480+ where
481+ go acc m1 = do
482+ res <- lift $ uncons m1
483+ case res of
484+ Just (h, t) -> go (step acc h) t
485+ Nothing -> acc
430
486
431------------------------------------------------------------------------------
487------------------------------------------------------------------------------
432-- Specialized folds
488-- Specialized folds
@@ -528,7 +584,7 @@ any p m = go m
528-- | Extract the last element of the stream, if any.
584-- | Extract the last element of the stream, if any.
529{-# INLINE last #-}
585{-# INLINE last #-}
530last :: (IsStream t, Monad m) => t m a -> m (Maybe a)
586last :: (IsStream t, Monad m) => t m a -> m (Maybe a)
531−last = foldx (\_ y -> Just y) Nothing id
587+last = foldx' (\_ y -> Just y) Nothing id
532
588
533{-# INLINE minimum #-}
589{-# INLINE minimum #-}
534minimum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
590minimum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
@@ -700,9 +756,9 @@ toStreamK = id
700-- Transformation by folding (Scans)
756-- Transformation by folding (Scans)
701-------------------------------------------------------------------------------
757-------------------------------------------------------------------------------
702
758
703−{-# INLINE scanx #-}
759+{-# INLINE scanx' #-}
704−scanx :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
760+scanx' :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
705−scanx step begin done m =
761+scanx' step begin done m =
706 cons (done begin) $ go m begin
762 cons (done begin) $ go m begin
707 where
763 where
708 go m1 !acc = mkStream $ \st yld sng stp ->
764 go m1 !acc = mkStream $ \st yld sng stp ->
@@ -714,7 +770,7 @@ scanx step begin done m =
714
770
715{-# INLINE scanl' #-}
771{-# INLINE scanl' #-}
716scanl' :: IsStream t => (b -> a -> b) -> b -> t m a -> t m b
772scanl' :: IsStream t => (b -> a -> b) -> b -> t m a -> t m b
717−scanl' step begin = scanx step begin id
773+scanl' step begin = scanx' step begin id
718
774
719-------------------------------------------------------------------------------
775-------------------------------------------------------------------------------
720-- Filtering
776-- Filtering
@@ -846,6 +902,14 @@ deleteBy eq x m = go m
846 else yld a (go r)
902 else yld a (go r)
847 in foldStream st yieldk single stp m1
903 in foldStream st yieldk single stp m1
848
904
905+------------------------------------------------------------------------------
906+-- Reordering
907+------------------------------------------------------------------------------
908+
909+{-# INLINE reverse #-}
910+reverse :: IsStream t => t m a -> t m a
911+reverse = foldlS (flip cons) nil
912+
849-------------------------------------------------------------------------------
913-------------------------------------------------------------------------------
850-- Map and Filter
914-- Map and Filter
851-------------------------------------------------------------------------------
915-------------------------------------------------------------------------------
stack.yaml+5−3
@@ -3,11 +3,13 @@ packages:
3- '.'
3- '.'
4extra-deps:
4extra-deps:
5 - SDL-0.6.6.0
5 - SDL-0.6.6.0
6− - Chart-1.9
6+ - Chart-1.9.1
7− - Chart-diagrams-1.9
7+ - Chart-diagrams-1.9.2
8− - SVGFonts-1.6.0.3
9 - bench-show-0.2.2
8 - bench-show-0.2.2
10
9
10+ - diagrams-postscript-1.4.1@sha256:a39c44f8a93929de88e5c363290d5bb8bc93ba15cd36af5c9ca5b620d51cdf68
11+
12+allow-newer: true
11flags: {}
13flags: {}
12extra-package-dbs: []
14extra-package-dbs: []
13rebuild-ghc-options: true
15rebuild-ghc-options: true
test/Main.hs+23−1
@@ -487,7 +487,7 @@ parallelTests = H.parallel $ do
487 it "takes n from stream of streams" (takeCombined 3 wAsyncly)
487 it "takes n from stream of streams" (takeCombined 3 wAsyncly)
488
488
489 ---------------------------------------------------------------------------
489 ---------------------------------------------------------------------------
490− -- Folds are strict enough
490+ -- Left folds are strict enough
491 ---------------------------------------------------------------------------
491 ---------------------------------------------------------------------------
492
492
493 it "foldx is strict enough" checkFoldxStrictness
493 it "foldx is strict enough" checkFoldxStrictness
@@ -498,6 +498,12 @@ parallelTests = H.parallel $ do
498 it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
498 it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
499 it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
499 it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
500
500
501+ ---------------------------------------------------------------------------
502+ -- Right folds are lazy enough
503+ ---------------------------------------------------------------------------
504+
505+ it "foldrM is lazy enough" checkFoldrLaziness
506+
501 ---------------------------------------------------------------------------
507 ---------------------------------------------------------------------------
502 -- Monadic state snapshot in concurrent tasks
508 -- Monadic state snapshot in concurrent tasks
503 ---------------------------------------------------------------------------
509 ---------------------------------------------------------------------------
@@ -623,6 +629,22 @@ takeCombined n t = do
623 S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
629 S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
624 r `shouldBe` []
630 r `shouldBe` []
625
631
632+checkFoldrLaziness :: IO ()
633+checkFoldrLaziness = do
634+ S.foldrM (\x xs -> if odd x then return True else xs)
635+ (return False) (S.fromList (2:4:5:undefined :: [Int]))
636+ `shouldReturn` True
637+
638+ S.toList (S.foldrS (\x xs -> if odd x then return True else xs)
639+ (return False)
640+ $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
641+ `shouldReturn` [True]
642+
643+ S.toList (S.foldrT (\x xs -> if odd x then return True else xs)
644+ (return False)
645+ $ (S.fromList (2:4:5:undefined) :: SerialT IO Int))
646+ `shouldReturn` [True]
647+
626checkFoldxStrictness :: IO ()
648checkFoldxStrictness :: IO ()
627checkFoldxStrictness = do
649checkFoldxStrictness = do
628 let s = return (1 :: Int) `S.consM` error "failure"
650 let s = return (1 :: Int) `S.consM` error "failure"
test/Prop.hs+4−1
@@ -336,7 +336,8 @@ concurrentFoldrApplication n =
336 -- XXX we should test empty list case as well
336 -- XXX we should test empty list case as well
337 let list = [0..n]
337 let list = [0..n]
338 stream <- run $
338 stream <- run $
339− sourceUnfoldrM1 n |&. S.foldrM (\x xs -> return (x : xs)) []
339+ sourceUnfoldrM1 n |&. S.foldrM (\x xs -> xs >>= return . (x :))
340+ (return [])
340 listEquals (==) stream list
341 listEquals (==) stream list
341
342
342-------------------------------------------------------------------------------
343-------------------------------------------------------------------------------
@@ -550,8 +551,10 @@ eliminationOps constr desc t = do
550 eliminateOp constr (foldl' (+) 0) $ S.foldl' (+) 0 . t
551 eliminateOp constr (foldl' (+) 0) $ S.foldl' (+) 0 . t
551 prop (desc <> " foldl1'") $
552 prop (desc <> " foldl1'") $
552 eliminateOp constr (wrapMaybe $ foldl1' (+)) $ S.foldl1' (+) . t
553 eliminateOp constr (wrapMaybe $ foldl1' (+)) $ S.foldl1' (+) . t
554+#ifdef DEVBUILD
553 prop (desc <> " foldr1") $
555 prop (desc <> " foldr1") $
554 eliminateOp constr (wrapMaybe $ foldr1 (+)) $ S.foldr1 (+) . t
556 eliminateOp constr (wrapMaybe $ foldr1 (+)) $ S.foldr1 (+) . t
557+#endif
555 prop (desc <> " all") $ eliminateOp constr (all even) $ S.all even . t
558 prop (desc <> " all") $ eliminateOp constr (all even) $ S.all even . t
556 prop (desc <> " any") $ eliminateOp constr (any even) $ S.any even . t
559 prop (desc <> " any") $ eliminateOp constr (any even) $ S.any even . t
557 prop (desc <> " and") $ eliminateOp constr (and . fmap (> 0)) $
560 prop (desc <> " and") $ eliminateOp constr (and . fmap (> 0)) $