on master help sign in
Fix right fold, document folds, add generic folds

* Fix the signature of foldrM * Implement some custom folds in terms of foldr * Document folds and scans better * Reorganize the documentation in Prelude * Add foldrS and foldrT for transforming folds * add toRevList * Add benchmarks and tests for the new folds

commit0ce4a6d5adbce5673d820817b2493d52bfa29fbe authorHarendra Kumar <harendra.kumar@gmail.com> dateFri 3 May 2019, 10:35 (7 years ago) parenta80a622
15 files changed+990−395
Changelog.md+20 benchmark/BaseStreams.hs+18−14 benchmark/Linear.hs+42−26 benchmark/LinearOps.hs+27−12 benchmark/StreamDOps.hs+12−6 benchmark/StreamKOps.hs+4−2 src/Streamly/Prelude.hs+498−250 src/Streamly/Streams/Prelude.hs+50−2 src/Streamly/Streams/SVar.hs+1−1 src/Streamly/Streams/StreamD.hs+119−55 src/Streamly/Streams/StreamD/Type.hs+83−2 src/Streamly/Streams/StreamK.hs+84−20 stack.yaml+5−3 test/Main.hs+23−1 test/Prop.hs+4−1
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+
121## 0.6.1
222
323### Bug Fixes
benchmark/BaseStreams.hs+18−14
@@ -52,14 +52,15 @@ main =
5252 -- , benchIO "fromFoldableM" D.sourceFromFoldableM
5353 ]
5454 , bgroup "elimination"
55− [ benchIO "toNull" D.toNull D.sourceUnfoldrM
56− , benchIO "uncons" D.uncons D.sourceUnfoldrM
57− , benchFold "tail" D.tail D.sourceUnfoldrM
55+ [ benchIO "toNull" D.toNull D.sourceUnfoldrM
56+ , benchIO "mapM_" D.mapM_ D.sourceUnfoldrM
57+ , benchIO "uncons" D.uncons D.sourceUnfoldrM
58+ , benchFold "tail" D.tail D.sourceUnfoldrM
5859 , benchIO "nullTail" D.nullTail D.sourceUnfoldrM
5960 , benchIO "headTail" D.headTail D.sourceUnfoldrM
60− , benchFold "toList" K.toList K.sourceUnfoldrM
61− , benchFold "fold" K.foldl K.sourceUnfoldrM
62− , benchFold "last" K.last K.sourceUnfoldrM
61+ , benchFold "toList" D.toList D.sourceUnfoldrM
62+ , benchFold "foldl'" D.foldl D.sourceUnfoldrM
63+ , benchFold "last" D.last D.sourceUnfoldrM
6364 ]
6465 , bgroup "transformation"
6566 [ benchIO "scan" (D.scan 1) D.sourceUnfoldrM
@@ -68,6 +69,8 @@ main =
6869 , benchIO "mapM" (D.mapM 1) D.sourceUnfoldrM
6970 , benchIO "mapMaybe" (D.mapMaybe 1) D.sourceUnfoldrM
7071 , benchIO "mapMaybeM" (D.mapMaybeM 1) D.sourceUnfoldrM
72+ -- This has horrible performance, never finishes
73+ -- , benchIO "foldlS" (D.foldlS 1) D.sourceUnfoldrM
7174 ]
7275 , bgroup "transformationX4"
7376 [ benchIO "scan" (D.scan 4) D.sourceUnfoldrM
@@ -164,16 +167,16 @@ main =
164167 , benchIO "foldMapWithM" K.toNull K.sourceFoldMapWithM
165168 ]
166169 , bgroup "elimination"
167− [ benchIO "toNull" K.toNull K.sourceUnfoldrM
168− , benchIO "mapM_" K.mapM_ K.sourceUnfoldrM
169− , benchIO "uncons" K.uncons K.sourceUnfoldrM
170− , benchFold "init" K.init K.sourceUnfoldrM
171− , benchFold "tail" K.tail K.sourceUnfoldrM
170+ [ benchIO "toNull" K.toNull K.sourceUnfoldrM
171+ , benchIO "mapM_" K.mapM_ K.sourceUnfoldrM
172+ , benchIO "uncons" K.uncons K.sourceUnfoldrM
173+ , benchFold "init" K.init K.sourceUnfoldrM
174+ , benchFold "tail" K.tail K.sourceUnfoldrM
172175 , benchIO "nullTail" K.nullTail K.sourceUnfoldrM
173176 , benchIO "headTail" K.headTail K.sourceUnfoldrM
174− , benchFold "toList" K.toList K.sourceUnfoldrM
175− , benchFold "fold" K.foldl K.sourceUnfoldrM
176− , benchFold "last" K.last K.sourceUnfoldrM
177+ , benchFold "toList" K.toList K.sourceUnfoldrM
178+ , benchFold "foldl'" K.foldl K.sourceUnfoldrM
179+ , benchFold "last" K.last K.sourceUnfoldrM
177180 ]
178181 , bgroup "transformation"
179182 [ benchIO "scan" (K.scan 1) K.sourceUnfoldrM
@@ -181,6 +184,7 @@ main =
181184 , benchIO "fmap" (K.fmap 1) K.sourceUnfoldrM
182185 , benchIO "mapM" (K.mapM 1) K.sourceUnfoldrM
183186 -- , benchIO "concat" K.concat K.sourceUnfoldrM
187+ , benchIO "foldlS" (K.foldlS 1) K.sourceUnfoldrM
184188 ]
185189 , bgroup "transformationX4"
186190 [ benchIO "scan" (K.scan 4) K.sourceUnfoldrM
benchmark/Linear.hs+42−26
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Main
34-- Copyright : (c) 2018 Harendra Kumar
@@ -5,7 +6,7 @@
56-- License : BSD3
67-- Maintainer : harendra.kumar@gmail.com
78
8−import Control.DeepSeq (NFData)
9+import Control.DeepSeq (NFData(..))
910import Data.Functor.Identity (Identity, runIdentity)
1011import System.Random (randomRIO)
1112
@@ -16,6 +17,10 @@ import Streamly
1617import qualified Streamly.Prelude as S
1718import Gauge
1819
20+-------------------------------------------------------------------------------
21+--
22+-------------------------------------------------------------------------------
23+
1924-- We need a monadic bind here to make sure that the function f does not get
2025-- completely optimized out by the compiler in some cases.
2126
@@ -108,25 +113,15 @@ main =
108113 , benchIOSrc serially "foldMapM" Ops.sourceFoldMapM
109114 ]
110115 , 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"
120117 [ bgroup "IO"
121− [ benchIOSink "foldr" Ops.foldrReduce
122− , benchIOSink "foldr1" Ops.foldr1Reduce
118+ [ benchIOSink "foldrM" Ops.foldrMReduce
123119 , benchIOSink "foldl'" Ops.foldl'Reduce
124120 , benchIOSink "foldl1'" Ops.foldl1'Reduce
125121 , benchIOSink "foldlM'" Ops.foldlM'Reduce
126122 ]
127123 , bgroup "Identity"
128− [ benchIdentitySink "foldr" Ops.foldrReduce
129− , benchIdentitySink "foldr1" Ops.foldr1Reduce
124+ [ benchIdentitySink "foldrM" Ops.foldrMReduce
130125 , benchIdentitySink "foldl'" Ops.foldl'Reduce
131126 , benchIdentitySink "foldl1'" Ops.foldl1'Reduce
132127 , benchIdentitySink "foldlM'" Ops.foldlM'Reduce
@@ -135,36 +130,52 @@ main =
135130
136131 , bgroup "build"
137132 [ bgroup "IO"
138− [ benchIOSink "foldr" Ops.foldrBuild
139− , benchIOSink "foldrM" Ops.foldrMBuild
133+ [ benchIOSink "foldrM" Ops.foldrMBuild
140134 , benchIOSink "foldl'" Ops.foldl'Build
141135 , benchIOSink "foldlM'" Ops.foldlM'Build
142136 ]
143137 , bgroup "Identity"
144− [ benchIdentitySink "foldr" Ops.foldrBuild
145− , benchIdentitySink "foldrM" Ops.foldrMBuild
138+ [ benchIdentitySink "foldrM" Ops.foldrMBuild
146139 , benchIdentitySink "foldl'" Ops.foldl'Build
147140 , benchIdentitySink "foldlM'" Ops.foldlM'Build
148141 ]
149142 ]
143+ , benchIOSink "uncons" Ops.uncons
144+ , benchIOSink "toNull" $ Ops.toNull serially
145+ , benchIOSink "mapM_" Ops.mapM_
150146
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
151153 , benchIOSink "last" Ops.last
152− , benchIOSink "length" Ops.length
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
160+ -- , benchIOSink "null" Ops.null
153161 , benchIOSink "elem" Ops.elem
154162 , benchIOSink "notElem" Ops.notElem
155163 , benchIOSink "all" Ops.all
156164 , benchIOSink "any" Ops.any
157165 , benchIOSink "and" Ops.and
158166 , benchIOSink "or" Ops.or
159− , benchIOSink "find" Ops.find
160− , benchIOSink "findIndex" Ops.findIndex
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
167+
168+ , benchIOSink "length" Ops.length
166169 , benchIOSink "sum" Ops.sum
167170 , 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
168179 ]
169180 , bgroup "transformation"
170181 [ benchIOSink "scan" (Ops.scan 1)
@@ -178,6 +189,11 @@ main =
178189 Ops.sequence serially (Ops.sourceUnfoldrMAction n)
179190 , benchIOSink "findIndices" (Ops.findIndices 1)
180191 , 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)
181197 ]
182198 , bgroup "transformationX4"
183199 [ benchIOSink "scan" (Ops.scan 4)
benchmark/LinearOps.hs+27−12
@@ -165,6 +165,7 @@ runStream = S.runStream
165165{-# INLINE toList #-}
166166toList :: Monad m => Stream m Int -> m [Int]
167167
168+{-# INLINE head #-}
168169{-# INLINE last #-}
169170{-# INLINE maximum #-}
170171{-# INLINE minimum #-}
@@ -172,8 +173,7 @@ toList :: Monad m => Stream m Int -> m [Int]
172173{-# INLINE findIndex #-}
173174{-# INLINE elemIndex #-}
174175{-# 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
177177 :: Monad m => Stream m Int -> m (Maybe Int)
178178
179179{-# INLINE minimumBy #-}
@@ -182,19 +182,18 @@ minimumBy, maximumBy :: Monad m => Stream m Int -> m (Maybe Int)
182182
183183{-# INLINE foldl'Reduce #-}
184184{-# INLINE foldlM'Reduce #-}
185−{-# INLINE foldrReduce #-}
185+{-# INLINE foldrMReduce #-}
186186{-# INLINE length #-}
187187{-# INLINE sum #-}
188188{-# INLINE product #-}
189−foldl'Reduce, foldlM'Reduce, foldrReduce, length, sum, product
189+foldl'Reduce, foldlM'Reduce, foldrMReduce, length, sum, product
190190 :: Monad m
191191 => Stream m Int -> m Int
192192
193193{-# INLINE foldl'Build #-}
194194{-# INLINE foldlM'Build #-}
195−{-# INLINE foldrBuild #-}
196195{-# INLINE foldrMBuild #-}
197−foldrBuild, foldrMBuild, foldl'Build, foldlM'Build
196+foldrMBuild, foldl'Build, foldlM'Build
198197 :: Monad m
199198 => Stream m Int -> m [Int]
200199
@@ -202,9 +201,10 @@ foldrBuild, foldrMBuild, foldl'Build, foldlM'Build
202201{-# INLINE any #-}
203202{-# INLINE and #-}
204203{-# INLINE or #-}
204+{-# INLINE null #-}
205205{-# INLINE elem #-}
206206{-# 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
208208
209209{-# INLINE toNull #-}
210210toNull :: Monad m => (t m a -> S.SerialT m a) -> t m a -> m ()
@@ -240,18 +240,22 @@ mapM_ = S.mapM_ (\_ -> return ())
240240
241241toList = S.toList
242242
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 [])
243248foldl'Build = S.foldl' (flip (:)) []
244−foldrBuild = S.foldr (:) []
245249foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
246−foldrMBuild = S.foldrM (\x xs -> return $ x : xs) []
247250
248−foldrReduce = S.foldr (+) 0
249−foldr1Reduce = S.foldr1 (+)
251+foldrMReduce = S.foldrM (\x xs -> xs >>= return . (x +)) (return 0)
250252foldl'Reduce = S.foldl' (+) 0
251253foldl1'Reduce = S.foldl1' (+)
252254foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) 0
253255
254256last = S.last
257+null = S.null
258+head = S.head
255259elem = S.elem maxValue
256260notElem = S.notElem maxValue
257261length = S.length
@@ -323,10 +327,16 @@ composeN' n f =
323327{-# INLINE elemIndices #-}
324328{-# INLINE insertBy #-}
325329{-# INLINE deleteBy #-}
330+{-# INLINE reverse #-}
331+{-# INLINE foldrS #-}
332+{-# INLINE foldrSMap #-}
333+{-# INLINE foldrT #-}
334+{-# INLINE foldrTMap #-}
326335scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
327336 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
328337 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
329− findIndices, elemIndices, insertBy, deleteBy
338+ findIndices, elemIndices, insertBy, deleteBy, reverse,
339+ foldrS, foldrSMap, foldrT, foldrTMap
330340 :: Monad m
331341 => Int -> Stream m Int -> m ()
332342
@@ -374,6 +384,11 @@ findIndices n = composeN n $ S.findIndices (== maxValue)
374384elemIndices n = composeN n $ S.elemIndices maxValue
375385insertBy n = composeN n $ S.insertBy compare maxValue
376386deleteBy 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
377392
378393-------------------------------------------------------------------------------
379394-- Iteration
benchmark/StreamDOps.hs+12−6
@@ -14,8 +14,8 @@ import Control.Monad (when)
1414import Data.Maybe (isJust)
1515import Prelude
1616 (Monad, Int, (+), ($), (.), return, (>), even, (<=), div,
17− subtract, undefined, Maybe(..), not, mapM_, (>>=),
18− maxBound, fmap, odd, (==))
17+ subtract, undefined, Maybe(..), not, (>>=),
18+ maxBound, fmap, odd, (==), flip)
1919import qualified Prelude as P
2020
2121import qualified Streamly.Streams.StreamD as S
@@ -77,6 +77,10 @@ source = sourceUnfoldrM
7777runStream :: Monad m => Stream m a -> m ()
7878runStream = S.runStream
7979
80+{-# INLINE mapM_ #-}
81+mapM_ :: Monad m => Stream m a -> m ()
82+mapM_ = S.mapM_ (\_ -> return ())
83+
8084{-# INLINE toNull #-}
8185toNull :: Monad m => Stream m Int -> m ()
8286toNull = runStream
@@ -96,15 +100,15 @@ uncons s = do
96100
97101{-# INLINE tail #-}
98102tail :: Monad m => Stream m a -> m ()
99−tail s = S.tail s >>= mapM_ tail
103+tail s = S.tail s >>= P.mapM_ tail
100104
101105nullTail s = do
102106 r <- S.null s
103− when (not r) $ S.tail s >>= mapM_ nullTail
107+ when (not r) $ S.tail s >>= P.mapM_ nullTail
104108
105109headTail s = do
106110 h <- S.head s
107− when (isJust h) $ S.tail s >>= mapM_ headTail
111+ when (isJust h) $ S.tail s >>= P.mapM_ headTail
108112
109113{-# INLINE toList #-}
110114toList :: Monad m => Stream m Int -> m [Int]
@@ -156,9 +160,10 @@ composeN n f =
156160{-# INLINE dropWhileTrue #-}
157161{-# INLINE dropWhileMTrue #-}
158162{-# INLINE dropWhileFalse #-}
163+{-# INLINE foldlS #-}
159164scan, map, fmap, mapM, mapMaybe, mapMaybeM, filterEven, filterAllOut,
160165 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
161− dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse
166+ dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse, foldlS
162167 :: Monad m
163168 => Int -> Stream m Int -> m ()
164169
@@ -182,6 +187,7 @@ dropAll n = composeN n $ S.drop maxValue
182187dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
183188dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
184189dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
190+foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
185191
186192-------------------------------------------------------------------------------
187193-- Iteration
benchmark/StreamKOps.hs+4−2
@@ -15,7 +15,7 @@ import Data.Maybe (isJust)
1515import Prelude
1616 (Monad, Int, (+), ($), (.), return, even, (>), (<=), div,
1717 subtract, undefined, Maybe(..), not, (>>=),
18− maxBound)
18+ maxBound, flip)
1919import qualified Prelude as P
2020
2121import qualified Streamly.Streams.StreamK as S
@@ -182,9 +182,10 @@ composeN n f =
182182{-# INLINE dropAll #-}
183183{-# INLINE dropWhileTrue #-}
184184{-# INLINE dropWhileFalse #-}
185+{-# INLINE foldlS #-}
185186scan, map, fmap, filterEven, filterAllOut,
186187 filterAllIn, takeOne, takeAll, takeWhileTrue, dropAll, dropOne,
187− dropWhileTrue, dropWhileFalse
188+ dropWhileTrue, dropWhileFalse, foldlS
188189 :: Monad m
189190 => Int -> Stream m Int -> m ()
190191
@@ -205,6 +206,7 @@ dropOne n = composeN n $ S.drop 1
205206dropAll n = composeN n $ S.drop maxValue
206207dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
207208dropWhileFalse n = composeN n $ S.dropWhile (<= 1)
209+foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
208210
209211-------------------------------------------------------------------------------
210212-- Iteration
src/Streamly/Prelude.hs+498−250
@@ -1,4 +1,5 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE RankNTypes #-}
23{-# LANGUAGE FlexibleContexts #-}
34
45#if __GLASGOW_HASKELL__ >= 800
@@ -109,39 +110,49 @@ module Streamly.Prelude
109110
110111 -- * Elimination
111112
112− -- ** Primitives
113+ -- ** Deconstruction
113114 -- | It is easy to express all the folds in terms of the 'uncons' primitive,
114115 -- however the specific implementations provided later are generally more
115− -- efficient. Folds are inherently serial as each step needs to use the
116− -- result of the previous step.
116+ -- efficient.
117+ --
117118 , uncons
119+ , tail
120+ , init
118121
119− -- ** General Folds
120−-- | Right and left folds.
121−-- As a simple rule, always use lazy right fold for construction and strict
122−-- left fold for reduction. By construction we mean using a constructor as the
123−-- outermost operation in the fold function, by reduction we mean using a
124−-- function as the outermost operation in the fold function.
125−--
126−-- +-----------------------------------+--------------------------------------+
127−-- | Right Fold | Left Fold |
128−-- +===================================+======================================+
129−-- | Construction consumes input | Construction consumes all input, |
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:
122+ -- ** Folding
123+-- | In imperative terms a fold can be considered as a loop over the stream
124+-- that reduces the stream to a single value.
125+-- Left and right folds use a fold function @f@ and an identity element @z@
126+-- (@zero@) to recursively deconstruct a structure and then combine and reduce
127+-- the values or transform and reconstruct a new container.
128+--
129+-- In general, a right fold is suitable for transforming and reconstructing a
130+-- right associated structure (e.g. cons lists and streamly streams) and a left
131+-- fold is suitable for reducing a right associated structure. The behavior of
132+-- right and left folds are described in detail in the individual fold's
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.
145156--
146157-- @
147158-- foldr f z xs = foldl (flip f) z (reverse xs)
@@ -151,42 +162,78 @@ module Streamly.Prelude
151162-- More generally:
152163--
153164-- @
154−-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
155165-- 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
156167-- @
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.
157175
158− , foldr
159− , foldr1
160176 , foldrM
177+ , foldrS
178+ , foldrT
179+
161180 , foldl'
162181 , foldl1'
163182 , foldlM'
164183 , foldx
165184 , foldxM
166185
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.
168191 , 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)
169223 , runN
170224 , runWhile
171225
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.
174227 , (!!)
175228 , head
176− , last
177229 , findM
178230 , find
179231 , lookup
180232 , findIndex
181233 , elemIndex
182234
183− -- ** To Parts
184− -- | Folds that extract selected parts of a stream.
185− , tail
186− , init
187−
188− -- ** To Boolean
189− -- | Folds that summarize the stream to a boolean value.
235+ -- -- ** To Boolean (Partial Folds)
236+ -- -- | Folds that summarize the stream to a boolean value.
190237 , null
191238 , elem
192239 , notElem
@@ -195,46 +242,76 @@ module Streamly.Prelude
195242 , and
196243 , or
197244
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−
219245 -- * Transformation
220246
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+
221264 -- ** Scanning
222− -- | Scans stream all the intermediate reduction steps of the corresponding
223− -- folds. The following equations hold for lists:
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+ --
282+ -- The following equations hold for lists:
224283 --
225284 -- > 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
227286 --
228− -- We do not provide a right associative scan, it can be recovered from a
229− -- 'scanl'' as follows:
287+ -- @
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
230294 --
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+ -- @
232302 --
233− -- Scan is like a stateful map. If we discard the state, we get the map:
303+ -- Left and right scans are duals:
234304 --
235− -- > S.drop 1 $ S.scanl' (\_ x -> f x) z xs == map f xs
236−
237− -- > S.postscanl' (\_ x -> f x) z xs == map f xs
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+ --
310+ -- > map f xs = tail $ scanl (\_ x -> f x) z xs
311+ -- > map f xs = reverse $ head $ scanr (\_ x -> f x) z xs
312+ --
313+ -- > foldl f z xs = last $ scanl f z xs
314+ -- > foldr f z xs = head $ scanr f z xs
238315
239316 , scanl'
240317 , scanlM'
@@ -246,20 +323,18 @@ module Streamly.Prelude
246323 , scanl1M'
247324 , scanx
248325
249− -- ** Mapping
250− -- | Map is a strictly one-to-one transformation of stream elements. It
251− -- cannot add or remove elements from the stream, just transforms them.
252− , Serial.map
253−
254− -- ** Flattening
255− , sequence
256− , mapM
326+ , indexed
327+ , indexedR
257328
258329 -- ** 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.
260333
261334 , filter
262335 , filterM
336+
337+ -- ** Stateful Filters
263338 , take
264339 , takeWhile
265340 , takeWhileM
@@ -269,6 +344,18 @@ module Streamly.Prelude
269344 , deleteBy
270345 , uniq
271346
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+
272359 -- ** Insertion
273360 -- | Insertion adds more elements to the stream.
274361
@@ -278,17 +365,31 @@ module Streamly.Prelude
278365 -- ** Reordering
279366 , reverse
280367
281− -- * Hybrid Operations
282− -- ** Map and Fold
283− , mapM_
284−
285− -- ** Map and Filter
286− , mapMaybe
287− , mapMaybeM
368+ -- ** Nested Producer Loops
369+ -- | Map each element to a stream and then flatten the results into a
370+ -- single stream. In imperative terms, a 'concatMap' corresponds to nested
371+ -- loops. We loop over the outer stream and then for each element of the
372+ -- outer stream an inner stream is generated and then we loop over the
373+ -- inner stream as well to generate a single output stream.
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.
288390
289− -- ** Scan and filter
290− , findIndices
291− , elemIndices
391+ , concatMapM
392+ , concatMap
292393
293394 -- * Multi-Stream Operations
294395 -- | New streams can be constructed by appending, merging or zipping
@@ -299,6 +400,12 @@ module Streamly.Prelude
299400 -- operation.
300401 --
301402 -- @
403+ --
404+ -- -------Stream m a------|-------Stream m a------|=>----Stream m a---
405+ --
406+ -- @
407+ --
408+ -- @
302409 -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
303410 -- [1,2,3,4]
304411 -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
@@ -308,6 +415,14 @@ module Streamly.Prelude
308415 -- ** Merging
309416 -- | Streams form a commutative semigroup under the merge
310417 -- operation.
418+ --
419+ -- @
420+ --
421+ -- -------Stream m a------|
422+ -- |=>----Stream m a---
423+ -- -------Stream m a------|
424+ -- @
425+ --
311426
312427 -- , merge
313428 , mergeBy
@@ -316,20 +431,20 @@ module Streamly.Prelude
316431 , mergeAsyncByM
317432
318433 -- ** Zipping
434+ -- |
435+ -- @
436+ --
437+ -- -------Stream m a------|
438+ -- |=>----Stream m c---
439+ -- -------Stream m b------|
440+ -- @
441+ --
319442 , zipWith
320443 , zipWithM
321444 , Z.zipAsyncWith
322445 , Z.zipAsyncWithM
323446
324− -- Special zips
325− , indexed
326− , indexedR
327−
328− -- ** Flattening
329− , concatMapM
330− , concatMap
331−
332− -- ** Folds
447+ -- ** Stream Level Folds
333448 , eqBy
334449 , cmpBy
335450 , isPrefixOf
@@ -340,12 +455,15 @@ module Streamly.Prelude
340455 , K.once
341456 , each
342457 , scan
458+ , foldr
459+ , foldr1
343460 , foldl
344461 , foldlM
345462 )
346463where
347464
348465import Control.Monad.IO.Class (MonadIO(..))
466+import Control.Monad.Trans (MonadTrans(..))
349467import Data.Maybe (isJust, fromJust)
350468import Prelude
351469 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
@@ -388,6 +506,12 @@ import qualified Streamly.Streams.Serial as Serial
388506-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
389507-- the head of the stream and @ma@ its tail.
390508--
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+--
391515-- @since 0.1.0
392516{-# INLINE uncons #-}
393517uncons :: (IsStream t, Monad m) => SerialT m a -> m (Maybe (a, t m a))
@@ -720,92 +844,158 @@ fromHandle h = go
720844-- Elimination by Folding
721845------------------------------------------------------------------------------
722846
723−-- | Lazy right fold with a monadic step function. For example, to fold a
724−-- stream into a list:
847+-- | Right associative/pull fold.
725848--
726−-- @
727−-- >> S.foldrM (\\x xs -> return (x : xs)) [] $ fromList [1,2,3]
728−-- [1,2,3]
729−-- @
849+-- Example, determine if any element is 'odd' in a stream:
730850--
731−-- @since 0.2.0
732−{-# INLINE foldrM #-}
733−foldrM :: Monad m => (a -> b -> m b) -> b -> SerialT m a -> m b
734−foldrM = P.foldrM
735−
736−-- | Lazy right associative fold.
851+-- >>> S.foldrM (\x xs -> if odd x then return True else xs) (return False) $ S.fromList (2:4:5:undefined)
852+-- > True
737853--
738−-- For lists a @foldr@ looks like:
854+-- Let's take a closer look at the @foldr@ definition for lists, as given
855+-- earlier:
739856--
740857-- @
741−-- foldr f z [] = z
742858-- foldr f z (x:xs) = x \`f` foldr f z xs
743859-- @
744860--
745−-- The recursive expression is the second argument of the fold step `f`.
746−-- Therefore, the evaluation of the recursive call depends on `f`. It can
747−-- terminate recursion by not inspecting the second argument based on a
748−-- condition. When expanded fully, it results in the following right associated
749−-- expression:
861+-- @foldr@ invokes the fold step function @f@ as @f x (foldr f z xs)@. At each
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@
872+-- can terminate recursion by not evaluating the @remaining@ part:
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
882+-- associated expression/:
750883--
751884-- @
752885-- foldr f z xs == x1 \`f` (x2 \`f` ...(xn \`f` z))
753886-- @
754887--
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,
761889--
762−-- @
763−-- > S.foldr (:) [] $ S.fromList [1,2,3,4]
764−-- [1,2,3,4]
765−-- @
890+-- > f x remaining = x + remaining
891+--
892+-- With this definition, @foldr f 0 [1..1000]@, would recurse completely until
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.
766948--
767−-- When `f` is a function strict in its second argument, the right side of the
768−-- expression gets evaluated as follows:
949+-- 'foldrS' can be used to perform stateless stream to stream transformations
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.
769953--
770−-- @
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−-- @
954+-- Find if any element in the stream is 'True':
777955--
778−-- In @foldl'@ we have both the arguments of `f` available at each step,
779−-- therefore, each step can be reduced immediately. However, in @foldr@ the
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:
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)
957+-- > [True]
784958--
785−-- @
786−-- > S.foldr (+) 0 $ S.fromList [1,2,3,4]
787−-- 10
788−-- @
959+-- Map (+2) on odd elements and filter out the even elements:
789960--
790−-- When the underlying monad @m@ is strict (e.g. IO), then @foldr@ ends up
791−-- evaluating all of its input because of strict evaluation of the recursive
792−-- call:
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)
962+-- > [3,5,7]
793963--
794−-- >> S.foldr (\_ _ -> []) [] $ S.fromList (1:undefined)
795−-- >*** Exception: Prelude.undefined
964+-- 'foldrM' can also be represented in terms of 'foldrS', however, the former
965+-- is much more efficient:
796966--
797−-- In a lazy monad, we can consume the input lazily, and terminate the fold
798−-- by conditionally not inspecting the recursive expression.
967+-- > foldrM f z s = runIdentityT $ foldrS (\x xs -> lift $ f x (runIdentityT xs)) (lift z) s
799968--
800−-- >> runIdentity $ S.foldr (\x rest -> if x == 3 then [] else x : rest) [] $ S.fromList (4:1:3:undefined)
801−-- >[4,1]
969+-- @since 0.7.0
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
802980--
803−-- The arguments to the folding function (@a -> b -> b@) are in the head and
804−-- tail order of the output, @a@ is the head and @b@ is the tail. Remember, in
805−-- a right fold the zero is on the right, it is the tail end.
981+-- 'foldrT' can be used to translate streamly streams to other transformer
982+-- monads e.g. to a different streaming type.
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.
806995--
807996-- @since 0.1.0
808997{-# INLINE foldr #-}
998+{-# DEPRECATED foldr "This is unnecessarily strict for strict monads. Use foldrM instead." #-}
809999foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
8101000foldr = P.foldr
8111001
@@ -817,6 +1007,7 @@ foldr = P.foldr
8171007--
8181008-- @since 0.5.0
8191009{-# INLINE foldr1 #-}
1010+{-# DEPRECATED foldr1 "This is unnecessarily strict for strict monads. Use foldrM instead." #-}
8201011foldr1 :: Monad m => (a -> a -> a) -> SerialT m a -> m (Maybe a)
8211012foldr1 f m = S.foldr1 f (toStreamS m)
8221013
@@ -828,48 +1019,59 @@ foldr1 f m = S.foldr1 f (toStreamS m)
8281019-- @since 0.2.0
8291020{-# INLINE foldx #-}
8301021foldx :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
831−foldx = K.foldx
1022+foldx = P.foldlx'
8321023
8331024-- |
8341025-- @since 0.1.0
8351026{-# DEPRECATED foldl "Please use foldx instead." #-}
8361027foldl :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
837−foldl = foldx
1028+foldl = P.foldlx'
8381029
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.
8401035--
841−-- For lists a @foldl@ looks like:
1036+-- Let's take a closer look at the @foldl@ definition for lists given above:
8421037--
8431038-- @
844−-- foldl f z [] = z
8451039-- foldl f z (x:xs) = foldl f (z \`f` x) xs
8461040-- @
8471041--
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+--
8481051-- The recursive call at the head of the output expression is bound to be
849−-- evaluated until recursion terminates,
850−-- /deconstructing the whole input container/ and building the following left
851−-- associated expression:
1052+-- evaluated until recursion terminates, therefore, a left fold always
1053+-- /consumes the whole input container/. The following would result in an
1054+-- error, even though the fold is not using the values at all:
8521055--
853−-- @
854−-- foldl f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
855−-- @
1056+-- >> foldl' (\_ _ -> 0) 0 (1:undefined)
1057+-- > *** Exception: Prelude.undefined
8561058--
857−-- When `f` is a constructor, we can see that the first deconstruction of this
858−-- expression would be the recursive expression on the left and `xn` on the
859−-- right. Therefore, it can access the input elements only in the reverse
860−-- (LIFO) order. For example:
1059+-- As @foldl@ recurses, it builds the left associated expression shown below.
1060+-- Notice, the order of the arguments to the step function @f b a@. It follows
1061+-- the left associative recursive expression generated by expanding @b \`f` a@.
8611062--
8621063-- @
863−-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
864−-- [4,3,2,1]
1064+-- foldl f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
8651065-- @
8661066--
1067+--
8671068-- The strict left fold @foldl'@ forces the reduction of its argument @z \`f`
8681069-- x@ before using it, therefore it never builds the whole expression in
8691070-- memory. Thus, @z \`f` x1@ would get reduced to @z1@ and then @z1 \`f` x2@
8701071-- 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
872−-- further help to use a strict data structure as accumulator. For example:
1072+-- from left to right as it recurses, consuming the input in FIFO order. Try
1073+-- out @foldl' (+) 0 (map (\\x -> trace (show x) x) [1..10])@ to see how it
1074+-- works. For example:
8731075--
8741076-- @
8751077-- > S.foldl' (+) 0 $ S.fromList [1,2,3,4]
@@ -877,36 +1079,44 @@ foldl = foldx
8771079-- @
8781080--
8791081-- @
880−-- 0 + 1
881−-- (0 + 1) + 2
882−-- ((0 + 1) + 2) + 3
883−-- (((0 + 1) + 2) + 3) + 4
1082+-- 0 + 1 = 1
1083+-- 1 + 2 = 3
1084+-- 3 + 3 = 6
1085+-- 6 + 4 = 10
8841086-- @
8851087--
886−-- @foldl@ strictly deconstructs the whole input container irrespective of
887−-- whether it needs it or not:
1088+-- However, @foldl'@ evaluates the accumulator only to WHNF. It may further
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.
8881092--
889−-- >> S.foldl' (\acc x -> if x == 3 then acc else x : acc) [] $ S.fromList (4:1:3:undefined)
890−-- >*** Exception: Prelude.undefined
1093+-- A left fold can also build a new structure instead of reducing one if a
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:
8911099--
892−-- However, evaluation of the items contained in the input container is lazy as
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]
896−-- >[4,1]
1100+-- @
1101+-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
1102+-- [4,3,2,1]
1103+-- @
8971104--
898−-- To perform a left fold without consuming all the input one can use @scanl@
899−-- to stream the intermediate results of the fold and use them lazily.
1105+-- A left fold is a push fold. The producer pushes its contents to the step
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.
9001110--
901−-- In stateful or event-driven programming, we can consider @z@ as the initial
902−-- state and the stream being folded as a stream of events, thus @foldl'@
903−-- processes all the events in the stream updating the state on each event and
904−-- then ultimately returning the final state.
1111+-- In general, a strict left fold is a reducing fold, whereas a right fold is a
1112+-- constructing fold. A strict left fold reduces in a FIFO order whereas it
1113+-- constructs in a LIFO order, and vice-versa for the right fold. See the
1114+-- documentation of 'foldrM' for a discussion on where a left or right fold is
1115+-- suitable.
9051116--
906−-- The arguments to the folding function (@b -> a -> b@) are in the head and
907−-- tail order of the output expression, @b@ is the head and @a@ is the tail.
908−-- Remember, in a left fold the zero is on the left, at the head of the
909−-- expression.
1117+-- To perform a left fold lazily without having to consume all the input one
1118+-- can use @scanl@ to stream the intermediate results of the fold and consume
1119+-- the resulting stream lazily.
9101120--
9111121-- @since 0.2.0
9121122{-# INLINE foldl' #-}
@@ -932,13 +1142,13 @@ foldl1' step m = do
9321142-- @since 0.2.0
9331143{-# INLINE foldxM #-}
9341144foldxM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> SerialT m a -> m b
935−foldxM = K.foldxM
1145+foldxM = P.foldlMx'
9361146
9371147-- |
9381148-- @since 0.1.0
9391149{-# DEPRECATED foldlM "Please use foldxM instead." #-}
9401150foldlM :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> SerialT m a -> m b
941−foldlM = foldxM
1151+foldlM = P.foldlMx'
9421152
9431153-- | Like 'foldl'' but with a monadic step function.
9441154--
@@ -951,7 +1161,10 @@ foldlM' step begin m = S.foldlM' step begin $ toStreamS m
9511161-- Specialized folds
9521162------------------------------------------------------------------------------
9531163
954−-- | Run a stream, discarding the results. By default it interprets the stream
1164+-- |
1165+-- > runStream = mapM_ (\_ -> return ())
1166+--
1167+-- Run a stream, discarding the results. By default it interprets the stream
9551168-- as 'SerialT', to run other types of streams use the type adapting
9561169-- combinators for example @runStream . 'asyncly'@.
9571170--
@@ -996,7 +1209,10 @@ null = K.null
9961209head :: Monad m => SerialT m a -> m (Maybe a)
9971210head = K.head
9981211
999−-- | Extract all but the first element of the stream, if any.
1212+-- |
1213+-- > tail = fmap (fmap snd) . uncons
1214+--
1215+-- Extract all but the first element of the stream, if any.
10001216--
10011217-- @since 0.1.1
10021218{-# INLINE tail #-}
@@ -1190,24 +1406,6 @@ elemIndices a = findIndices (==a)
11901406elemIndex :: (Monad m, Eq a) => a -> SerialT m a -> m (Maybe Int)
11911407elemIndex a = findIndex (== a)
11921408
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−
12111409------------------------------------------------------------------------------
12121410-- Substreams
12131411------------------------------------------------------------------------------
@@ -1256,8 +1454,12 @@ stripPrefix m1 m2 = fmap fromStreamD <$>
12561454------------------------------------------------------------------------------
12571455
12581456-- XXX this can utilize parallel mapping if we implement it as runStream . mapM
1259−-- | Apply a monadic action to each element of the stream and discard the
1260−-- output of the action.
1457+-- |
1458+-- > mapM_ = runStream . mapM
1459+--
1460+-- Apply a monadic action to each element of the stream and discard the output
1461+-- of the action. This is not really a pure transformation operation but a
1462+-- transformation followed by fold.
12611463--
12621464-- @since 0.1.0
12631465{-# INLINE mapM_ #-}
@@ -1273,13 +1475,27 @@ mapM_ f m = S.mapM_ f $ toStreamS m
12731475-- toList = S.foldr (:) []
12741476-- @
12751477--
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.
12771481--
12781482-- @since 0.1.0
12791483{-# INLINE toList #-}
12801484toList :: Monad m => SerialT m a -> m [a]
12811485toList = P.toList
12821486
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+
12831499-- |
12841500-- @
12851501-- toHandle h = S.mapM_ $ hPutStrLn h
@@ -1301,6 +1517,16 @@ toHandle h m = go m
13011517-- Transformation by Folding (Scans)
13021518------------------------------------------------------------------------------
13031519
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+--
13041530-- | Strict left scan with an extraction function. Like 'scanl'', but applies a
13051531-- user supplied extraction function (the third argument) at each step. This is
13061532-- designed to work with the @foldl@ library. The suffix @x@ is a mnemonic for
@@ -1309,7 +1535,7 @@ toHandle h m = go m
13091535-- @since 0.2.0
13101536{-# INLINE scanx #-}
13111537scanx :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
1312−scanx = K.scanx
1538+scanx = K.scanx'
13131539
13141540-- |
13151541-- @since 0.1.1
@@ -1325,7 +1551,8 @@ scan = scanx
13251551scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
13261552scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
13271553
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.
13291556--
13301557-- @
13311558-- > 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
13461573-- thus modularizing the stream processing. This can be useful in
13471574-- stateful or event-driven programming.
13481575--
1349−-- Consider the following example, computing the sum and the product of the
1350−-- elements in a stream in one go using a @foldl'@:
1576+-- Consider the following monolothic example, computing the sum and the product
1577+-- of the elements in a stream in one go using a @foldl'@:
13511578--
13521579-- @
13531580-- > S.foldl' (\\(s, p) x -> (s + x, p * x)) (0,1) $ S.fromList \[1,2,3,4]
13541581-- (10,24)
13551582-- @
13561583--
1357−-- Using @scanl'@ we can compute the sum in the first stage and pass it down to
1358−-- the next stage for computing the product:
1584+-- Using @scanl'@ we can make it modular by computing the sum in the first
1585+-- stage and passing it down to the next stage for computing the product:
13591586--
13601587-- @
13611588-- > S.foldl' (\\(_, p) (s, x) -> (s, p * x)) (0,1)
@@ -1609,19 +1836,16 @@ mapMaybeMSerial f m = fromStreamD $ D.mapMaybeM f $ toStreamD m
16091836-- XXX to scale this we need to use a slab allocated array backed
16101837-- representation for temporary storage.
16111838--
1612−-- | Returns the elements of the stream in reverse order.
1613−-- The stream must be finite.
1839+-- |
1840+-- > reverse = S.foldlT (flip S.cons) S.nil
1841+--
1842+-- Returns the elements of the stream in reverse order. The stream must be
1843+-- finite.
16141844--
16151845-- @since 0.1.1
1616−reverse :: (IsStream t) => t m a -> t m a
1617−reverse m = go K.nil m
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
1846+{-# INLINE reverse #-}
1847+reverse :: (IsStream t, Monad m) => t m a -> t m a
1848+reverse s = fromStreamS $ S.reverse $ toStreamS s
16251849
16261850------------------------------------------------------------------------------
16271851-- Transformation by Inserting
@@ -1682,9 +1906,10 @@ deleteBy cmp x m = fromStreamS $ S.deleteBy cmp x (toStreamS m)
16821906------------------------------------------------------------------------------
16831907
16841908-- |
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)
16861911--
1687−-- Pair each element in a stream with its index.
1912+-- Pair each element in a stream with its index, starting from index 0.
16881913--
16891914-- @
16901915-- > S.toList $ S.indexed $ S.fromList "hello"
@@ -1697,14 +1922,15 @@ indexed :: (IsStream t, Monad m) => t m a -> t m (Int, a)
16971922indexed = fromStreamD . D.indexed . toStreamD
16981923
16991924-- |
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))
17011927--
17021928-- Pair each element in a stream with its index, starting from the
17031929-- given index @n@ and counting down.
17041930--
17051931-- @
17061932-- > 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')]
17081934-- @
17091935--
17101936-- @since 0.6.0
@@ -1847,3 +2073,25 @@ mergeAsyncByM f m1 m2 = K.mkStream $ \st stp sng yld -> do
18472073 ma <- mkAsync' st m1
18482074 mb <- mkAsync' st m2
18492075 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
3131
3232 -- * Fold operations
3333 , foldrM
34+ , foldrMx
3435 , foldr
36+
37+ , foldlx'
38+ , foldlMx'
3539 , foldl'
3640
41+ -- Lazy left folds are useful only for reversing the stream
42+ , foldlS
43+ , foldlT
44+
3745 -- * Zip style operations
3846 , eqBy
3947 , cmpBy
@@ -45,6 +53,7 @@ module Streamly.Streams.Prelude
4553 )
4654where
4755
56+import Control.Monad.Trans (MonadTrans(..))
4857import Prelude hiding (foldr)
4958import qualified Prelude
5059
@@ -112,12 +121,36 @@ toList m = S.toList $ toStreamS m
112121------------------------------------------------------------------------------
113122
114123{-# 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
116125foldrM step acc m = S.foldrM step acc $ toStreamS m
117126
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+
118132{-# INLINE foldr #-}
119133foldr :: (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
121154
122155-- | Strict left associative fold.
123156--
@@ -126,6 +159,21 @@ foldr f = foldrM (\a b -> return (f a b))
126159foldl' :: (Monad m, IsStream t) => (b -> a -> b) -> b -> t m a -> m b
127160foldl' step begin m = S.foldl' step begin $ toStreamS m
128161
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+
129177------------------------------------------------------------------------------
130178-- Comparison
131179------------------------------------------------------------------------------
src/Streamly/Streams/SVar.hs+1−1
@@ -28,7 +28,7 @@ import Streamly.Time.Clock (Clock(Monotonic), getTime)
2828import System.Mem (performMajorGC)
2929
3030import Streamly.SVar
31−import Streamly.Streams.StreamK
31+import Streamly.Streams.StreamK hiding (reverse)
3232
3333printSVar :: SVar t m a -> String -> IO ()
3434printSVar sv how = do
src/Streamly/Streams/StreamD.hs+119−55
@@ -6,6 +6,8 @@
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE MultiParamTypeClasses #-}
88{-# LANGUAGE PatternSynonyms #-}
9+{-# LANGUAGE RecordWildCards #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
911{-# LANGUAGE ViewPatterns #-}
1012{-# LANGUAGE RankNTypes #-}
1113
@@ -86,11 +88,20 @@ module Streamly.Streams.StreamD
8688
8789 -- * Elimination
8890 -- ** General Folds
89− , foldr
91+ , foldrT
9092 , foldrM
93+ , foldrMx
94+ , foldr
9195 , foldr1
96+
9297 , foldl'
9398 , foldlM'
99+ , foldlS
100+ , foldlT
101+ , reverse
102+
103+ , foldlx'
104+ , foldlMx'
94105
95106 -- ** Specialized Folds
96107 , runStream
@@ -111,6 +122,8 @@ module Streamly.Streams.StreamD
111122 , findM
112123 , find
113124 , (!!)
125+
126+ -- ** Flattening nested streams
114127 , concatMapM
115128 , concatMap
116129
@@ -125,6 +138,7 @@ module Streamly.Streams.StreamD
125138 -- ** Conversions
126139 -- | Transform a stream into another type.
127140 , toList
141+ , toRevList
128142 , toStreamK
129143 , toStreamD
130144
@@ -191,13 +205,15 @@ module Streamly.Streams.StreamD
191205 )
192206where
193207
208+import Control.Monad.Trans (MonadTrans(lift))
194209import Data.Maybe (fromJust, isJust)
195−import GHC.Types ( SPEC(..) )
210+import GHC.Types (SPEC(..))
196211import Prelude
197212 hiding (map, mapM, mapM_, repeat, foldr, last, take, filter,
198213 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
199214 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
200− (!!), scanl, scanl1, concatMap, replicate, enumFromTo)
215+ (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
216+ reverse)
201217
202218import Streamly.SVar (MonadAsync, defState, adaptState)
203219
@@ -508,20 +524,9 @@ toStreamD = fromStreamK . K.toStream
508524-- Elimination by Folds
509525------------------------------------------------------------------------------
510526
511−{-# INLINE_NORMAL foldrM #-}
512−foldrM :: Monad m => (a -> b -> m b) -> b -> Stream m a -> m b
513−foldrM f z (Stream step state) = go SPEC state
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))
527+------------------------------------------------------------------------------
528+-- Right Folds
529+------------------------------------------------------------------------------
525530
526531{-# INLINE_NORMAL foldr1 #-}
527532foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)
@@ -531,6 +536,32 @@ foldr1 f m = do
531536 Nothing -> return Nothing
532537 Just (h, t) -> fmap Just (foldr f h t)
533538
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'
534565{-# INLINE_NORMAL foldlM' #-}
535566foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
536567foldlM' fstep begin (Stream step state) = go SPEC begin state
@@ -544,6 +575,40 @@ foldlM' fstep begin (Stream step state) = go SPEC begin state
544575 Skip s -> go SPEC acc s
545576 Stop -> return acc
546577
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+
547612{-# INLINE foldl' #-}
548613foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
549614foldl' fstep = foldlM' (\b a -> return (fstep b a))
@@ -555,6 +620,7 @@ foldl' fstep = foldlM' (\b a -> return (fstep b a))
555620-- | Run a streaming composition, discard the results.
556621{-# INLINE_LATE runStream #-}
557622runStream :: Monad m => Stream m a -> m ()
623+-- runStream m = foldrM (\x xs -> x `seq` xs) (return ()) m
558624runStream (Stream step state) = go SPEC state
559625 where
560626 go !_ st = do
@@ -566,26 +632,12 @@ runStream (Stream step state) = go SPEC state
566632
567633{-# INLINE_NORMAL null #-}
568634null :: 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
575− Skip s -> go s
576− Stop -> return True
635+null m = foldrM (\_ _ -> return False) (return True) m
577636
578637-- XXX SPEC?
579638{-# INLINE_NORMAL head #-}
580639head :: 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
589641
590642-- Does not fuse, has the same performance as the StreamK version.
591643{-# INLINE_NORMAL tail #-}
@@ -606,6 +658,7 @@ last = foldl' (\_ y -> Just y) Nothing
606658
607659{-# INLINE_NORMAL elem #-}
608660elem :: (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
609662elem e (Stream step state) = go state
610663 where
611664 go st = do
@@ -623,6 +676,7 @@ notElem e s = fmap not (elem e s)
623676
624677{-# INLINE_NORMAL all #-}
625678all :: 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
626680all p (Stream step state) = go state
627681 where
628682 go st = do
@@ -636,6 +690,7 @@ all p (Stream step state) = go state
636690
637691{-# INLINE_NORMAL any #-}
638692any :: 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
639694any p (Stream step state) = go state
640695 where
641696 go st = do
@@ -738,27 +793,13 @@ minimumBy cmp (Stream step state) = go Nothing state
738793
739794{-# INLINE_NORMAL lookup #-}
740795lookup :: (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
747− Skip s -> go s
748− Stop -> return Nothing
796+lookup e m = foldrM (\(a, b) xs -> if e == a then return (Just b) else xs)
797+ (return Nothing) m
749798
750799{-# INLINE_NORMAL findM #-}
751800findM :: 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
760− Skip s -> go SPEC s
761− Stop -> return Nothing
801+findM p m = foldrM (\x xs -> p x >>= \r -> if r then return (Just x) else xs)
802+ (return Nothing) m
762803
763804{-# INLINE find #-}
764805find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)
@@ -776,6 +817,33 @@ findIndices p (Stream step state) = Stream step' (state, 0)
776817 Skip s -> Skip (s, i+1)
777818 Stop -> Stop
778819
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+
779847{-# INLINE_NORMAL concatMapM #-}
780848concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
781849concatMapM f (Stream step state) = Stream step' (Left state)
@@ -888,10 +956,6 @@ mapM_ m = runStream . mapM m
888956-- Converting folds
889957------------------------------------------------------------------------------
890958
891−{-# INLINE toList #-}
892−toList :: Monad m => Stream m a -> m [a]
893−toList = foldr (:) []
894−
895959-- Convert a direct stream to and from CPS encoded stream
896960{-# INLINE_LATE toStreamK #-}
897961toStreamK :: 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
3434#endif
3535 , map
3636 , mapM
37+ , foldrT
38+ , foldrM
39+ , foldrMx
40+ , foldr
41+ , toList
3742 )
3843where
3944
40−import Streamly.SVar (State(..), adaptState)
45+import Control.Applicative (liftA2)
46+import Control.Monad.Trans (lift, MonadTrans)
47+import GHC.Types (SPEC(..))
48+import Prelude hiding (map, mapM, foldr)
49+import Streamly.SVar (State(..), adaptState, defState)
50+
4151import qualified Streamly.Streams.StreamK as K
42−import Prelude hiding (map, mapM)
4352
4453------------------------------------------------------------------------------
4554-- The direct style stream type
@@ -98,3 +107,75 @@ map f = mapM (return . f)
98107instance Monad m => Functor (Stream m) where
99108 {-# INLINE fmap #-}
100109 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
7575 -- ** General Folds
7676 , foldr
7777 , foldrM
78+ , foldrS
79+ , foldrT
7880 , foldr1
7981 , foldl'
8082 , foldlM'
81− , foldx
82− , foldxM
83+ , foldlS
84+ , foldlT
85+ , foldx'
86+ , foldxM'
8387
8488 -- ** Specialized Folds
8589 , runStream
@@ -112,7 +116,7 @@ module Streamly.Streams.StreamK
112116 -- * Transformation
113117 -- ** By folding (scans)
114118 , scanl'
115− , scanx
119+ , scanx'
116120
117121 -- ** Filtering
118122 , filter
@@ -133,6 +137,9 @@ module Streamly.Streams.StreamK
133137 -- ** Deleting
134138 , deleteBy
135139
140+ -- ** Reordering
141+ , reverse
142+
136143 -- ** Map and Filter
137144 , mapMaybe
138145
@@ -161,13 +168,14 @@ module Streamly.Streams.StreamK
161168 )
162169where
163170
171+import Control.Monad.Trans (MonadTrans(lift))
164172import Control.Monad (void)
165173import Control.Monad.Reader.Class (MonadReader(..))
166174import Prelude
167175 hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,
168176 take, filter, all, any, takeWhile, drop, dropWhile, minimum,
169177 maximum, elem, notElem, null, head, tail, init, zipWith, lookup,
170− foldr1, (!!), replicate)
178+ foldr1, (!!), replicate, reverse)
171179import qualified Prelude
172180
173181import Streamly.SVar
@@ -349,15 +357,39 @@ foldr step acc m = go m
349357
350358-- | Lazy right fold with a monadic step function.
351359{-# 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
353361foldrM step acc m = go m
354362 where
355363 go m1 =
356− let stop = return acc
364+ let stop = acc
357365 single a = step a acc
358− yieldk a r = go r >>= step a
366+ yieldk a r = step a (go r)
359367 in foldStream defState yieldk single stop m1
360368
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+
361393{-# INLINE foldr1 #-}
362394foldr1 :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> m (Maybe a)
363395foldr1 step m = do
@@ -378,10 +410,10 @@ foldr1 step m = do
378410-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
379411--
380412-- Note that the accumulator is always evaluated including the initial value.
381−{-# INLINE foldx #-}
382−foldx :: forall t m a b x. (IsStream t, Monad m)
413+{-# INLINE foldx' #-}
414+foldx' :: forall t m a b x. (IsStream t, Monad m)
383415 => (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
385417 where
386418 {-# NOINLINE get #-}
387419 get :: t m x -> m b
@@ -408,14 +440,14 @@ foldx step begin done m = get $ go m begin
408440-- | Strict left associative fold.
409441{-# INLINE foldl' #-}
410442foldl' :: (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
412444
413445-- XXX replace the recursive "go" with explicit continuations.
414446-- | Like 'foldx', but with a monadic step function.
415−{-# INLINABLE foldxM #-}
416−foldxM :: (IsStream t, Monad m)
447+{-# INLINABLE foldxM' #-}
448+foldxM' :: (IsStream t, Monad m)
417449 => (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
419451 where
420452 go !acc m1 =
421453 let stop = acc >>= done
@@ -426,7 +458,31 @@ foldxM step begin done m = go begin m
426458-- | Like 'foldl'' but with a monadic step function.
427459{-# INLINE foldlM' #-}
428460foldlM' :: (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
430486
431487------------------------------------------------------------------------------
432488-- Specialized folds
@@ -528,7 +584,7 @@ any p m = go m
528584-- | Extract the last element of the stream, if any.
529585{-# INLINE last #-}
530586last :: (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
532588
533589{-# INLINE minimum #-}
534590minimum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
@@ -700,9 +756,9 @@ toStreamK = id
700756-- Transformation by folding (Scans)
701757-------------------------------------------------------------------------------
702758
703−{-# INLINE scanx #-}
704−scanx :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
705−scanx step begin done m =
759+{-# INLINE scanx' #-}
760+scanx' :: IsStream t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
761+scanx' step begin done m =
706762 cons (done begin) $ go m begin
707763 where
708764 go m1 !acc = mkStream $ \st yld sng stp ->
@@ -714,7 +770,7 @@ scanx step begin done m =
714770
715771{-# INLINE scanl' #-}
716772scanl' :: 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
718774
719775-------------------------------------------------------------------------------
720776-- Filtering
@@ -846,6 +902,14 @@ deleteBy eq x m = go m
846902 else yld a (go r)
847903 in foldStream st yieldk single stp m1
848904
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+
849913-------------------------------------------------------------------------------
850914-- Map and Filter
851915-------------------------------------------------------------------------------
stack.yaml+5−3
@@ -3,11 +3,13 @@ packages:
33- '.'
44extra-deps:
55 - SDL-0.6.6.0
6− - Chart-1.9
7− - Chart-diagrams-1.9
8− - SVGFonts-1.6.0.3
6+ - Chart-1.9.1
7+ - Chart-diagrams-1.9.2
98 - bench-show-0.2.2
109
10+ - diagrams-postscript-1.4.1@sha256:a39c44f8a93929de88e5c363290d5bb8bc93ba15cd36af5c9ca5b620d51cdf68
11+
12+allow-newer: true
1113flags: {}
1214extra-package-dbs: []
1315rebuild-ghc-options: true
test/Main.hs+23−1
@@ -487,7 +487,7 @@ parallelTests = H.parallel $ do
487487 it "takes n from stream of streams" (takeCombined 3 wAsyncly)
488488
489489 ---------------------------------------------------------------------------
490− -- Folds are strict enough
490+ -- Left folds are strict enough
491491 ---------------------------------------------------------------------------
492492
493493 it "foldx is strict enough" checkFoldxStrictness
@@ -498,6 +498,12 @@ parallelTests = H.parallel $ do
498498 it "foldlM' is strict enough" (checkFoldMStrictness foldlM'StrictCheck)
499499 it "scanlM' is strict enough" (checkScanlMStrictness scanlM'StrictCheck)
500500
501+ ---------------------------------------------------------------------------
502+ -- Right folds are lazy enough
503+ ---------------------------------------------------------------------------
504+
505+ it "foldrM is lazy enough" checkFoldrLaziness
506+
501507 ---------------------------------------------------------------------------
502508 -- Monadic state snapshot in concurrent tasks
503509 ---------------------------------------------------------------------------
@@ -623,6 +629,22 @@ takeCombined n t = do
623629 S.take n (constr ([] :: [Int]) <> constr ([] :: [Int]))
624630 r `shouldBe` []
625631
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+
626648checkFoldxStrictness :: IO ()
627649checkFoldxStrictness = do
628650 let s = return (1 :: Int) `S.consM` error "failure"
test/Prop.hs+4−1
@@ -336,7 +336,8 @@ concurrentFoldrApplication n =
336336 -- XXX we should test empty list case as well
337337 let list = [0..n]
338338 stream <- run $
339− sourceUnfoldrM1 n |&. S.foldrM (\x xs -> return (x : xs)) []
339+ sourceUnfoldrM1 n |&. S.foldrM (\x xs -> xs >>= return . (x :))
340+ (return [])
340341 listEquals (==) stream list
341342
342343-------------------------------------------------------------------------------
@@ -550,8 +551,10 @@ eliminationOps constr desc t = do
550551 eliminateOp constr (foldl' (+) 0) $ S.foldl' (+) 0 . t
551552 prop (desc <> " foldl1'") $
552553 eliminateOp constr (wrapMaybe $ foldl1' (+)) $ S.foldl1' (+) . t
554+#ifdef DEVBUILD
553555 prop (desc <> " foldr1") $
554556 eliminateOp constr (wrapMaybe $ foldr1 (+)) $ S.foldr1 (+) . t
557+#endif
555558 prop (desc <> " all") $ eliminateOp constr (all even) $ S.all even . t
556559 prop (desc <> " any") $ eliminateOp constr (any even) $ S.any even . t
557560 prop (desc <> " and") $ eliminateOp constr (and . fmap (> 0)) $