on master help sign in
Add "Streamly.Fold" module
fd7bd71Harendra Kumar
Changelog.md+10−4
@@ -7,8 +7,10 @@
7
7
8### Enhancements
8### Enhancements
9
9
10−* Add Streamly.Array module to support efficient fixed size immutable arrays
10+* MAJOR: Add Streamly.Array module to support efficient fixed size immutable
11− for buffering and IO.
11+ arrays for buffering and IO. See the haddock docs for the module.
12+* MAJOR: Add Streamly.Fold module to support composable folds (stream
13+ consumers). See the haddock docs for the module.
12* Add the following to "Streamly.Prelude":
14* Add the following to "Streamly.Prelude":
13 * `foldrS` to fold a stream to a stream
15 * `foldrS` to fold a stream to a stream
14 * `foldrT` to fold a stream to an arbitrary transformer monad
16 * `foldrT` to fold a stream to an arbitrary transformer monad
@@ -16,10 +18,14 @@
16
18
17### Deprecations
19### Deprecations
18
20
21+* `runStream` has been replaced by `Streamly.Prelude.drain`
22+* `runN` has been replaced by `drainN`
23+* `runWhile` has been replaced by `drainWhile`
24+
19−* `runStream`, `foldWith`, `foldMapWith`, `forEachWith` have been moved from
25+* `foldWith`, `foldMapWith`, `forEachWith` have been moved from
20 "Streamly" module to "Streamly.Prelude" module.
26 "Streamly" module to "Streamly.Prelude" module.
21
27
22−* "Streamly.Prelude":
28+* In "Streamly.Prelude":
23 * Deprecate `scanx`, `foldx`, `foldxM`, `foldr1`
29 * Deprecate `scanx`, `foldx`, `foldxM`, `foldr1`
24 * Remove deprecated APIs `scan`, `foldl`, `foldlM`
30 * Remove deprecated APIs `scan`, `foldl`, `foldlM`
25
31
README.md+16−15
@@ -93,10 +93,11 @@ lookahead style concurrency, the only difference is that this time multiple
93directories are read concurrently:
93directories are read concurrently:
94
94
95``` haskell
95``` haskell
96−import Streamly (runStream, aheadly)
96+import Streamly (aheadly)
97+import qualified Streamly.Prelude as S
97
98
98main :: IO ()
99main :: IO ()
99−main = runStream $ aheadly $ listDirRecursive
100+main = S.drain $ aheadly $ listDirRecursive
100```
101```
101
102
102Isn't that magical? What's going on here? Streamly does not introduce any new
103Isn't that magical? What's going on here? Streamly does not introduce any new
@@ -134,7 +135,7 @@ import Streamly
134import qualified Streamly.Prelude as S
135import qualified Streamly.Prelude as S
135import Data.Function ((&))
136import Data.Function ((&))
136
137
137−main = runStream $
138+main = S.drain $
138 S.repeatM getLine
139 S.repeatM getLine
139 & fmap read
140 & fmap read
140 & S.filter even
141 & S.filter even
@@ -172,7 +173,7 @@ The following code finishes in 3 seconds (6 seconds when serial):
172The following finishes in 10 seconds (100 seconds when serial):
173The following finishes in 10 seconds (100 seconds when serial):
173
174
174``` haskell
175``` haskell
175−runStream $ asyncly $ S.replicateM 10 $ p 10
176+S.drain $ asyncly $ S.replicateM 10 $ p 10
176```
177```
177
178
178## Concurrent Streaming Pipelines
179## Concurrent Streaming Pipelines
@@ -183,7 +184,7 @@ following example prints a "hello" every second; if you use `&` instead of
183application.
184application.
184
185
185``` haskell
186``` haskell
186−main = runStream $
187+main = S.drain $
187 S.repeatM (threadDelay 1000000 >> return "hello")
188 S.repeatM (threadDelay 1000000 >> return "hello")
188 |& S.mapM (\x -> threadDelay 1000000 >> putStrLn x)
189 |& S.mapM (\x -> threadDelay 1000000 >> putStrLn x)
189```
190```
@@ -194,7 +195,7 @@ We can use `mapM` or `sequence` functions concurrently on a stream.
194
195
195``` haskell
196``` haskell
196> let p n = threadDelay (n * 1000000) >> return n
197> let p n = threadDelay (n * 1000000) >> return n
197−> runStream $ aheadly $ S.mapM (\x -> p 1 >> print x) (serially $ repeatM (p 1))
198+> S.drain $ aheadly $ S.mapM (\x -> p 1 >> print x) (serially $ repeatM (p 1))
198```
199```
199
200
200## Serial and Concurrent Merging
201## Serial and Concurrent Merging
@@ -230,7 +231,7 @@ delay n = S.yieldM $ do
230### Serial
231### Serial
231
232
232``` haskell
233``` haskell
233−main = runStream $ delay 3 <> delay 2 <> delay 1
234+main = S.drain $ delay 3 <> delay 2 <> delay 1
234```
235```
235```
236```
236ThreadId 36: Delay 3
237ThreadId 36: Delay 3
@@ -241,7 +242,7 @@ ThreadId 36: Delay 1
241### Parallel
242### Parallel
242
243
243``` haskell
244``` haskell
244−main = runStream . parallely $ delay 3 <> delay 2 <> delay 1
245+main = S.drain . parallely $ delay 3 <> delay 2 <> delay 1
245```
246```
246```
247```
247ThreadId 42: Delay 1
248ThreadId 42: Delay 1
@@ -262,7 +263,7 @@ loops = do
262 y <- S.fromFoldable [3,4]
263 y <- S.fromFoldable [3,4]
263 S.yieldM $ putStrLn $ show (x, y)
264 S.yieldM $ putStrLn $ show (x, y)
264
265
265−main = runStream loops
266+main = S.drain loops
266```
267```
267```
268```
268(1,3)
269(1,3)
@@ -278,21 +279,21 @@ the loop can run run concurrently by but the results are presented in the same
278order as serial execution:
279order as serial execution:
279
280
280``` haskell
281``` haskell
281−main = runStream $ aheadly $ loops
282+main = S.drain $ aheadly $ loops
282```
283```
283
284
284To run it with depth first concurrency yielding results asynchronously in the
285To run it with depth first concurrency yielding results asynchronously in the
285same order as they become available (deep async composition):
286same order as they become available (deep async composition):
286
287
287``` haskell
288``` haskell
288−main = runStream $ asyncly $ loops
289+main = S.drain $ asyncly $ loops
289```
290```
290
291
291To run it with breadth first concurrency and yeilding results asynchronously
292To run it with breadth first concurrency and yeilding results asynchronously
292(wide async composition):
293(wide async composition):
293
294
294``` haskell
295``` haskell
295−main = runStream $ wAsyncly $ loops
296+main = S.drain $ wAsyncly $ loops
296```
297```
297
298
298The above streams provide lazy/demand-driven concurrency which is automatically
299The above streams provide lazy/demand-driven concurrency which is automatically
@@ -301,14 +302,14 @@ infinite streams. The following combinator provides strict, unbounded
301concurrency irrespective of demand:
302concurrency irrespective of demand:
302
303
303``` haskell
304``` haskell
304−main = runStream $ parallely $ loops
305+main = S.drain $ parallely $ loops
305```
306```
306
307
307To run it serially but interleaving the outer and inner loop iterations
308To run it serially but interleaving the outer and inner loop iterations
308(breadth first serial):
309(breadth first serial):
309
310
310``` haskell
311``` haskell
311−main = runStream $ wSerially $ loops
312+main = S.drain $ wSerially $ loops
312```
313```
313
314
314## Magical Concurrency
315## Magical Concurrency
@@ -346,7 +347,7 @@ example, to print hello once every second you can simply write this:
346import Streamly
347import Streamly
347import Streamly.Prelude as S
348import Streamly.Prelude as S
348
349
349−main = runStream $ asyncly $ avgRate 1 $ S.repeatM $ putStrLn "hello"
350+main = S.drain $ asyncly $ avgRate 1 $ S.repeatM $ putStrLn "hello"
350```
351```
351
352
352For some practical uses of rate control, see
353For some practical uses of rate control, see
benchmark/Adaptive.hs+2−2
@@ -9,7 +9,7 @@ import Control.Concurrent (threadDelay)
9import Control.Monad (when)
9import Control.Monad (when)
10import Control.Monad.IO.Class (liftIO)
10import Control.Monad.IO.Class (liftIO)
11import Gauge
11import Gauge
12−import Streamly hiding (runStream)
12+import Streamly
13import Streamly.Prelude as S
13import Streamly.Prelude as S
14import System.Random (randomRIO)
14import System.Random (randomRIO)
15
15
@@ -32,7 +32,7 @@ source range = S.replicateM value $ do
32
32
33{-# INLINE run #-}
33{-# INLINE run #-}
34run :: IsStream t => (Int, Int) -> (Int, Int) -> (t IO Int -> SerialT IO Int) -> IO ()
34run :: IsStream t => (Int, Int) -> (Int, Int) -> (t IO Int -> SerialT IO Int) -> IO ()
35−run srange crange t = S.runStream $ do
35+run srange crange t = S.drain $ do
36 n <- t $ source srange
36 n <- t $ source srange
37 d <- liftIO (randomRIO crange)
37 d <- liftIO (randomRIO crange)
38 when (d /= 0) $ liftIO $ threadDelay d
38 when (d /= 0) $ liftIO $ threadDelay d
benchmark/Array.hs+16−7
@@ -7,12 +7,15 @@
7-- Maintainer : harendra.kumar@gmail.com
7-- Maintainer : harendra.kumar@gmail.com
8
8
9import Control.DeepSeq (NFData(..), deepseq)
9import Control.DeepSeq (NFData(..), deepseq)
10+import Data.Functor.Identity (Identity)
10import Foreign.Storable (Storable(..))
11import Foreign.Storable (Storable(..))
11import System.Random (randomRIO)
12import System.Random (randomRIO)
12
13
14+import Streamly
13import qualified GHC.Exts as GHC
15import qualified GHC.Exts as GHC
14
16
15import qualified ArrayOps as Ops
17import qualified ArrayOps as Ops
18+import qualified Streamly.Array as A
16
19
17import Gauge
20import Gauge
18
21
@@ -42,13 +45,15 @@ main =
42 defaultMain
45 defaultMain
43 [ bgroup "array"
46 [ bgroup "array"
44 [ bgroup "generation"
47 [ bgroup "generation"
45− [ benchPureSrc "intFromTo" Ops.sourceIntFromTo
48+ [ benchPureSrc "fromStreamN . intFromTo" Ops.sourceIntFromTo
49+ , benchPureSrc "fromStream . intFromTo" Ops.sourceIntFromToFromStream
50+ , benchPureSrc "fromList . intFromTo" Ops.sourceIntFromToFromList
46− , benchPureSrc "unfoldr" Ops.sourceUnfoldr
51+ , benchPureSrc "fromStreamN . unfoldr" Ops.sourceUnfoldr
47− , benchPureSrc "fromList" Ops.sourceFromList
52+ , benchPureSrc "fromStreamN . fromList" Ops.sourceFromList
48− , benchPureSrc "IsList.fromList" Ops.sourceIsList
53+ , benchPureSrc "fromStreamN . IsList.fromList" Ops.sourceIsList
49− , benchPureSrc "IsString.fromString" Ops.sourceIsString
54+ , benchPureSrc "fromStreamN . IsString.fromString" Ops.sourceIsString
50− , mkString `deepseq` (bench "readsPrec" $ nf Ops.readInstance mkString)
55+ , mkString `deepseq` (bench "read" $ nf Ops.readInstance mkString)
51− , benchPureSink "showsPrec pure streams" Ops.showInstance
56+ , benchPureSink "show" Ops.showInstance
52 ]
57 ]
53 , bgroup "elimination"
58 , bgroup "elimination"
54 [ benchPureSink "id" id
59 [ benchPureSink "id" id
@@ -62,6 +67,10 @@ main =
62 , benchPureSink "min" Ops.ordInstanceMin
67 , benchPureSink "min" Ops.ordInstanceMin
63 , benchPureSink "IsList.toList" GHC.toList
68 , benchPureSink "IsList.toList" GHC.toList
64 , benchPureSink "foldl'" Ops.pureFoldl'
69 , benchPureSink "foldl'" Ops.pureFoldl'
70+ , benchPureSink "toStream"
71+ (A.toStream :: A.Array Int -> SerialT Identity Int)
72+ , benchPureSink "toStreamRev"
73+ (A.toStreamRev :: A.Array Int -> SerialT Identity Int)
65#ifdef DEVBUILD
74#ifdef DEVBUILD
66 , benchPureSink "foldable/foldl'" Ops.foldableFoldl'
75 , benchPureSink "foldable/foldl'" Ops.foldableFoldl'
67 , benchPureSink "foldable/sum" Ops.foldableSum
76 , benchPureSink "foldable/sum" Ops.foldableSum
benchmark/ArrayOps.hs+12−2
@@ -58,7 +58,16 @@ sourceUnfoldr n = runIdentity $ A.fromStreamN value $ S.unfoldr step n
58
58
59{-# INLINE sourceIntFromTo #-}
59{-# INLINE sourceIntFromTo #-}
60sourceIntFromTo :: Int -> Stream Int
60sourceIntFromTo :: Int -> Stream Int
61+sourceIntFromTo n = runIdentity $
61−sourceIntFromTo n = runIdentity $ A.fromStreamN value $ S.enumerateFromTo n (n + value)
62+ A.fromStreamN value $ S.enumerateFromTo n (n + value)
63+
64+{-# INLINE sourceIntFromToFromStream #-}
65+sourceIntFromToFromStream :: Int -> Stream Int
66+sourceIntFromToFromStream n = runIdentity $
67+ A.fromStream $ S.enumerateFromTo n (n + value)
68+
69+sourceIntFromToFromList :: Int -> Stream Int
70+sourceIntFromToFromList n = A.fromList $ [n..n + value]
62
71
63{-# INLINE sourceFromList #-}
72{-# INLINE sourceFromList #-}
64sourceFromList :: Int -> Stream Int
73sourceFromList :: Int -> Stream Int
@@ -70,7 +79,8 @@ sourceIsList n = runIdentity $ A.fromStreamN value $ GHC.fromList [n..n+value]
70
79
71{-# INLINE sourceIsString #-}
80{-# INLINE sourceIsString #-}
72sourceIsString :: Int -> Stream P.Char
81sourceIsString :: Int -> Stream P.Char
82+sourceIsString n = runIdentity $
73−sourceIsString n = runIdentity $ A.fromStreamN value $ GHC.fromString (P.replicate (n + value) 'a')
83+ A.fromStreamN value $ GHC.fromString (P.replicate (n + value) 'a')
74
84
75{-
85{-
76-------------------------------------------------------------------------------
86-------------------------------------------------------------------------------
benchmark/Linear.hs+51−2
@@ -9,12 +9,16 @@
9import Control.DeepSeq (NFData(..), deepseq)
9import Control.DeepSeq (NFData(..), deepseq)
10import Data.Functor.Identity (Identity, runIdentity)
10import Data.Functor.Identity (Identity, runIdentity)
11import System.Random (randomRIO)
11import System.Random (randomRIO)
12+import Data.Monoid (Last(..))
12
13
13import qualified GHC.Exts as GHC
14import qualified GHC.Exts as GHC
14import qualified LinearOps as Ops
15import qualified LinearOps as Ops
15
16
16import Streamly
17import Streamly
18+import qualified Streamly.Array as A
19+import qualified Streamly.Fold as FL
17import qualified Streamly.Prelude as S
20import qualified Streamly.Prelude as S
21+-- import qualified Streamly.Sink as Sink
18import Gauge
22import Gauge
19
23
20-------------------------------------------------------------------------------
24-------------------------------------------------------------------------------
@@ -66,7 +70,7 @@ benchPureSinkIO name f =
66
70
67{-# INLINE benchPureSrc #-}
71{-# INLINE benchPureSrc #-}
68benchPureSrc :: String -> (Int -> SerialT Identity a) -> Benchmark
72benchPureSrc :: String -> (Int -> SerialT Identity a) -> Benchmark
69−benchPureSrc name src = benchPure name src (runIdentity . S.runStream)
73+benchPureSrc name src = benchPure name src (runIdentity . S.drain)
70
74
71mkString :: String
75mkString :: String
72mkString = "fromList [1" ++ concat (replicate Ops.value ",1") ++ "]"
76mkString = "fromList [1" ++ concat (replicate Ops.value ",1") ++ "]"
@@ -186,7 +190,51 @@ main =
186 , benchIOSink "minimum" Ops.minimum
190 , benchIOSink "minimum" Ops.minimum
187
191
188 , benchIOSink "toList" Ops.toList
192 , benchIOSink "toList" Ops.toList
189− , benchIOSink "toRevList" Ops.toRevList
193+ -- , benchIOSink "toListRev" Ops.toListRev
194+ ]
195+ , bgroup "folds"
196+ [ benchIOSink "drain" (FL.foldl' FL.drain)
197+ -- , benchIOSink "sink" (Sink.sink Sink.drain)
198+ , benchIOSink "last" (FL.foldl' FL.last)
199+ , benchIOSink "length" (FL.foldl' FL.length)
200+ , benchIOSink "sum" (FL.foldl' FL.sum)
201+ , benchIOSink "product" (FL.foldl' FL.product)
202+ , benchIOSink "maximumBy" (FL.foldl' (FL.maximumBy compare))
203+ , benchIOSink "maximum" (FL.foldl' FL.maximum)
204+ , benchIOSink "minimumBy" (FL.foldl' (FL.minimumBy compare))
205+ , benchIOSink "minimum" (FL.foldl' FL.minimum)
206+ , benchIOSink "mean" (\s -> FL.foldl' FL.mean (S.map (fromIntegral :: Int -> Double) s))
207+ , benchIOSink "variance" (\s -> FL.foldl' FL.variance (S.map (fromIntegral :: Int -> Double) s))
208+ , benchIOSink "stdDev" (\s -> FL.foldl' FL.stdDev (S.map (fromIntegral :: Int -> Double) s))
209+
210+ , benchIOSink "mconcat" (FL.foldl' FL.mconcat . (S.map (Last . Just)))
211+ , benchIOSink "foldMap" (FL.foldl' (FL.foldMap (Last . Just)))
212+
213+ -- , benchIOSink "toList" (FL.foldl' FL.toList)
214+ , benchIOSink "toListRev" (FL.foldl' FL.toListRev)
215+ -- , benchIOSink "toStream" (FL.foldl' FL.toStream)
216+ , benchIOSink "toStreamRev" (FL.foldl' FL.toStreamRev)
217+ , benchIOSink "toArrayN" (FL.foldl' (A.toArrayN Ops.value))
218+
219+ , benchIOSink "index" (FL.foldl' (FL.index Ops.maxValue))
220+ , benchIOSink "head" (FL.foldl' FL.head)
221+ , benchIOSink "find" (FL.foldl' (FL.find (== Ops.maxValue)))
222+ , benchIOSink "findIndex" (FL.foldl' (FL.findIndex (== Ops.maxValue)))
223+ , benchIOSink "elemIndex" (FL.foldl' (FL.elemIndex Ops.maxValue))
224+
225+ , benchIOSink "null" (FL.foldl' FL.null)
226+ , benchIOSink "elem" (FL.foldl' (FL.elem Ops.maxValue))
227+ , benchIOSink "notElem" (FL.foldl' (FL.notElem Ops.maxValue))
228+ , benchIOSink "all" (FL.foldl' (FL.all (<= Ops.maxValue)))
229+ , benchIOSink "any" (FL.foldl' (FL.any (> Ops.maxValue)))
230+ , benchIOSink "and" (\s -> FL.foldl' FL.and (S.map (<= Ops.maxValue) s))
231+ , benchIOSink "or" (\s -> FL.foldl' FL.or (S.map (> Ops.maxValue) s))
232+ ]
233+ , bgroup "folds-mixed" -- Applicative
234+ [
235+ benchIOSink "all,any" (FL.foldl' ((,) <$> FL.all (<= Ops.maxValue)
236+ <*> FL.any (> Ops.maxValue)))
237+ , benchIOSink "sum,length" (FL.foldl' ((,) <$> FL.sum <*> FL.length))
190 ]
238 ]
191 , bgroup "transformation"
239 , bgroup "transformation"
192 [ benchIOSink "scanl" (Ops.scan 1)
240 [ benchIOSink "scanl" (Ops.scan 1)
@@ -201,6 +249,7 @@ main =
201 , benchIOSink "findIndices" (Ops.findIndices 1)
249 , benchIOSink "findIndices" (Ops.findIndices 1)
202 , benchIOSink "elemIndices" (Ops.elemIndices 1)
250 , benchIOSink "elemIndices" (Ops.elemIndices 1)
203 , benchIOSink "reverse" (Ops.reverse 1)
251 , benchIOSink "reverse" (Ops.reverse 1)
252+ , benchIOSink "reverse'" (Ops.reverse' 1)
204 , benchIOSink "foldrS" (Ops.foldrS 1)
253 , benchIOSink "foldrS" (Ops.foldrS 1)
205 , benchIOSink "foldrSMap" (Ops.foldrSMap 1)
254 , benchIOSink "foldrSMap" (Ops.foldrSMap 1)
206 , benchIOSink "foldrT" (Ops.foldrT 1)
255 , benchIOSink "foldrT" (Ops.foldrT 1)
benchmark/LinearOps.hs+7−3
@@ -160,7 +160,7 @@ sourceIsString n = GHC.fromString (P.replicate (n + value) 'a')
160
160
161{-# INLINE runStream #-}
161{-# INLINE runStream #-}
162runStream :: Monad m => Stream m a -> m ()
162runStream :: Monad m => Stream m a -> m ()
163−runStream = S.runStream
163+runStream = S.drain
164
164
165{-# INLINE toList #-}
165{-# INLINE toList #-}
166toList :: Monad m => Stream m Int -> m [Int]
166toList :: Monad m => Stream m Int -> m [Int]
@@ -221,7 +221,7 @@ uncons s = do
221
221
222{-# INLINE init #-}
222{-# INLINE init #-}
223init :: Monad m => Stream m a -> m ()
223init :: Monad m => Stream m a -> m ()
224−init s = S.init s >>= Prelude.mapM_ S.runStream
224+init s = S.init s >>= Prelude.mapM_ S.drain
225
225
226{-# INLINE tail #-}
226{-# INLINE tail #-}
227tail :: Monad m => Stream m a -> m ()
227tail :: Monad m => Stream m a -> m ()
@@ -241,9 +241,11 @@ mapM_ = S.mapM_ (\_ -> return ())
241
241
242toList = S.toList
242toList = S.toList
243
243
244+{-
244{-# INLINE toRevList #-}
245{-# INLINE toRevList #-}
245toRevList :: Monad m => Stream m Int -> m [Int]
246toRevList :: Monad m => Stream m Int -> m [Int]
246toRevList = S.toRevList
247toRevList = S.toRevList
248+-}
247
249
248foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
250foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
249foldl'Build = S.foldl' (flip (:)) []
251foldl'Build = S.foldl' (flip (:)) []
@@ -330,6 +332,7 @@ composeN' n f =
330{-# INLINE insertBy #-}
332{-# INLINE insertBy #-}
331{-# INLINE deleteBy #-}
333{-# INLINE deleteBy #-}
332{-# INLINE reverse #-}
334{-# INLINE reverse #-}
335+{-# INLINE reverse' #-}
333{-# INLINE foldrS #-}
336{-# INLINE foldrS #-}
334{-# INLINE foldrSMap #-}
337{-# INLINE foldrSMap #-}
335{-# INLINE foldrT #-}
338{-# INLINE foldrT #-}
@@ -337,7 +340,7 @@ composeN' n f =
337scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
340scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
338 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
341 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
339 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
342 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
340− findIndices, elemIndices, insertBy, deleteBy, reverse,
343+ findIndices, elemIndices, insertBy, deleteBy, reverse, reverse',
341 foldrS, foldrSMap, foldrT, foldrTMap
344 foldrS, foldrSMap, foldrT, foldrTMap
342 :: Monad m
345 :: Monad m
343 => Int -> Stream m Int -> m ()
346 => Int -> Stream m Int -> m ()
@@ -387,6 +390,7 @@ elemIndices n = composeN n $ S.elemIndices maxValue
387insertBy n = composeN n $ S.insertBy compare maxValue
390insertBy n = composeN n $ S.insertBy compare maxValue
388deleteBy n = composeN n $ S.deleteBy (>=) maxValue
391deleteBy n = composeN n $ S.deleteBy (>=) maxValue
389reverse n = composeN n $ S.reverse
392reverse n = composeN n $ S.reverse
393+reverse' n = composeN n $ S.reverse'
390foldrS n = composeN n $ S.foldrS S.cons S.nil
394foldrS n = composeN n $ S.foldrS S.cons S.nil
391foldrSMap n = composeN n $ S.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
395foldrSMap n = composeN n $ S.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
392foldrT n = composeN n $ S.foldrT S.cons S.nil
396foldrT n = composeN n $ S.foldrT S.cons S.nil
benchmark/NestedOps.hs+1−1
@@ -52,7 +52,7 @@ sourceUnfoldr start n = S.unfoldr step start
52
52
53{-# INLINE runStream #-}
53{-# INLINE runStream #-}
54runStream :: Monad m => Stream m a -> m ()
54runStream :: Monad m => Stream m a -> m ()
55−runStream = S.runStream
55+runStream = S.drain
56
56
57{-# INLINE runToList #-}
57{-# INLINE runToList #-}
58runToList :: Monad m => Stream m a -> m [a]
58runToList :: Monad m => Stream m a -> m [a]
examples/AcidRain.hs+1−1
@@ -54,5 +54,5 @@ main :: IO ()
54main = do
54main = do
55 putStrLn "Your health is deteriorating due to acid rain,\
55 putStrLn "Your health is deteriorating due to acid rain,\
56 \ type \"potion\" or \"quit\""
56 \ type \"potion\" or \"quit\""
57− let runGame = S.runWhile (== Alive) $ S.mapM getStatus runEvents
57+ let runGame = S.drainWhile (== Alive) $ S.mapM getStatus runEvents
58 void $ runStateT runGame 60
58 void $ runStateT runGame 60
examples/CirclingSquare.hs+1−1
@@ -78,6 +78,6 @@ main :: IO ()
78main = do
78main = do
79 sdlInit
79 sdlInit
80 cref <- newIORef (0,0)
80 cref <- newIORef (0,0)
81− S.runStream $ asyncly $ constRate 40
81+ S.drain $ asyncly $ constRate 40
82 $ S.repeatM (updateController cref)
82 $ S.repeatM (updateController cref)
83 `parallel` S.repeatM (updateDisplay cref)
83 `parallel` S.repeatM (updateDisplay cref)
examples/ControlFlow.hs+8−9
@@ -26,7 +26,6 @@ import Control.Monad.Trans.Maybe
26import Control.Monad.Trans.Except
26import Control.Monad.Trans.Except
27import Control.Monad.Trans.Cont
27import Control.Monad.Trans.Cont
28import Streamly
28import Streamly
29−import Streamly.Prelude ((|:))
30import qualified Streamly.Prelude as S
29import qualified Streamly.Prelude as S
31
30
32-------------------------------------------------------------------------------
31-------------------------------------------------------------------------------
@@ -58,7 +57,7 @@ getSequenceMaybeBelow = do
58
57
59mainMaybeBelow :: IO ()
58mainMaybeBelow :: IO ()
60mainMaybeBelow = do
59mainMaybeBelow = do
61− r <- runMaybeT (runStream getSequenceMaybeBelow)
60+ r <- runMaybeT (S.drain getSequenceMaybeBelow)
62 case r of
61 case r of
63 Just _ -> putStrLn "Bingo"
62 Just _ -> putStrLn "Bingo"
64 Nothing -> putStrLn "Wrong"
63 Nothing -> putStrLn "Wrong"
@@ -122,7 +121,7 @@ getSequenceEitherBelow = do
122mainEitherBelow :: IO ()
121mainEitherBelow :: IO ()
123mainEitherBelow = do
122mainEitherBelow = do
124 -- XXX Cannot lift catchE
123 -- XXX Cannot lift catchE
125− r <- runExceptT (runStream getSequenceEitherBelow)
124+ r <- runExceptT (S.drain getSequenceEitherBelow)
126 case r of
125 case r of
127 Right _ -> liftIO $ putStrLn "Bingo"
126 Right _ -> liftIO $ putStrLn "Bingo"
128 Left s -> liftIO $ putStrLn s
127 Left s -> liftIO $ putStrLn s
@@ -155,7 +154,7 @@ getSequenceEitherAsyncBelow = do
155
154
156mainEitherAsyncBelow :: IO ()
155mainEitherAsyncBelow :: IO ()
157mainEitherAsyncBelow = do
156mainEitherAsyncBelow = do
158− r <- runExceptT (runStream $ asyncly getSequenceEitherAsyncBelow)
157+ r <- runExceptT (S.drain $ asyncly getSequenceEitherAsyncBelow)
159 case r of
158 case r of
160 Right _ -> liftIO $ putStrLn "Bingo"
159 Right _ -> liftIO $ putStrLn "Bingo"
161 Left s -> liftIO $ putStrLn s
160 Left s -> liftIO $ putStrLn s
@@ -219,7 +218,7 @@ getSequenceMonadThrow = do
219
218
220mainMonadThrow :: IO ()
219mainMonadThrow :: IO ()
221mainMonadThrow =
220mainMonadThrow =
222− catch (runStream getSequenceMonadThrow >> liftIO (putStrLn "Bingo"))
221+ catch (S.drain getSequenceMonadThrow >> liftIO (putStrLn "Bingo"))
223 (\(e :: SomeException) -> liftIO $ print e)
222 (\(e :: SomeException) -> liftIO $ print e)
224
223
225-------------------------------------------------------------------------------
224-------------------------------------------------------------------------------
@@ -300,10 +299,10 @@ mainContAbove = do
300main :: IO ()
299main :: IO ()
301main = do
300main = do
302 mainMaybeBelow
301 mainMaybeBelow
303− runStream $ runMaybeT mainMaybeAbove
302+ S.drain $ runMaybeT mainMaybeAbove
304− runContT (runStream mainContBelow) return
303+ runContT (S.drain mainContBelow) return
305− runStream (runContT mainContAbove return)
304+ S.drain (runContT mainContAbove return)
306 mainEitherBelow
305 mainEitherBelow
307− runStream (runExceptT mainEitherAbove)
306+ S.drain (runExceptT mainEitherAbove)
308 mainMonadThrow
307 mainMonadThrow
309 mainEitherAsyncBelow
308 mainEitherAsyncBelow
examples/ListDir.hs+3−2
@@ -1,7 +1,8 @@
1import Control.Monad.IO.Class (liftIO)
1import Control.Monad.IO.Class (liftIO)
2import Path.IO (listDir, getCurrentDir)
2import Path.IO (listDir, getCurrentDir)
3import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
3import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
4−import Streamly (runStream, aheadly, (<>))
4+import Streamly (aheadly, (<>))
5+import qualified Streamly.Prelude as S
5
6
6-- | List the current directory recursively using concurrent processing
7-- | List the current directory recursively using concurrent processing
7--
8--
@@ -11,7 +12,7 @@ import Streamly (runStream, aheadly, (<>))
11main :: IO ()
12main :: IO ()
12main = do
13main = do
13 hSetBuffering stdout LineBuffering
14 hSetBuffering stdout LineBuffering
14− runStream . aheadly $ getCurrentDir >>= readdir
15+ S.drain . aheadly $ getCurrentDir >>= readdir
15 where readdir d = do
16 where readdir d = do
16 (ds, fs) <- listDir d
17 (ds, fs) <- listDir d
17 liftIO $ mapM_ putStrLn $ fmap show fs <> fmap show ds
18 liftIO $ mapM_ putStrLn $ fmap show fs <> fmap show ds
examples/SearchQuery.hs+4−4
@@ -1,5 +1,5 @@
1import Streamly
1import Streamly
2−import Streamly.Prelude (nil, yieldM, (|:))
2+import Streamly.Prelude (drain, nil, yieldM, (|:))
3import Network.HTTP.Simple
3import Network.HTTP.Simple
4
4
5-- | Runs three search engine queries in parallel and prints the search engine
5-- | Runs three search engine queries in parallel and prints the search engine
@@ -10,13 +10,13 @@ import Network.HTTP.Simple
10main :: IO ()
10main :: IO ()
11main = do
11main = do
12 putStrLn "Using parallel stream construction"
12 putStrLn "Using parallel stream construction"
13− runStream . parallely $ google |: bing |: duckduckgo |: nil
13+ drain . parallely $ google |: bing |: duckduckgo |: nil
14
14
15 putStrLn "\nUsing parallel semigroup composition"
15 putStrLn "\nUsing parallel semigroup composition"
16− runStream . parallely $ yieldM google <> yieldM bing <> yieldM duckduckgo
16+ drain . parallely $ yieldM google <> yieldM bing <> yieldM duckduckgo
17
17
18 putStrLn "\nUsing parallel applicative zip"
18 putStrLn "\nUsing parallel applicative zip"
19− runStream . zipAsyncly $
19+ drain . zipAsyncly $
20 (,,) <$> yieldM google <*> yieldM bing <*> yieldM duckduckgo
20 (,,) <$> yieldM google <*> yieldM bing <*> yieldM duckduckgo
21
21
22 where
22 where
src/Streamly.hs+138−60
@@ -7,55 +7,25 @@
7-- Stability : experimental
7-- Stability : experimental
8-- Portability : GHC
8-- Portability : GHC
9--
9--
10−-- The way a list represents a sequence of pure values, a stream represents a
10+-- Haskell lists provide a simple and powerful API but they are limited to
11−-- sequence of monadic actions. The monadic stream API offered by Streamly is
11+-- holding only pure values. Streamly streams are a logical extension of
12−-- very close to the Haskell "Prelude" pure lists' API, it can be considered as
12+-- Haskell lists to support monadic sequences and powerful declarative
13−-- a natural extension of lists to monadic actions. Streamly streams provide
13+-- concurrency. They provide almost the same API as lists (See
14−-- concurrent composition and merging of streams. It can be considered as a
14+-- "Streamly.Prelude"). In fact, Haskell lists can be expressed as pure
15−-- concurrent list transformer. In contrast to the "Prelude" lists, merging or
15+-- streams, a special case of monadic streams, with similar or better
16−-- appending streams of arbitrary length is scalable and inexpensive.
16+-- performance and almost drop-in replacement.
17−--
17+--
18−-- The basic stream type is 'Serial', it represents a sequence of IO actions,
18+-- Streams are designed for high performance applications and do not exhibit
19−-- and is a 'Monad'. The type 'SerialT' is a monad transformer that can
19+-- issues that are usually associated with lists. For example, streams
20−-- represent a sequence of actions in an arbitrary monad. The type 'Serial' is
20+-- express strings as streams of 'Char' and bytestrings as streams of 'Word8'
21−-- in fact a synonym for @SerialT IO@. There are a few more types similar to
21+-- without any issues, obviating the need for special purpose libraries like
22−-- 'SerialT', all of them represent a stream and differ only in the
22+-- bytestring and text, and various lazy and strict falvors of those. Streamly
23−-- 'Semigroup', 'Applicative' and 'Monad' compositions of the stream. 'Serial'
23+-- arrays (See "Streamly.Array") complement streams for storing or buffering
24−-- and 'WSerial' types compose serially whereas 'Async' and 'WAsync'
24+-- data efficiently and facilitating efficient interfacing of streams with IO
25−-- types compose concurrently. All these types can be freely inter-converted
25+-- systems.
26−-- using type combinators without any cost. You can freely switch to any type
27−-- of composition at any point in the program. When no type annotation or
28−-- explicit stream type combinators are used, the default stream type is
29−-- inferred as 'Serial'.
30−--
31−-- Here is a simple console echo program example:
32−--
33−-- @
34−-- > runStream $ S.repeatM getLine & S.mapM putStrLn
35−-- @
36−--
37−-- For more details please see the "Streamly.Tutorial" module and the examples
38−-- directory in this package.
39−--
40−-- This module exports stream types, instances and some basic operations.
41−-- Functionality exported by this module include:
42−--
43−-- * Semigroup append ('<>') instances as well as explicit operations for merging streams
44−-- * Monad and Applicative instances for looping over streams
45−-- * Zip Applicatives for zipping streams
46−-- * Stream type combinators to convert between different composition styles
47−-- * Some basic utilities to run and fold streams
48−--
49−-- See the "Streamly.Prelude" module for comprehensive APIs for construction,
50−-- generation, elimination and transformation of streams.
51−--
52−-- This module is designed to be imported unqualified:
53−--
54−-- @
55−-- import Streamly
56−-- @
57
26
58{-# LANGUAGE CPP #-}
27{-# LANGUAGE CPP #-}
28+{-# LANGUAGE MultiParamTypeClasses #-}
59
29
60#if __GLASGOW_HASKELL__ >= 800
30#if __GLASGOW_HASKELL__ >= 800
61{-# OPTIONS_GHC -Wno-orphans #-}
31{-# OPTIONS_GHC -Wno-orphans #-}
@@ -65,6 +35,13 @@
65
35
66module Streamly
36module Streamly
67 (
37 (
38+ -- * Streams and Folds
39+ -- $overview
40+
41+ -- * Streams Overview
42+ -- $streams
43+
44+ -- * Type Synonyms
68 MonadAsync
45 MonadAsync
69
46
70 -- * Stream transformers
47 -- * Stream transformers
@@ -73,11 +50,11 @@ module Streamly
73 , SerialT
50 , SerialT
74 , WSerialT
51 , WSerialT
75
52
76− -- ** Concurrent Lookahead Streams
53+ -- ** Speculative Streams
77 -- $lookahead
54 -- $lookahead
78 , AheadT
55 , AheadT
79
56
80− -- ** Concurrent Asynchronous Streams
57+ -- ** Asynchronous Streams
81 -- $async
58 -- $async
82 , AsyncT
59 , AsyncT
83 , WAsyncT
60 , WAsyncT
@@ -147,6 +124,7 @@ module Streamly
147
124
148 -- * Deprecated
125 -- * Deprecated
149 , Streaming
126 , Streaming
127+ , runStream
150 , runStreaming
128 , runStreaming
151 , runStreamT
129 , runStreamT
152 , runInterleavedT
130 , runInterleavedT
@@ -165,7 +143,6 @@ module Streamly
165
143
166 -- * Moved
144 -- * Moved
167 -- | These APIs have been moved to other modules
145 -- | These APIs have been moved to other modules
168− , runStream
169 , foldWith
146 , foldWith
170 , foldMapWith
147 , foldMapWith
171 , forEachWith
148 , forEachWith
@@ -179,12 +156,113 @@ import Streamly.Streams.Async
179import Streamly.Streams.Combinators
156import Streamly.Streams.Combinators
180import Streamly.Streams.Parallel
157import Streamly.Streams.Parallel
181import Streamly.Streams.Serial
158import Streamly.Streams.Serial
182−import Streamly.Streams.StreamK hiding (runStream, serial)
159+import Streamly.Streams.StreamK hiding (serial)
183import Streamly.Streams.Zip
160import Streamly.Streams.Zip
184
161
185import qualified Streamly.Prelude as P
162import qualified Streamly.Prelude as P
186import qualified Streamly.Streams.StreamK as K
163import qualified Streamly.Streams.StreamK as K
187
164
165+-- XXX provide good succinct examples of pipelining, merging, splitting ect.
166+-- below.
167+--
168+-- $overview
169+-- Streamly is a general purpose concurrent data flow programming engine
170+-- expressing a program as a network of streams and folds. Stream types (e.g.
171+-- 'SerialT') exported from this module represent streams or more specifically
172+-- stream producers while the fold type exported from "Streamly.Fold"
173+-- represents stream consumers. Both producers and consumers can be
174+-- transformed in many ways e.g. using map, scan, filter etc. Transformations
175+-- can be chained into a pipeline, multiple streams can be merged together or a
176+-- stream can be split into multiple independent data flows. All these
177+-- compositions can be combined together to form an arbitrary data flow network
178+-- expressing any kind of general purpose computing in a declarative manner.
179+--
180+-- The simplest way to compose a data processing pipeline is to generate a
181+-- stream of data (e.g. from a file or network), chain pipelines of
182+-- transformations on the stream and finally pass it to a fold. Folds represent
183+-- specific ways to consume a stream (e.g. producing a sum of numbers).
184+-- Transformations can also be applied on folds before they are attached with a
185+-- stream.
186+--
187+-- @
188+--
189+-- ---Stream m a-->stream transforms-->fold transforms-->Fold m a b
190+-- @
191+--
192+-- Stream producers can be appended, merged, zipped or nested to build an
193+-- arbitrarily complex composed stream producer. Transformations can be applied
194+-- at any point in the composition tree. See "Streamly.Prelude" module for more
195+-- details on the combinators.
196+--
197+-- @
198+--
199+-- -------Stream m a-->transform-->|
200+-- |
201+-- -------Stream m a-->transform-->|=>---transform-->Stream m a--->
202+-- |
203+-- -------Stream m a-->transform-->|
204+-- @
205+--
206+-- Folds are consumers of streams and can be used to split a stream into
207+-- multiple independent flows. Grouping transforms a stream by applying a fold
208+-- on segments of a stream, distributing applies multiple folds in parallel on
209+-- the same stream and combines them, partitioning sends different elements of
210+-- a stream to different folds, unzipping divides the elements of a stream into
211+-- parts and sends them through different folds. Parsers are nothing but a
212+-- particular type of folds. Transformations can be applied contravariantly on
213+-- the input of a fold.
214+--
215+-- @
216+--
217+-- |---transform----Fold m a b--------|
218+-- ---stream m a-->transform-->| |---f b c ...
219+-- |---transform----Fold m a c--------|
220+-- | |
221+-- ...
222+-- @
223+--
224+
225+-- $streams
226+-- The basic stream type is 'Serial', it represents a sequence of IO actions,
227+-- and is a 'Monad'. The type 'SerialT' is a monad transformer that can
228+-- represent a sequence of actions in an arbitrary monad. The type 'Serial' is
229+-- in fact a synonym for @SerialT IO@. There are a few more types similar to
230+-- 'SerialT', all of them represent a stream and differ only in the
231+-- 'Semigroup', 'Applicative' and 'Monad' compositions of the stream. 'Serial'
232+-- and 'WSerial' types compose serially whereas 'Async' and 'WAsync'
233+-- types compose concurrently. All these types can be freely inter-converted
234+-- using type combinators without any cost. You can freely switch to any type
235+-- of composition at any point in the program. When no type annotation or
236+-- explicit stream type combinators are used, the default stream type is
237+-- inferred as 'Serial'.
238+--
239+-- Here is a simple console echo program example:
240+--
241+-- @
242+-- > drain $ S.repeatM getLine & S.mapM putStrLn
243+-- @
244+--
245+-- For more details please see the "Streamly.Tutorial" module and the examples
246+-- directory in this package.
247+--
248+-- This module exports stream types, instances and some basic operations.
249+-- Functionality exported by this module include:
250+--
251+-- * Semigroup append ('<>') instances as well as explicit operations for merging streams
252+-- * Monad and Applicative instances for looping over streams
253+-- * Zip Applicatives for zipping streams
254+-- * Stream type combinators to convert between different composition styles
255+-- * Some basic utilities to run and fold streams
256+--
257+-- See the "Streamly.Prelude" module for comprehensive APIs for construction,
258+-- generation, elimination and transformation of streams.
259+--
260+-- This module is designed to be imported unqualified:
261+--
262+-- @
263+-- import Streamly
264+-- @
265+
188------------------------------------------------------------------------------
266------------------------------------------------------------------------------
189-- Eliminating a stream
267-- Eliminating a stream
190------------------------------------------------------------------------------
268------------------------------------------------------------------------------
@@ -194,55 +272,55 @@ import qualified Streamly.Streams.StreamK as K
194-- @since 0.1.0
272-- @since 0.1.0
195{-# DEPRECATED runStreaming "Please use runStream instead." #-}
273{-# DEPRECATED runStreaming "Please use runStream instead." #-}
196runStreaming :: (Monad m, IsStream t) => t m a -> m ()
274runStreaming :: (Monad m, IsStream t) => t m a -> m ()
197−runStreaming = P.runStream . K.adapt
275+runStreaming = P.drain . K.adapt
198
276
199-- | Same as @runStream@.
277-- | Same as @runStream@.
200--
278--
201-- @since 0.1.0
279-- @since 0.1.0
202{-# DEPRECATED runStreamT "Please use runStream instead." #-}
280{-# DEPRECATED runStreamT "Please use runStream instead." #-}
203runStreamT :: Monad m => SerialT m a -> m ()
281runStreamT :: Monad m => SerialT m a -> m ()
204−runStreamT = P.runStream
282+runStreamT = P.drain
205
283
206-- | Same as "Streamly.Prelude.runStream".
284-- | Same as "Streamly.Prelude.runStream".
207--
285--
208−{-# DEPRECATED runStream "Please use Streamly.Prelude.runStream instead." #-}
286+{-# DEPRECATED runStream "Please use Streamly.Prelude.drain instead." #-}
209runStream :: Monad m => SerialT m a -> m ()
287runStream :: Monad m => SerialT m a -> m ()
210−runStream = P.runStream
288+runStream = P.drain
211
289
212-- | Same as @runStream . wSerially@.
290-- | Same as @runStream . wSerially@.
213--
291--
214-- @since 0.1.0
292-- @since 0.1.0
215{-# DEPRECATED runInterleavedT "Please use 'runStream . interleaving' instead." #-}
293{-# DEPRECATED runInterleavedT "Please use 'runStream . interleaving' instead." #-}
216runInterleavedT :: Monad m => WSerialT m a -> m ()
294runInterleavedT :: Monad m => WSerialT m a -> m ()
217−runInterleavedT = P.runStream . K.adapt
295+runInterleavedT = P.drain . K.adapt
218
296
219-- | Same as @runStream . parallely@.
297-- | Same as @runStream . parallely@.
220--
298--
221-- @since 0.1.0
299-- @since 0.1.0
222{-# DEPRECATED runParallelT "Please use 'runStream . parallely' instead." #-}
300{-# DEPRECATED runParallelT "Please use 'runStream . parallely' instead." #-}
223runParallelT :: Monad m => ParallelT m a -> m ()
301runParallelT :: Monad m => ParallelT m a -> m ()
224−runParallelT = P.runStream . K.adapt
302+runParallelT = P.drain . K.adapt
225
303
226-- | Same as @runStream . asyncly@.
304-- | Same as @runStream . asyncly@.
227--
305--
228-- @since 0.1.0
306-- @since 0.1.0
229{-# DEPRECATED runAsyncT "Please use 'runStream . asyncly' instead." #-}
307{-# DEPRECATED runAsyncT "Please use 'runStream . asyncly' instead." #-}
230runAsyncT :: Monad m => AsyncT m a -> m ()
308runAsyncT :: Monad m => AsyncT m a -> m ()
231−runAsyncT = P.runStream . K.adapt
309+runAsyncT = P.drain . K.adapt
232
310
233-- | Same as @runStream . zipping@.
311-- | Same as @runStream . zipping@.
234--
312--
235-- @since 0.1.0
313-- @since 0.1.0
236{-# DEPRECATED runZipStream "Please use 'runStream . zipSerially instead." #-}
314{-# DEPRECATED runZipStream "Please use 'runStream . zipSerially instead." #-}
237runZipStream :: Monad m => ZipSerialM m a -> m ()
315runZipStream :: Monad m => ZipSerialM m a -> m ()
238−runZipStream = P.runStream . K.adapt
316+runZipStream = P.drain . K.adapt
239
317
240-- | Same as @runStream . zippingAsync@.
318-- | Same as @runStream . zippingAsync@.
241--
319--
242-- @since 0.1.0
320-- @since 0.1.0
243{-# DEPRECATED runZipAsync "Please use 'runStream . zipAsyncly instead." #-}
321{-# DEPRECATED runZipAsync "Please use 'runStream . zipAsyncly instead." #-}
244runZipAsync :: Monad m => ZipAsyncM m a -> m ()
322runZipAsync :: Monad m => ZipAsyncM m a -> m ()
245−runZipAsync = P.runStream . K.adapt
323+runZipAsync = P.drain . K.adapt
246
324
247-- | Same as "Streamly.Prelude.foldWith".
325-- | Same as "Streamly.Prelude.foldWith".
248--
326--
src/Streamly/Array.hs+87−38
@@ -55,6 +55,17 @@
55-- * provide /O(1)/ access to elements
55-- * provide /O(1)/ access to elements
56-- * store only data and not functions
56-- * store only data and not functions
57-- * provide efficient IO interfacing
57-- * provide efficient IO interfacing
58+--
59+-- 'ByteString' data type from the 'bytestring' package and the 'Text' data
60+-- type from the 'text' package are just special cases of arrays. 'ByteString'
61+-- is equivalent to @Array Word8@ and 'Text' is equivalent to a @utf16@ encoded
62+-- @Array Word8@. All the 'bytestring' and 'text' operations can be performed
63+-- on arrays with equivalent or better performance by converting them to and
64+-- from streams.
65+--
66+-- This module is designed to be imported qualified:
67+--
68+-- > import qualified Streamly.Array as A
58
69
59-- Each array is one pointer visible to the GC. Too many small arrays (e.g.
70-- Each array is one pointer visible to the GC. Too many small arrays (e.g.
60-- single byte) are only as good as holding those elements in a Haskell list.
71-- single byte) are only as good as holding those elements in a Haskell list.
@@ -69,26 +80,44 @@ module Streamly.Array
69 (
80 (
70 Array
81 Array
71
82
83+ -- , defaultChunkSize
84+
72 -- * Construction
85 -- * Construction
73− -- , fromStream
86+ -- | When performance matters, the fastest way to generate an array is
87+ -- 'fromStreamN'. For regular use, 'IsList' and 'IsString' instances can be
88+ -- used to conveniently construct arrays from literal values.
89+ -- 'OverloadedLists' extension or 'fromList' can be used to construct an
90+ -- array from a list literal. Similarly, 'OverloadedStrings' extension or
91+ -- 'fromList' can be used to construct an array from a string literal.
92+
74 , fromStreamN
93 , fromStreamN
94+ , fromStream
95+ , fromListN
75− -- , fromList
96+ , fromList
76− -- , fromListN
97+
98+ -- Folds
99+ , toArrayN
100+ -- , toArrays
101+ -- , toArray
102+
103+ -- Streams
104+ , arraysOf
77
105
78 -- * Elimination
106 -- * Elimination
79− -- , toList
107+ -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
108+ -- list.
109+
80 , toStream
110 , toStream
111+ , toStreamRev
112+ , toList
113+ , flattenArrays
114+ -- , flattenArraysRev
115+ , spliceArrays
81
116
82 -- * Random Access
117 -- * Random Access
83 , length
118 , length
84 , (!!)
119 , (!!)
85
120
86− -- * Streams of Arrays
87− -- , defaultChunkSize
88− -- , fromStreamArraysOf
89− , flattenArrays
90− , spliceArrays
91−
92 -- * IO: Single Array
121 -- * IO: Single Array
93 -- , fromHandleUpto
122 -- , fromHandleUpto
94 -- , fromHandleN
123 -- , fromHandleN
@@ -117,10 +146,10 @@ import GHC.IO (unsafeDupablePerformIO)
117
146
118import Streamly.Streams.Serial (SerialT)
147import Streamly.Streams.Serial (SerialT)
119import Streamly.Streams.StreamK.Type (IsStream, mkStream)
148import Streamly.Streams.StreamK.Type (IsStream, mkStream)
120−import Streamly.Array.Types
149+import Streamly.Array.Types hiding (flattenArrays)
150+import qualified Streamly.Array.Types as A
121
151
122import qualified Streamly.Streams.StreamD as D
152import qualified Streamly.Streams.StreamD as D
123−import qualified Streamly.Streams.Prelude as P
124import qualified Streamly.Prelude as S
153import qualified Streamly.Prelude as S
125
154
126-------------------------------------------------------------------------------
155-------------------------------------------------------------------------------
@@ -135,7 +164,6 @@ import qualified Streamly.Prelude as S
135{-# INLINE fromStreamN #-}
164{-# INLINE fromStreamN #-}
136fromStreamN :: (Monad m, Storable a) => Int -> SerialT m a -> m (Array a)
165fromStreamN :: (Monad m, Storable a) => Int -> SerialT m a -> m (Array a)
137fromStreamN n m = fromStreamDN n $ D.toStreamD m
166fromStreamN n m = fromStreamDN n $ D.toStreamD m
138−-- fromStreamN n = FL.foldl (FL.toArrayN n)
139
167
140-------------------------------------------------------------------------------
168-------------------------------------------------------------------------------
141-- Elimination
169-- Elimination
@@ -144,9 +172,22 @@ fromStreamN n m = fromStreamDN n $ D.toStreamD m
144-- | Convert an 'Array' into a stream.
172-- | Convert an 'Array' into a stream.
145--
173--
146-- @since 0.7.0
174-- @since 0.7.0
147−{-# INLINABLE toStream #-}
175+{-# INLINE_EARLY toStream #-}
148toStream :: (Monad m, IsStream t, Storable a) => Array a -> t m a
176toStream :: (Monad m, IsStream t, Storable a) => Array a -> t m a
149−toStream = P.fromArray
177+toStream = D.fromStreamD . toStreamD
178+-- XXX add fallback to StreamK rule
179+-- {-# RULES "Streamly.Array.toStream fallback to StreamK" [1]
180+-- forall a. S.toStreamK (toStream a) = K.fromArray a #-}
181+
182+-- | Convert an 'Array' into a stream in reverse order.
183+--
184+-- @since 0.7.0
185+{-# INLINE_EARLY toStreamRev #-}
186+toStreamRev :: (Monad m, IsStream t, Storable a) => Array a -> t m a
187+toStreamRev = D.fromStreamD . toStreamDRev
188+-- XXX add fallback to StreamK rule
189+-- {-# RULES "Streamly.Array.toStreamRev fallback to StreamK" [1]
190+-- forall a. S.toStreamK (toStreamRev a) = K.revFromArray a #-}
150
191
151{-# INLINE null #-}
192{-# INLINE null #-}
152null :: Storable a => Array a -> Bool
193null :: Storable a => Array a -> Bool
@@ -174,22 +215,35 @@ arr !! i =
174-- Streams of Arrays
215-- Streams of Arrays
175-------------------------------------------------------------------------------
216-------------------------------------------------------------------------------
176
217
177−-- | Convert a stream of Arrays into a stream of their elements.
218+-- | Convert a stream of arrays into a stream of their elements.
178--
219--
179-- @since 0.7.0
220-- @since 0.7.0
180{-# INLINE flattenArrays #-}
221{-# INLINE flattenArrays #-}
181flattenArrays :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
222flattenArrays :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
182−flattenArrays m = D.fromStreamD $ D.flattenArrays (D.toStreamD m)
223+flattenArrays m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
183
224
184−{-
225+-- XXX should we have a reverseArrays API to reverse the stream of arrays
185−-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
226+-- instead?
186−-- arrays of size n.
227+--
187−{-# INLINE fromStreamArraysOf #-}
228+-- | Convert a stream of arrays into a stream of their elements reversing the
229+-- contents of each array before flattening.
230+--
231+-- @since 0.7.0
232+{-# INLINE _flattenArraysRev #-}
233+_flattenArraysRev :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
234+_flattenArraysRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
235+
236+-- |
237+-- > arraysOf n = FL.groupsOf n (FL.toArrayN n)
238+--
239+-- Groups the elements in an input stream into arrays of given size.
240+--
241+-- @since 0.7.0
242+{-# INLINE arraysOf #-}
188−fromStreamArraysOf :: (IsStream t, Monad m, Storable a)
243+arraysOf :: (IsStream t, Monad m, Storable a)
189 => Int -> t m a -> t m (Array a)
244 => Int -> t m a -> t m (Array a)
190−fromStreamArraysOf n str =
245+arraysOf n str =
191− D.fromStreamD $ D.groupsOf n (fromStreamN n) (D.toStreamD str)
246+ D.fromStreamD $ fromStreamDArraysOf n (D.toStreamD str)
192− -}
193
247
194-- | Given a stream of arrays, splice them all together to generate a single
248-- | Given a stream of arrays, splice them all together to generate a single
195-- array. The stream must be /finite/.
249-- array. The stream must be /finite/.
@@ -214,23 +268,17 @@ spliceArrays s = do
214 return $ dst `plusPtr` len
268 return $ dst `plusPtr` len
215 in dst'
269 in dst'
216
270
217−{-
218−-- CAUTION: a very large number (millions) of arrays can degrade performance
219−-- due to GC overhead because we need to buffer the arrays before we flatten
220−-- all the arrays.
221−--
222-- | Create an 'Array' from a stream. This is useful when we want to create a
271-- | Create an 'Array' from a stream. This is useful when we want to create a
223−-- single array from a stream of unknown size. 'fromStreamN' is more efficient
272+-- single array from a stream of unknown size. 'fromStreamN' is at least twice
224−-- when the size is already known.
273+-- as efficient when the size is already known.
274+--
275+-- Note that if the input stream is too large memory allocation for the array
276+-- may fail. When the stream size is not known, `arraysOf` followed by
277+-- processing of indvidual arrays in the resulting stream should be preferred.
225--
278--
226{-# INLINE fromStream #-}
279{-# INLINE fromStream #-}
227fromStream :: (Monad m, Storable a) => SerialT m a -> m (Array a)
280fromStream :: (Monad m, Storable a) => SerialT m a -> m (Array a)
228−fromStream m = do
281+fromStream m = A.fromStreamD $ D.toStreamD m
229− let s = fromStreamArraysOf defaultChunkSize $ D.toStreamD m
230− buffered = S.foldr S.cons S.nil s
231− len = S.sum (S.map length buffered)
232− fromStreamN len $ flattenArrays buffered
233−-}
234
282
235{-
283{-
236-- Move this to FileIO
284-- Move this to FileIO
@@ -253,6 +301,7 @@ toHandleArrays h m = S.mapM_ (liftIO . toHandle h) m
253fromHandleUpto :: Int -> Handle -> IO (Array Word8)
301fromHandleUpto :: Int -> Handle -> IO (Array Word8)
254fromHandleUpto size h = do
302fromHandleUpto size h = do
255 ptr <- mallocPlainForeignPtrBytes size
303 ptr <- mallocPlainForeignPtrBytes size
304+ -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
256 withForeignPtr ptr $ \p -> do
305 withForeignPtr ptr $ \p -> do
257 n <- hGetBufSome h p size
306 n <- hGetBufSome h p size
258 let v = Array
307 let v = Array
src/Streamly/Array/Types.hs+157−7
@@ -29,11 +29,15 @@ module Streamly.Array.Types
29 , unsafeAppend
29 , unsafeAppend
30 , shrinkToFit
30 , shrinkToFit
31 , memcpy
31 , memcpy
32+ , memcmp
32
33
33− , fromCStringAddrUnsafe
34 , fromList
34 , fromList
35 , fromListN
35 , fromListN
36 , fromStreamDN
36 , fromStreamDN
37+ , fromStreamD
38+ , fromStreamDArraysOf
39+ , flattenArrays
40+ , flattenArraysRev
37
41
38 -- * Elimination
42 -- * Elimination
39 , unsafeIndex
43 , unsafeIndex
@@ -42,10 +46,13 @@ module Streamly.Array.Types
42 , foldr
46 , foldr
43
47
44 , toStreamD
48 , toStreamD
49+ , toStreamDRev
45 , toList
50 , toList
51+ , toArrayN
46
52
47 -- * Utilities
53 -- * Utilities
48 , defaultChunkSize
54 , defaultChunkSize
55+ , mkChunkSizeKB
49 )
56 )
50where
57where
51
58
@@ -70,8 +77,11 @@ import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes, newForeignPtr_)
70import GHC.IO (IO(IO), unsafeDupablePerformIO)
77import GHC.IO (IO(IO), unsafeDupablePerformIO)
71import GHC.Ptr (Ptr(..))
78import GHC.Ptr (Ptr(..))
72
79
73−import qualified Prelude
80+import Streamly.Fold.Types (Fold(..))
81+import Streamly.SVar (adaptState)
82+
74import qualified Streamly.Streams.StreamD.Type as D
83import qualified Streamly.Streams.StreamD.Type as D
84+import qualified Streamly.Streams.StreamK as K
75import qualified GHC.Exts as Exts
85import qualified GHC.Exts as Exts
76
86
77-------------------------------------------------------------------------------
87-------------------------------------------------------------------------------
@@ -118,7 +128,8 @@ import qualified GHC.Exts as Exts
118-- is to store the elemSize in the array at construction and use that instead
128-- is to store the elemSize in the array at construction and use that instead
119-- of using sizeOf. Need to charaterize perf cost of this.
129-- of using sizeOf. Need to charaterize perf cost of this.
120--
130--
121−-- XXX add reverse flag to reverse the contents without physically reversing.
131+-- XXX rename the fields to "start, next, end".
132+--
122data Array a =
133data Array a =
123#ifdef DEVBUILD
134#ifdef DEVBUILD
124 Storable a =>
135 Storable a =>
@@ -222,13 +233,18 @@ shrinkToFit arr@Array{..} = do
222 }
233 }
223 else return arr
234 else return arr
224
235
236+-- XXX when converting an array of Word8 from a literal string we can simply
237+-- refer to the literal string. Is it possible to write rules such that
238+-- fromList Word8 can be rewritten so that GHC does not first convert the
239+-- literal to [Char] and then we convert it back to an Array Word8?
240+--
225-- Note that the address must be a read-only address (meant to be used for
241-- Note that the address must be a read-only address (meant to be used for
226-- read-only string literals) because we are sharing it, any modification to
242-- read-only string literals) because we are sharing it, any modification to
227-- the original address would change our array. That's why this function is
243-- the original address would change our array. That's why this function is
228-- unsafe.
244-- unsafe.
229−{-# INLINE fromCStringAddrUnsafe #-}
245+{-# INLINE _fromCStringAddrUnsafe #-}
230−fromCStringAddrUnsafe :: Addr# -> IO (Array Word8)
246+_fromCStringAddrUnsafe :: Addr# -> IO (Array Word8)
231−fromCStringAddrUnsafe addr# = do
247+_fromCStringAddrUnsafe addr# = do
232 ptr <- newForeignPtr_ (castPtr cstr)
248 ptr <- newForeignPtr_ (castPtr cstr)
233 len <- c_strlen cstr
249 len <- c_strlen cstr
234 let n = fromIntegral len
250 let n = fromIntegral len
@@ -287,6 +303,23 @@ toStreamD Array{..} =
287 return r
303 return r
288 return $ D.Yield x (p `plusPtr` (sizeOf (undefined :: a)))
304 return $ D.Yield x (p `plusPtr` (sizeOf (undefined :: a)))
289
305
306+{-# INLINE_NORMAL toStreamDRev #-}
307+toStreamDRev :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
308+toStreamDRev Array{..} =
309+ let p = aEnd `plusPtr` negate (sizeOf (undefined :: a))
310+ in D.Stream step p
311+
312+ where
313+
314+ {-# INLINE_LATE step #-}
315+ step _ p | p < unsafeForeignPtrToPtr aStart = return D.Stop
316+ step _ p = do
317+ let !x = unsafeInlineIO $ do
318+ r <- peek p
319+ touchForeignPtr aStart
320+ return r
321+ return $ D.Yield x (p `plusPtr` negate (sizeOf (undefined :: a)))
322+
290{-# INLINE_NORMAL foldl' #-}
323{-# INLINE_NORMAL foldl' #-}
291foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
324foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
292foldl' f z arr = runIdentity $ D.foldl' f z $ toStreamD arr
325foldl' f z arr = runIdentity $ D.foldl' f z $ toStreamD arr
@@ -299,6 +332,39 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
299-- Instances
332-- Instances
300-------------------------------------------------------------------------------
333-------------------------------------------------------------------------------
301
334
335+-- | @toArrayN n@ folds a maximum of @n@ elements from the input stream to an
336+-- 'Array'.
337+--
338+-- @since 0.7.0
339+{-# INLINE_NORMAL toArrayN #-}
340+toArrayN :: forall m a. (Monad m, Storable a) => Int -> Fold m a (Array a)
341+toArrayN n = Fold step initial extract
342+
343+ where
344+
345+ initial =
346+ let !arr = unsafeDupablePerformIO $ unsafeNew n
347+ in return arr
348+ step arr@(Array _ end bound) _ | end == bound = return arr
349+ step (Array start end bound) x =
350+ let !_ = unsafeInlineIO $ poke end x
351+ in return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
352+ extract = return
353+
354+{-
355+-- | Fold the input to a pure buffered stream (List) of arrays.
356+{-# INLINE toArrays #-}
357+toArrays :: (Monad m, Storable a) => Int -> Fold m a (List (Array a))
358+toArrays n = Fold step initial extract
359+
360+-- This can be implemented by combining the List of arrays from toArrays into a
361+-- single array.
362+-- | Fold the whole input to a single array.
363+{-# INLINE toArray #-}
364+toArray :: forall m a. (Monad m, Storable a) => Fold m a (Array a)
365+toArray = Fold step begin done
366+-}
367+
302{-# INLINE_NORMAL fromStreamDN #-}
368{-# INLINE_NORMAL fromStreamDN #-}
303fromStreamDN :: forall m a. (Monad m, Storable a)
369fromStreamDN :: forall m a. (Monad m, Storable a)
304 => Int -> D.Stream m a -> m (Array a)
370 => Int -> D.Stream m a -> m (Array a)
@@ -313,6 +379,86 @@ fromStreamDN limit str = do
313 let !_ = unsafeInlineIO $ poke ptr x
379 let !_ = unsafeInlineIO $ poke ptr x
314 in ptr `plusPtr` sizeOf (undefined :: a)
380 in ptr `plusPtr` sizeOf (undefined :: a)
315
381
382+-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
383+-- arrays of size n.
384+{-# INLINE fromStreamDArraysOf #-}
385+fromStreamDArraysOf :: (Monad m, Storable a)
386+ => Int -> D.Stream m a -> D.Stream m (Array a)
387+fromStreamDArraysOf n str = D.groupsOf n (toArrayN n) str
388+
389+-- XXX concatMap does not seem to have the best possible performance so we have
390+-- a custom way to concat arrays.
391+data CAState s a = CAParent s | CANested s (ForeignPtr a) (Ptr a) (Ptr a)
392+
393+{-# INLINE_NORMAL flattenArrays #-}
394+flattenArrays :: forall m a. (Monad m, Storable a)
395+ => D.Stream m (Array a) -> D.Stream m a
396+flattenArrays (D.Stream step state) = D.Stream step' (CAParent state)
397+
398+ where
399+
400+ {-# INLINE_LATE step' #-}
401+ step' gst (CAParent st) = do
402+ r <- step (adaptState gst) st
403+ return $ case r of
404+ D.Yield Array{..} s ->
405+ let p = unsafeForeignPtrToPtr aStart
406+ in D.Skip (CANested s aStart p aEnd)
407+ D.Skip s -> D.Skip (CAParent s)
408+ D.Stop -> D.Stop
409+
410+ step' _ (CANested st _ p end) | p == end =
411+ return $ D.Skip $ CAParent st
412+
413+ step' _ (CANested st startf p end) = do
414+ let !x = unsafeInlineIO $ do
415+ r <- peek p
416+ touchForeignPtr startf
417+ return r
418+ return $ D.Yield x (CANested st startf
419+ (p `plusPtr` (sizeOf (undefined :: a))) end)
420+
421+{-# INLINE_NORMAL flattenArraysRev #-}
422+flattenArraysRev :: forall m a. (Monad m, Storable a)
423+ => D.Stream m (Array a) -> D.Stream m a
424+flattenArraysRev (D.Stream step state) = D.Stream step' (CAParent state)
425+
426+ where
427+
428+ {-# INLINE_LATE step' #-}
429+ step' gst (CAParent st) = do
430+ r <- step (adaptState gst) st
431+ return $ case r of
432+ D.Yield Array{..} s ->
433+ let p = aEnd `plusPtr` negate (sizeOf (undefined :: a))
434+ -- XXX we do not need aEnd
435+ in D.Skip (CANested s aStart p aEnd)
436+ D.Skip s -> D.Skip (CAParent s)
437+ D.Stop -> D.Stop
438+
439+ step' _ (CANested st start p _) | p < unsafeForeignPtrToPtr start =
440+ return $ D.Skip $ CAParent st
441+
442+ step' _ (CANested st startf p end) = do
443+ let !x = unsafeInlineIO $ do
444+ r <- peek p
445+ touchForeignPtr startf
446+ return r
447+ return $ D.Yield x (CANested st startf
448+ (p `plusPtr` negate (sizeOf (undefined :: a))) end)
449+
450+-- CAUTION: a very large number (millions) of arrays can degrade performance
451+-- due to GC overhead because we need to buffer the arrays before we flatten
452+-- all the arrays.
453+--
454+{-# INLINE fromStreamD #-}
455+fromStreamD :: (Monad m, Storable a) => D.Stream m a -> m (Array a)
456+fromStreamD m = do
457+ let s = fromStreamDArraysOf defaultChunkSize m
458+ buffered <- D.foldr K.cons K.nil s
459+ len <- K.foldl' (+) 0 (K.map length buffered)
460+ fromStreamDN len $ flattenArrays $ D.fromStreamK buffered
461+
316-- | Convert an 'Array' into a list.
462-- | Convert an 'Array' into a list.
317--
463--
318-- @since 0.7.0
464-- @since 0.7.0
@@ -338,7 +484,7 @@ fromListN n xs = runIdentity $ fromStreamDN n $ D.fromList xs
338-- @since 0.7.0
484-- @since 0.7.0
339{-# INLINABLE fromList #-}
485{-# INLINABLE fromList #-}
340fromList :: Storable a => [a] -> Array a
486fromList :: Storable a => [a] -> Array a
341−fromList xs = fromListN (Prelude.length xs) xs
487+fromList xs = runIdentity $ fromStreamD $ D.fromList xs
342
488
343instance (Storable a, Read a, Show a) => Read (Array a) where
489instance (Storable a, Read a, Show a) => Read (Array a) where
344 {-# INLINE readPrec #-}
490 {-# INLINE readPrec #-}
@@ -468,3 +614,7 @@ allocOverhead = 2 * sizeOf (undefined :: Int)
468defaultChunkSize :: Int
614defaultChunkSize :: Int
469defaultChunkSize = 32 * k - allocOverhead
615defaultChunkSize = 32 * k - allocOverhead
470 where k = 1024
616 where k = 1024
617+
618+mkChunkSizeKB :: Int -> Int
619+mkChunkSizeKB n = n * k - allocOverhead
620+ where k = 1024
src/Streamly/Fold.hsnew file+1781
@@ -0,0 +1,1781 @@
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE RecordWildCards #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+
8+-- |
9+-- Module : Streamly.Fold
10+-- Copyright : (c) 2019 Composewell Technologies
11+-- (c) 2013 Gabriel Gonzalez
12+-- License : BSD3
13+-- Maintainer : harendra.kumar@gmail.com
14+-- Stability : experimental
15+-- Portability : GHC
16+--
17+-- This module is the consumer side dual of the "Streamly.Prelude" module. The
18+-- "Streamly.Prelude" module contains producer or stream generation side
19+-- compositions while this module contains consumer side or stream fold
20+-- composition. Both the modules are organized in the same way so that you can
21+-- easily find the corresponding operations.
22+--
23+-- Stream consumers are essentially left folds that can be composed in many
24+-- interesting ways. For example, a parsing applicative composition builds an
25+-- applicative parser, a distributing applicative fold composition distributes
26+-- the input to the constituent folds and then combines the outputs.
27+-- Similarly, a partitioning applicative can partition the input among the
28+-- constituent folds. There are many other ways to combine folds.
29+--
30+-- > import qualified Streamly.Fold as FL
31+--
32+--
33+-- A left fold is represented by the type 'Fold'. @Fold m a b@ folds an
34+-- input stream consisting of values of type @a@ to a structure of type
35+-- @b@. The fold can be run using 'foldl'' and an input stream.
36+--
37+-- >>> FL.foldl' FL.sum (S.enumerateFromTo 1 100)
38+-- 5050
39+
40+-- Also see the "Streamly.Sink" module that provides specialized left folds
41+-- that discard the outputs.
42+-- IMPORTANT: keep the signatures consistent with the folds in Streamly.Prelude
43+
44+module Streamly.Fold
45+ (
46+ -- * Fold Type
47+ Fold -- (..)
48+
49+ -- * Folding
50+ -- $termination
51+
52+ , foldl'
53+
54+ -- , tail
55+ -- , init
56+
57+ -- ** Full Folds
58+ , drain
59+ , last
60+ , length
61+ , sum
62+ , product
63+ , maximumBy
64+ , maximum
65+ , minimumBy
66+ , minimum
67+ -- , the
68+ , mean
69+ , variance
70+ , stdDev
71+
72+ -- ** Full Folds (Monoidal)
73+ , mconcat
74+ , foldMap
75+ , foldMapM
76+
77+ -- ** Full Folds (To Containers)
78+ -- , toStream -- experimental
79+ , toStreamRev -- experimental
80+
81+ -- , toList
82+ , toListRev -- experimental
83+
84+ -- ** Partial Folds
85+ -- , drainN
86+ -- , drainWhile
87+ -- , (!!)
88+ -- , genericIndex
89+ , index
90+ , head
91+ -- , findM
92+ , find
93+ , lookup
94+ , findIndex
95+ , elemIndex
96+ , null
97+ , elem
98+ , notElem
99+ -- XXX these are slower than right folds even when full input is used
100+ , all
101+ , any
102+ , and
103+ , or
104+
105+ -- * Transformations
106+ -- | Unlike stream producer types (e.g. @SerialT m a@) which have only
107+ -- output side, folds have an input side as well as an output side. In the
108+ -- type @Fold m a b@, the input type is @a@ and the output type is @b@.
109+ -- Transformations can be applied either on the input side or on the output
110+ -- side. The 'Functor' instance of a fold maps on the output of the fold:
111+ --
112+ -- >>> FL.foldl' (fmap show FL.sum) (S.enumerateFromTo 1 100)
113+ -- "5050"
114+ --
115+ -- However, the input side or contravariant transformations are more
116+ -- interesting for folds. The following sections describe the input
117+ -- transformation operations on a fold. The names of the operations are
118+ -- consistent with their covariant counterparts in "Streamly.Prelude", the
119+ -- only difference is that they are prefixed with 'l' which stands for
120+ -- 'left' assuming left side is the input side, notice that in @Fold m a b@
121+ -- the type variable @a@ is on the left side.
122+
123+ -- ** Mapping
124+ , lmap
125+ --, sequence
126+ , lmapM
127+
128+ -- ** Scanning
129+ , scanl'
130+ , postscanl'
131+
132+ -- , lscanl'
133+ -- , lscanlM'
134+ -- , lscanl1'
135+ -- , lscanl1M'
136+ --
137+ -- , lpostscanl'
138+ -- , lpostscanlM'
139+ -- , lprescanl'
140+ -- , lprescanlM'
141+
142+ -- ** Filtering
143+ , lfilter
144+ , lfilterM
145+ , ltake
146+ , ltakeWhile
147+ {-
148+ , ltakeWhileM
149+ , ldrop
150+ , ldropWhile
151+ , ldropWhileM
152+ , ldeleteBy
153+ , luniq
154+
155+ -- ** Mapping Filters
156+ , lmapMaybe
157+ , lmapMaybeM
158+
159+ -- ** Scanning Filters
160+ , lfindIndices
161+ , lelemIndices
162+
163+ -- ** Insertion
164+ -- | Insertion adds more elements to the stream.
165+
166+ , linsertBy
167+ , lintersperseM
168+
169+ -- ** Reordering
170+ , lreverse
171+ -}
172+
173+ -- * Stream Splitting Operations
174+ {-
175+ -- ** Spanning
176+ -- | Spanning splits the input into two groups and applies two different
177+ -- folds on each group.
178+
179+ -- Element unaware spanning
180+ -- , splitAt
181+
182+ -- Element aware spanning
183+ , span
184+ , break
185+ , spanBy
186+ , spanRollingBy
187+ , spanned
188+ -}
189+
190+ -- ** Grouping
191+ -- | Grouping splits the stream into groups and applies the supplied fold
192+ -- on each group.
193+
194+ -- In imperative terms grouped folding can be considered as a nested loop
195+ -- where we loop over the stream to group elements and then loop over
196+ -- individual groups to fold them to a single value that is yielded in the
197+ -- output stream.
198+ --
199+ -- Note that these grouping folds are true streaming folds that never
200+ -- accumulate the group in memory before folding, i.e. the group elements
201+ -- are consumed by the folds as they are yielded by the stream. Therefore,
202+ -- the whole computation runs in constant space.
203+ -- In contrast, we can simply use a scan on the stream to buffer the whole
204+ -- groups in memory and then map a fold on it to fold the groups. This kind
205+ -- of grouping and folding would not work well when the group size is big.
206+
207+ -- Element unaware grouping
208+ , groupsOf
209+
210+ -- Element aware grouping
211+ , groups
212+ , groupsBy
213+ -- , groupsRollingBy
214+ , grouped -- XXX experimental
215+
216+ -- ** Splitting by an Element
217+ , splitBy
218+ , splitSuffixBy
219+ -- , splitPrefixBy
220+ , wordsBy
221+
222+ -- ** Splitting on a Sequence
223+ , splitOn
224+ , splitSuffixOn
225+ -- , splitPrefixOn
226+ -- , wordsOn
227+
228+ -- Keeping the delimiters
229+ , splitOn'
230+ , splitSuffixOn'
231+ -- , splitPrefixOn'
232+
233+ -- Splitting by multiple sequences
234+ -- , splitOnAny
235+ -- , splitSuffixOnAny
236+ -- , splitPrefixOnAny
237+
238+ -- ** Distributing
239+ -- |
240+ -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
241+ -- of the stream to each fold and combines the results using a function.
242+ --
243+ -- @
244+ --
245+ -- |-------Fold m a b--------|
246+ -- ---stream m a---| |---m (b,c,...)
247+ -- |-------Fold m a c--------|
248+ -- | |
249+ -- ...
250+ -- @
251+ --
252+ -- To compute the average of numbers in a stream without going throught he
253+ -- stream twice:
254+ --
255+ -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
256+ -- >>> FL.foldl' avg (S.enumerateFromTo 1.0 100.0)
257+ -- 50.5
258+ --
259+ -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
260+ -- the input to both the folds and combines the outputs using Monoid or
261+ -- Semigroup instances of the output types:
262+ --
263+ -- >>> import Data.Monoid (Sum)
264+ -- >>> FL.foldl' (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
265+ -- Just (Sum {getSum = 101.0})
266+ --
267+ -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.
268+
269+ , tee
270+ , distribute
271+
272+ -- ** Partitioning
273+ -- |
274+ -- Direct items in the input stream to different folds using a function to
275+ -- select the fold. This is useful to demultiplex the input stream.
276+ , partitionByM
277+ , partitionBy
278+
279+ -- ** Unzipping
280+ , unzip
281+ , unzipWith
282+ , unzipWithM
283+
284+ -- ** Nested Folds
285+ -- , concatMap
286+ -- , groupsOf
287+ , duplicate -- experimental
288+
289+ {-
290+ -- * Splitter scans
291+ -- | Scans that can be used to split and fold a stream using 'grouped'.
292+ , _newline
293+ -}
294+ )
295+where
296+
297+import Data.Functor.Identity (Identity)
298+import Foreign.Storable (Storable(..))
299+import Prelude
300+ hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
301+ foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
302+ notElem, maximum, minimum, head, last, tail, length, null,
303+ reverse, iterate, init, and, or, lookup, foldr1, (!!),
304+ scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
305+ span, splitAt, break)
306+
307+import Streamly.Array (Array)
308+import Streamly.Fold.Types (Fold(..))
309+import Streamly.Streams.Serial (SerialT)
310+import Streamly.Streams.StreamK (IsStream())
311+
312+import Streamly.Strict
313+
314+import Streamly (MonadAsync)
315+import qualified Streamly.Array as A
316+import qualified Streamly.Prelude as S
317+import qualified Streamly.Streams.StreamD as D
318+import qualified Streamly.Streams.StreamK as K
319+import qualified Streamly.Streams.Prelude as P
320+
321+-- $termination
322+--
323+-- We can use the left folds in this module instead of the folds in
324+-- "Streamly.Prelude". For example the following two ways of folding are
325+-- equivalent in functionality and performance,
326+--
327+-- >>> FL.foldl' FL.sum (S.enumerateFromTo 1 100)
328+-- 5050
329+-- >>> S.sum (S.enumerateFromTo 1 100)
330+-- 5050
331+--
332+-- However, left folds are push type folds. That means we push the entire input
333+-- to a fold before we can get the output. Therefore, the performance is
334+-- equivalent only for full folds like 'sum' and 'length'. For partial folds
335+-- like 'head' or 'any' the folds in "Streamly.Prelude" may be much more
336+-- efficient because they are implemented as right folds that terminate as soon
337+-- as we get the result. Note that when a full fold is composed with a partial
338+-- fold in parallel the performance is not impacted as we anyway have to
339+-- consume the whole stream due to the full fold.
340+--
341+-- >>> S.head (1 `S.cons` undefined)
342+-- Just 1
343+-- >>> FL.foldl' FL.head (1 `S.cons` undefined)
344+-- *** Exception: Prelude.undefined
345+--
346+-- However, we can wrap the fold in a scan to convert it into a lazy stream of
347+-- fold steps. We can then terminate the stream whenever we want. For example,
348+--
349+-- >>> S.toList $ S.take 1 $ FL.scanl' FL.head (1 `S.cons` undefined)
350+-- [Nothing]
351+--
352+-- The following example extracts the input stream up to a point where the
353+-- running average of elements is no more than 10:
354+--
355+-- @
356+-- > S.toList
357+-- $ S.map (fromJust . fst)
358+-- $ S.takeWhile (\\(_,x) -> x <= 10)
359+-- $ FL.postscanl' ((,) \<$> FL.last \<*> avg) (S.enumerateFromTo 1.0 100.0)
360+-- @
361+-- @
362+-- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]
363+-- @
364+
365+------------------------------------------------------------------------------
366+-- Running a Fold
367+------------------------------------------------------------------------------
368+
369+-- | Fold a stream using the supplied monadic fold.
370+--
371+-- >>> FL.foldl' FL.sum (S.enumerateFromTo 1 100)
372+-- 5050
373+--
374+-- @since 0.7.0
375+{-# INLINE foldl' #-}
376+foldl' :: Monad m => Fold m a b -> SerialT m a -> m b
377+foldl' (Fold step begin done) = P.foldlMx' step begin done
378+
379+------------------------------------------------------------------------------
380+-- Transformations on fold inputs
381+------------------------------------------------------------------------------
382+
383+-- | @(lmap f fold)@ maps the function @f@ on the input of the fold.
384+--
385+-- >>> FL.foldl' (FL.lmap (\x -> x * x) FL.sum) (S.enumerateFromTo 1 100)
386+-- 338350
387+--
388+-- @since 0.7.0
389+{-# INLINABLE lmap #-}
390+lmap :: (a -> b) -> Fold m b r -> Fold m a r
391+lmap f (Fold step begin done) = Fold step' begin done
392+ where
393+ step' x a = step x (f a)
394+
395+-- | @(lmapM f fold)@ maps the monadic function @f@ on the input of the fold.
396+--
397+-- @since 0.7.0
398+{-# INLINABLE lmapM #-}
399+lmapM :: Monad m => (a -> m b) -> Fold m b r -> Fold m a r
400+lmapM f (Fold step begin done) = Fold step' begin done
401+ where
402+ step' x a = f a >>= step x
403+
404+------------------------------------------------------------------------------
405+-- Utilities
406+------------------------------------------------------------------------------
407+
408+-- | @_Fold1 step@ returns a new 'Fold' using just a step function that has the
409+-- same type for the accumulator and the element. The result type is the
410+-- accumulator type wrapped in 'Maybe'. The initial accumulator is retrieved
411+-- from the 'Foldable', the result is 'None' for empty containers.
412+{-# INLINABLE _Fold1 #-}
413+_Fold1 :: Monad m => (a -> a -> a) -> Fold m a (Maybe a)
414+_Fold1 step = Fold step_ (return Nothing') fromStrictMaybe
415+ where
416+ step_ mx a = return $ Just' $
417+ case mx of
418+ Nothing' -> a
419+ Just' x -> step x a
420+
421+------------------------------------------------------------------------------
422+-- Left folds
423+------------------------------------------------------------------------------
424+
425+------------------------------------------------------------------------------
426+-- Run Effects
427+------------------------------------------------------------------------------
428+
429+-- | A fold that drains all its input, running the effects and discarding the
430+-- results.
431+--
432+-- @since 0.7.0
433+{-# INLINABLE drain #-}
434+drain :: Monad m => Fold m a ()
435+drain = Fold step begin done
436+ where
437+ begin = return ()
438+ step _ _ = return ()
439+ done = return
440+
441+-- | Extract the last element of the input stream, if any.
442+--
443+-- @since 0.7.0
444+{-# INLINABLE last #-}
445+last :: Monad m => Fold m a (Maybe a)
446+last = _Fold1 (flip const)
447+
448+------------------------------------------------------------------------------
449+-- To Summary
450+------------------------------------------------------------------------------
451+
452+-- | Like 'length', except with a more general 'Num' return value
453+--
454+-- @since 0.7.0
455+{-# INLINABLE genericLength #-}
456+genericLength :: (Monad m, Num b) => Fold m a b
457+genericLength = Fold (\n _ -> return $ n + 1) (return 0) return
458+
459+-- | Determine the length of the input stream.
460+--
461+-- @since 0.7.0
462+{-# INLINABLE length #-}
463+length :: Monad m => Fold m a Int
464+length = genericLength
465+
466+-- | Determine the sum of all elements of a stream of numbers. Returns additive
467+-- identity (@0@) when the stream is empty. Note that this is not numerically
468+-- stable for floating point numbers.
469+--
470+-- @since 0.7.0
471+{-# INLINABLE sum #-}
472+sum :: (Monad m, Num a) => Fold m a a
473+sum = Fold (\x a -> return $ x + a) (return 0) return
474+
475+-- | Determine the product of all elements of a stream of numbers. Returns
476+-- multiplicative identity (@1@) when the stream is empty.
477+--
478+-- @since 0.7.0
479+{-# INLINABLE product #-}
480+product :: (Monad m, Num a) => Fold m a a
481+product = Fold (\x a -> return $ x * a) (return 1) return
482+
483+------------------------------------------------------------------------------
484+-- To Summary (Maybe)
485+------------------------------------------------------------------------------
486+
487+-- | Determine the maximum element in a stream using the supplied comparison
488+-- function.
489+--
490+-- @since 0.7.0
491+{-# INLINABLE maximumBy #-}
492+maximumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)
493+maximumBy cmp = _Fold1 max'
494+ where
495+ max' x y = case cmp x y of
496+ GT -> x
497+ _ -> y
498+
499+-- |
500+-- @
501+-- maximum = 'maximumBy' compare
502+-- @
503+--
504+-- Determine the maximum element in a stream.
505+--
506+-- @since 0.7.0
507+{-# INLINABLE maximum #-}
508+maximum :: (Monad m, Ord a) => Fold m a (Maybe a)
509+maximum = _Fold1 max
510+
511+-- | Computes the minimum element with respect to the given comparison function
512+{-# INLINABLE minimumBy #-}
513+minimumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)
514+minimumBy cmp = _Fold1 min'
515+ where
516+ min' x y = case cmp x y of
517+ GT -> y
518+ _ -> x
519+
520+-- | Determine the minimum element in a stream using the supplied comparison
521+-- function.
522+--
523+-- @since 0.7.0
524+{-# INLINABLE minimum #-}
525+minimum :: (Monad m, Ord a) => Fold m a (Maybe a)
526+minimum = _Fold1 min
527+
528+------------------------------------------------------------------------------
529+-- To Summary (Statistical)
530+------------------------------------------------------------------------------
531+
532+-- | Compute a numerically stable arithmetic mean of all elements in the input
533+-- stream.
534+--
535+-- @since 0.7.0
536+{-# INLINABLE mean #-}
537+mean :: (Monad m, Fractional a) => Fold m a a
538+mean = Fold step (return begin) (return . done)
539+ where
540+ begin = Tuple' 0 0
541+ step (Tuple' x n) y = return $
542+ let n' = n + 1
543+ in Tuple' (x + (y - x) / n') n'
544+ done (Tuple' x _) = x
545+
546+-- | Compute a numerically stable (population) variance over all elements in
547+-- the input stream.
548+--
549+-- @since 0.7.0
550+{-# INLINABLE variance #-}
551+variance :: (Monad m, Fractional a) => Fold m a a
552+variance = Fold step (return begin) (return . done)
553+ where
554+ begin = Tuple3' 0 0 0
555+
556+ step (Tuple3' n mean_ m2) x = return $ Tuple3' n' mean' m2'
557+ where
558+ n' = n + 1
559+ mean' = (n * mean_ + x) / (n + 1)
560+ delta = x - mean_
561+ m2' = m2 + delta * delta * n / (n + 1)
562+
563+ done (Tuple3' n _ m2) = m2 / n
564+
565+-- | Compute a numerically stable (population) standard deviation over all
566+-- elements in the input stream.
567+--
568+-- @since 0.7.0
569+{-# INLINABLE stdDev #-}
570+stdDev :: (Monad m, Floating a) => Fold m a a
571+stdDev = sqrt variance
572+
573+------------------------------------------------------------------------------
574+-- Monoidal left folds
575+------------------------------------------------------------------------------
576+
577+-- | Fold an input stream consisting of monoidal elements using 'mappend'
578+-- and 'mempty'.
579+--
580+-- > FL.foldl FL.mconcat (S.map Sum $ S.enumerateFromTo 1 10)
581+--
582+-- @since 0.7.0
583+{-# INLINABLE mconcat #-}
584+mconcat :: (Monad m, Monoid a) => Fold m a a
585+mconcat = Fold (\x a -> return $ mappend x a) (return mempty) return
586+
587+-- |
588+-- > foldMap f = map f mconcat
589+--
590+-- Make a fold from a pure function that folds the output of the function
591+-- using 'mappend' and 'mempty'.
592+--
593+-- > FL.foldl (FL.foldMap Sum) $ S.enumerateFromTo 1 10
594+--
595+-- @since 0.7.0
596+{-# INLINABLE foldMap #-}
597+foldMap :: (Monad m, Monoid b) => (a -> b) -> Fold m a b
598+foldMap f = lmap f mconcat
599+
600+-- |
601+-- > foldMapM f = mapM f mconcat
602+--
603+-- Make a fold from a monadic function that folds the output of the function
604+-- using 'mappend' and 'mempty'.
605+--
606+-- > FL.foldM (FL.foldMapM (return . Sum)) $ S.enumerateFromTo 1 10
607+--
608+-- @since 0.7.0
609+{-# INLINABLE foldMapM #-}
610+foldMapM :: (Monad m, Monoid b) => (a -> m b) -> Fold m a b
611+foldMapM act = Fold step begin done
612+ where
613+ done = return
614+ begin = return mempty
615+ step m a = do
616+ m' <- act a
617+ return $! mappend m m'
618+
619+------------------------------------------------------------------------------
620+-- To Containers
621+------------------------------------------------------------------------------
622+
623+-- | Folds the input stream to a list.
624+--
625+-- /Warning!/ working on large lists accumulated as buffers in memory could be
626+-- very inefficient, consider using "Streamly.Array" instead.
627+--
628+-- @since 0.7.0
629+
630+-- id . (x1 :) . (x2 :) . (x3 :) . ... . (xn :) $ []
631+{-# INLINABLE _toList #-}
632+_toList :: Monad m => Fold m a [a]
633+_toList = Fold (\f x -> return $ f . (x :))
634+ (return id)
635+ (return . ($ []))
636+
637+-- This is more efficient than 'toList'. toList is exactly the same as
638+-- reversing the list after toListRev.
639+--
640+-- | Buffers the input stream to a list in the reverse order of the input.
641+--
642+-- /Warning!/ working on large lists accumulated as buffers in memory could be
643+-- very inefficient, consider using "Streamly.Array" instead.
644+--
645+-- @since 0.7.0
646+
647+-- xn : ... : x2 : x1 : []
648+{-# INLINABLE toListRev #-}
649+toListRev :: Monad m => Fold m a [a]
650+toListRev = Fold (\xs x -> return $ x:xs) (return []) return
651+
652+-- | A fold that buffers its input to a pure stream.
653+--
654+-- /Warning!/ working on large streams accumulated as buffers in memory could
655+-- be very inefficient, consider using "Streamly.Array" instead.
656+--
657+-- @since 0.7.0
658+{-# INLINABLE _toStream #-}
659+_toStream :: Monad m => Fold m a (SerialT Identity a)
660+_toStream = Fold (\f x -> return $ f . (x `K.cons`))
661+ (return id)
662+ (return . ($ K.nil))
663+
664+-- This is more efficient than 'toStream'. toStream is exactly the same as
665+-- reversing the stream after toStreamRev.
666+--
667+-- | Buffers the input stream to a pure stream in the reverse order of the
668+-- input.
669+--
670+-- /Warning!/ working on large streams accumulated as buffers in memory could
671+-- be very inefficient, consider using "Streamly.Array" instead.
672+--
673+-- @since 0.7.0
674+
675+-- xn : ... : x2 : x1 : []
676+{-# INLINABLE toStreamRev #-}
677+toStreamRev :: Monad m => Fold m a (SerialT Identity a)
678+toStreamRev = Fold (\xs x -> return $ x `S.cons` xs) (return S.nil) return
679+
680+------------------------------------------------------------------------------
681+-- Partial Folds
682+------------------------------------------------------------------------------
683+
684+------------------------------------------------------------------------------
685+-- To Elements
686+------------------------------------------------------------------------------
687+
688+-- | Like 'index', except with a more general 'Integral' argument
689+--
690+-- @since 0.7.0
691+{-# INLINABLE genericIndex #-}
692+genericIndex :: (Integral i, Monad m) => i -> Fold m a (Maybe a)
693+genericIndex i = Fold step (return $ Left' 0) done
694+ where
695+ step x a = return $
696+ case x of
697+ Left' j -> if i == j
698+ then Right' a
699+ else Left' (j + 1)
700+ _ -> x
701+ done x = return $
702+ case x of
703+ Left' _ -> Nothing
704+ Right' a -> Just a
705+
706+-- | Lookup the element at the given index.
707+--
708+-- @since 0.7.0
709+{-# INLINABLE index #-}
710+index :: Monad m => Int -> Fold m a (Maybe a)
711+index = genericIndex
712+
713+-- | Extract the first element of the stream, if any.
714+--
715+-- @since 0.7.0
716+{-# INLINABLE head #-}
717+head :: Monad m => Fold m a (Maybe a)
718+head = _Fold1 const
719+
720+-- | Returns the first element that satisfies the given predicate.
721+--
722+-- @since 0.7.0
723+{-# INLINABLE find #-}
724+find :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
725+find predicate = Fold step (return Nothing') fromStrictMaybe
726+ where
727+ step x a = return $
728+ case x of
729+ Nothing' -> if predicate a
730+ then Just' a
731+ else Nothing'
732+ _ -> x
733+
734+-- | In a stream of (key-value) pairs @(a, b)@, return the value @b@ of the
735+-- first pair where the key equals the given value @a@.
736+--
737+-- @since 0.7.0
738+{-# INLINABLE lookup #-}
739+lookup :: (Eq a, Monad m) => a -> Fold m (a,b) (Maybe b)
740+lookup a0 = Fold step (return Nothing') fromStrictMaybe
741+ where
742+ step x (a,b) = return $
743+ case x of
744+ Nothing' -> if a == a0
745+ then Just' b
746+ else Nothing'
747+ _ -> x
748+
749+-- | Convert strict 'Either'' to lazy 'Maybe'
750+{-# INLINABLE hush #-}
751+hush :: Either' a b -> Maybe b
752+hush (Left' _) = Nothing
753+hush (Right' b) = Just b
754+
755+-- | Returns the first index that satisfies the given predicate.
756+--
757+-- @since 0.7.0
758+{-# INLINABLE findIndex #-}
759+findIndex :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)
760+findIndex predicate = Fold step (return $ Left' 0) (return . hush)
761+ where
762+ step x a = return $
763+ case x of
764+ Left' i ->
765+ if predicate a
766+ then Right' i
767+ else Left' (i + 1)
768+ _ -> x
769+
770+-- | Returns the first index where a given value is found in the stream.
771+--
772+-- @since 0.7.0
773+{-# INLINABLE elemIndex #-}
774+elemIndex :: (Eq a, Monad m) => a -> Fold m a (Maybe Int)
775+elemIndex a = findIndex (a ==)
776+
777+------------------------------------------------------------------------------
778+-- To Boolean
779+------------------------------------------------------------------------------
780+
781+-- | Return 'True' if the input stream is empty.
782+--
783+-- @since 0.7.0
784+{-# INLINABLE null #-}
785+null :: Monad m => Fold m a Bool
786+null = Fold (\_ _ -> return False) (return True) return
787+
788+-- |
789+-- > any p = lmap p or
790+--
791+-- | Returns 'True' if any of the elements of a stream satisfies a predicate.
792+--
793+-- @since 0.7.0
794+{-# INLINABLE any #-}
795+any :: Monad m => (a -> Bool) -> Fold m a Bool
796+any predicate = Fold (\x a -> return $ x || predicate a) (return False) return
797+
798+-- | Return 'True' if the given element is present in the stream.
799+--
800+-- @since 0.7.0
801+{-# INLINABLE elem #-}
802+elem :: (Eq a, Monad m) => a -> Fold m a Bool
803+elem a = any (a ==)
804+
805+-- |
806+-- > all p = lmap p and
807+--
808+-- | Returns 'True' if all elements of a stream satisfy a predicate.
809+--
810+-- @since 0.7.0
811+{-# INLINABLE all #-}
812+all :: Monad m => (a -> Bool) -> Fold m a Bool
813+all predicate = Fold (\x a -> return $ x && predicate a) (return True) return
814+
815+-- | Returns 'True' if the given element is not present in the stream.
816+--
817+-- @since 0.7.0
818+{-# INLINABLE notElem #-}
819+notElem :: (Eq a, Monad m) => a -> Fold m a Bool
820+notElem a = all (a /=)
821+
822+-- | Returns 'True' if all elements are 'True', 'False' otherwise
823+--
824+-- @since 0.7.0
825+{-# INLINABLE and #-}
826+and :: Monad m => Fold m Bool Bool
827+and = Fold (\x a -> return $ x && a) (return True) return
828+
829+-- | Returns 'True' if any element is 'True', 'False' otherwise
830+--
831+-- @since 0.7.0
832+{-# INLINABLE or #-}
833+or :: Monad m => Fold m Bool Bool
834+or = Fold (\x a -> return $ x || a) (return False) return
835+
836+------------------------------------------------------------------------------
837+-- Scanning with a Fold
838+------------------------------------------------------------------------------
839+
840+-- | Scan a stream using the given monadic fold.
841+--
842+-- @since 0.7.0
843+{-# INLINE scanl' #-}
844+scanl' :: Monad m => Fold m a b -> SerialT m a -> SerialT m b
845+scanl' (Fold step begin done) = P.scanlMx' step begin done
846+
847+-- | Postscan a stream using the given monadic fold.
848+--
849+-- @since 0.7.0
850+{-# INLINE postscanl' #-}
851+postscanl' :: Monad m => Fold m a b -> SerialT m a -> SerialT m b
852+postscanl' (Fold step begin done) = P.postscanlMx' step begin done
853+
854+-- XXX toPrescanl
855+
856+------------------------------------------------------------------------------
857+-- Filtering
858+------------------------------------------------------------------------------
859+
860+-- | Include only those elements that pass a predicate.
861+--
862+-- >>> FL.foldl (lfilter (> 5) FL.sum) [1..10]
863+-- 40
864+--
865+-- @since 0.7.0
866+{-# INLINABLE lfilter #-}
867+lfilter :: Monad m => (a -> Bool) -> Fold m a r -> Fold m a r
868+lfilter f (Fold step begin done) = Fold step' begin done
869+ where
870+ step' x a = if f a then step x a else return x
871+
872+-- | Like 'lfilter' but with a monadic predicate.
873+--
874+-- @since 0.7.0
875+{-# INLINABLE lfilterM #-}
876+lfilterM :: Monad m => (a -> m Bool) -> Fold m a r -> Fold m a r
877+lfilterM f (Fold step begin done) = Fold step' begin done
878+ where
879+ step' x a = do
880+ use <- f a
881+ if use then step x a else return x
882+
883+-- | Take first 'n' elements from the stream and discard the rest.
884+--
885+-- @since 0.7.0
886+{-# INLINABLE ltake #-}
887+ltake :: Monad m => Int -> Fold m a b -> Fold m a b
888+ltake n (Fold step initial done) = Fold step' initial' done'
889+ where
890+ initial' = fmap (Tuple' 0) initial
891+ step' (Tuple' i r) a = do
892+ if i < n
893+ then do
894+ res <- step r a
895+ return $ Tuple' (i + 1) res
896+ else return $ Tuple' i r
897+ done' (Tuple' _ r) = done r
898+
899+-- | Takes elements from the input as long as the predicate succeeds.
900+--
901+-- @since 0.7.0
902+{-# INLINABLE ltakeWhile #-}
903+ltakeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
904+ltakeWhile predicate (Fold step initial done) = Fold step' initial' done'
905+ where
906+ initial' = fmap Left' initial
907+ step' (Left' r) a = do
908+ if predicate a
909+ then fmap Left' $ step r a
910+ else return (Right' r)
911+ step' r _ = return r
912+ done' (Left' r) = done r
913+ done' (Right' r) = done r
914+
915+------------------------------------------------------------------------------
916+-- Grouping/Splitting
917+------------------------------------------------------------------------------
918+
919+------------------------------------------------------------------------------
920+-- Grouping without looking at elements
921+------------------------------------------------------------------------------
922+--
923+------------------------------------------------------------------------------
924+-- Binary APIs
925+------------------------------------------------------------------------------
926+--
927+
928+{-
929+-- | Split the input stream into two groups at index @n@, the first group
930+-- consisting of elements from index @0@ to index @n - 1@ i.e. the stream
931+-- prefix of length @n@ and the second group consisting of the rest of the
932+-- stream.
933+--
934+splitAt
935+ :: Monad m
936+ => Int
937+ -> Fold m a b
938+ -> Fold m a c
939+ -> Fold m a (b, c)
940+splitAt n = undefined
941+-}
942+
943+------------------------------------------------------------------------------
944+-- N-ary APIs
945+------------------------------------------------------------------------------
946+
947+-- | Group the input stream into groups of @n@ elements each and then fold each
948+-- group using the provided fold function.
949+--
950+-- >> S.toList $ FL.groupsOf 2 FL.sum (S.enumerateFromTo 1 10)
951+-- > [3,7,11,15,19]
952+--
953+-- @since 0.7.0
954+{-# INLINE groupsOf #-}
955+groupsOf
956+ :: (IsStream t, Monad m)
957+ => Int -> Fold m a b -> t m a -> t m b
958+groupsOf n f m = D.fromStreamD $ D.groupsOf n f (D.toStreamD m)
959+
960+------------------------------------------------------------------------------
961+-- Element Aware APIs
962+------------------------------------------------------------------------------
963+--
964+------------------------------------------------------------------------------
965+-- Binary APIs
966+------------------------------------------------------------------------------
967+
968+{-
969+-- | Break the input stream of type (a,Bool) into two groups, the first group
970+-- takes input as long as the boolean is True, the second group takes the rest
971+-- of the input.
972+--
973+-- This is the most general spanning combinator, all others can be implemented
974+-- in terms of this.
975+--
976+spanned
977+ :: Monad m
978+ => Fold m a b
979+ -> Fold m a c
980+ -> Fold m (a, Bool) (b, c)
981+spanned f m = undefined
982+
983+-- | Break the input stream into two groups, the first group takes the input as
984+-- long as the predicate applied to the first element of the stream and next
985+-- input element holds 'True', the second group takes the rest of the input.
986+spanBy
987+ :: Monad m
988+ => (a -> a -> Bool)
989+ -> Fold m a b
990+ -> Fold m a c
991+ -> Fold m a (b, c)
992+spanBy cmp f1 f2 = undefined
993+
994+-- |
995+-- > span p = spanBy (\_ x -> p x)
996+--
997+-- Break the input stream into two groups, the first group takes the input as
998+-- long as the predicate is 'True', the second group takes the rest of the
999+-- input.
1000+span
1001+ :: Monad m
1002+ => (a -> Bool)
1003+ -> Fold m a b
1004+ -> Fold m a c
1005+ -> Fold m a (b, c)
1006+span p = spanBy (\_ x -> p x)
1007+
1008+-- |
1009+-- > break p = span (not . p)
1010+--
1011+-- Break the input stream into two groups, the first group takes the input as
1012+-- long as the predicate is 'False', the second group takes the rest of the
1013+-- input.
1014+break
1015+ :: Monad m
1016+ => (a -> Bool)
1017+ -> Fold m a b
1018+ -> Fold m a c
1019+ -> Fold m a (b, c)
1020+break p = span (not . p)
1021+
1022+-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
1023+-- predicate is applied to the previous and the next input elements.
1024+spanRollingBy
1025+ :: Monad m
1026+ => (a -> a -> Bool)
1027+ -> Fold m a b
1028+ -> Fold m a c
1029+ -> Fold m a (b, c)
1030+spanRollingBy cmp f1 f2 = undefined
1031+-}
1032+
1033+------------------------------------------------------------------------------
1034+-- N-ary APIs
1035+------------------------------------------------------------------------------
1036+--
1037+-- XXX should we use a strict pair?
1038+--
1039+-- | The splitter returns True if the current element is the last element of
1040+-- the group, otherwise returns false.
1041+--
1042+-- @since 0.7.0
1043+{-# INLINE grouped #-}
1044+grouped
1045+ :: (IsStream t, Monad m)
1046+ => Fold m a b
1047+ -> t m (a, Bool)
1048+ -> t m b
1049+grouped f m = D.fromStreamD $ D.grouped f (D.toStreamD m)
1050+
1051+-- An example of a splitter to be used with grouped.
1052+_newline :: IsStream t => t m Char -> t m (Char,Bool)
1053+_newline m = S.foldrS (\x xs ->
1054+ if x == '\n'
1055+ then (x,True) `K.cons` xs
1056+ else (x,False) `K.cons` xs) K.nil m
1057+
1058+-- | Group by applying a predicate to the first element of the current group
1059+-- and the next element in the input stream. The next element is considered
1060+-- part of the current group if the predicate succeeds otherwise a new group
1061+-- starts.
1062+--
1063+-- >>> S.toList $ FL.groupsBy (==) FL.toList $ S.fromList [1,1,2,2]
1064+-- > [[1,1],[2,2]]
1065+--
1066+-- @since 0.7.0
1067+groupsBy
1068+ :: (IsStream t, Monad m)
1069+ => (a -> a -> Bool)
1070+ -> Fold m a b
1071+ -> t m a
1072+ -> t m b
1073+groupsBy cmp f m = D.fromStreamD $ D.groupsBy cmp f (D.toStreamD m)
1074+
1075+{-
1076+-- | Apply a predicate to each new element in the input stream and the last
1077+-- element of the current group. In other words, perform a rolling comparison
1078+-- between two successive elements in the stream. The new element is considered
1079+-- part of the current group if the predicate succeeds otherwise a new group
1080+-- starts.
1081+groupsRollingBy
1082+ :: (IsStream t, Monad m)
1083+ => (a -> a -> Bool)
1084+ -> Fold m a b
1085+ -> t m a
1086+ -> t m b
1087+groupsRollingBy cmp f m = undefined
1088+-}
1089+
1090+-- |
1091+-- > groups = groupsBy (==)
1092+--
1093+-- >>> S.toList $ FL.groups FL.toList $ S.fromList [1,1,2,2]
1094+-- > [[1,1],[2,2]]
1095+--
1096+-- @since 0.7.0
1097+groups :: (IsStream t, Monad m, Eq a) => Fold m a b -> t m a -> t m b
1098+groups = groupsBy (==)
1099+
1100+------------------------------------------------------------------------------
1101+-- Binary splitting on a separator
1102+------------------------------------------------------------------------------
1103+
1104+{-
1105+{-# INLINE breakOn #-}
1106+breakOn :: Monad m => Array a -> Fold m a b -> Fold m a c -> Fold m a (b,c)
1107+breakOn pat f m = undefined
1108+-}
1109+
1110+------------------------------------------------------------------------------
1111+-- N-ary split on a predicate
1112+------------------------------------------------------------------------------
1113+
1114+-- TODO: Use a Splitter configuration similar to the "split" package to make it
1115+-- possible to express all splitting combinations. In general, we can have
1116+-- infix/suffix/prefix/condensing of separators, dropping both leading/trailing
1117+-- separators. We can have a single split operation taking the splitter config
1118+-- as argument.
1119+
1120+-- | Split a stream on separator elements determined by a predicate, dropping
1121+-- the separator. Separators are not considered part of stream segments on
1122+-- either side of it instead they are treated as infixed between two stream
1123+-- segments. For example, with @.@ as separator, @"a.b.c"@ would be parsed as
1124+-- @["a","b","c"]@. When @.@ is in leading or trailing position it is still
1125+-- considered as infixed, treating the first or the last segment as empty. For
1126+-- example, @".a."@ would be parsed as @["","a",""]@. This operation is
1127+-- opposite of 'intercalate'.
1128+--
1129+-- Let's use the following definition for illustration:
1130+--
1131+-- > splitBy_ p xs = S.toList $ FL.splitBy p (FL.toList) (S.fromList xs)
1132+--
1133+-- >>> splitBy_ (== '.') ""
1134+-- [""]
1135+--
1136+-- >>> splitBy_ (== '.') "."
1137+-- ["",""]
1138+--
1139+-- >>> splitBy_ (== '.') ".a"
1140+-- > ["","a"]
1141+--
1142+-- >>> splitBy_ (== '.') "a."
1143+-- > ["a",""]
1144+--
1145+-- >>> splitBy_ (== '.') "a.b"
1146+-- > ["a","b"]
1147+--
1148+-- >>> splitBy_ (== '.') "a..b"
1149+-- > ["a","","b"]
1150+--
1151+-- @since 0.7.0
1152+{-# INLINE splitBy #-}
1153+splitBy
1154+ :: (IsStream t, Monad m)
1155+ => (a -> Bool) -> Fold m a b -> t m a -> t m b
1156+splitBy predicate f m =
1157+ D.fromStreamD $ D.splitBy predicate f (D.toStreamD m)
1158+
1159+-- | Like 'splitBy' but the separator is treated as part of the previous
1160+-- stream segment (suffix). Therefore, when the separator is in trailing
1161+-- position, no empty segment is considered to follow it. For example, @"a.b."@
1162+-- would be parsed as @["a","b"]@ instead of @["a","b",""]@ as in the case of
1163+-- 'splitBy'.
1164+--
1165+-- > splitSuffixBy_ p xs = S.toList $ FL.splitSuffixBy p (FL.toList) (S.fromList xs)
1166+--
1167+-- >>> splitSuffixBy_ (== '.') ""
1168+-- []
1169+--
1170+-- >>> splitSuffixBy_ (== '.') "."
1171+-- [""]
1172+--
1173+-- >>> splitSuffixBy_ (== '.') "a"
1174+-- ["a"]
1175+--
1176+-- >>> splitSuffixBy_ (== '.') ".a"
1177+-- > ["","a"]
1178+--
1179+-- >>> splitSuffixBy_ (== '.') "a."
1180+-- > ["a"]
1181+--
1182+-- >>> splitSuffixBy_ (== '.') "a.b"
1183+-- > ["a","b"]
1184+--
1185+-- >>> splitSuffixBy_ (== '.') "a.b."
1186+-- > ["a","b"]
1187+--
1188+-- >>> splitSuffixBy_ (== '.') "a..b.."
1189+-- > ["a","","b",""]
1190+--
1191+-- > lines = splitSuffixBy (== '\n')
1192+--
1193+-- @since 0.7.0
1194+{-# INLINE splitSuffixBy #-}
1195+splitSuffixBy
1196+ :: (IsStream t, Monad m)
1197+ => (a -> Bool) -> Fold m a b -> t m a -> t m b
1198+splitSuffixBy predicate f m =
1199+ D.fromStreamD $ D.splitSuffixBy predicate f (D.toStreamD m)
1200+
1201+-- | Like 'splitBy' but ignores repeated separators or separators in leading
1202+-- or trailing position. Therefore, @"..a..b.."@ would be parsed as
1203+-- @["a","b"]@. In other words, it treats the input like words separated by
1204+-- whitespace elements determined by the predicate.
1205+--
1206+-- > wordsBy' p xs = S.toList $ FL.wordsBy p (FL.toList) (S.fromList xs)
1207+--
1208+-- >>> wordsBy' (== ',') ""
1209+-- > []
1210+--
1211+-- >>> wordsBy' (== ',') ","
1212+-- > []
1213+--
1214+-- >>> wordsBy' (== ',') ",a,,b,"
1215+-- > ["a","b"]
1216+--
1217+-- > words = wordsBy isSpace
1218+--
1219+-- @since 0.7.0
1220+{-# INLINE wordsBy #-}
1221+wordsBy
1222+ :: (IsStream t, Monad m)
1223+ => (a -> Bool) -> Fold m a b -> t m a -> t m b
1224+wordsBy predicate f m =
1225+ D.fromStreamD $ D.wordsBy predicate f (D.toStreamD m)
1226+
1227+-- XXX we should express this using the Splitter config.
1228+--
1229+-- We can get splitSuffixBy' by appending the suffix to the output segments
1230+-- produced by splitSuffixBy. However, it may add an additional suffix if the last
1231+-- fragment did not have a suffix in the first place.
1232+
1233+-- | Like 'splitSuffixBy' but keeps the suffix in the splits.
1234+--
1235+-- > splitSuffixBy'_ p xs = S.toList $ FL.splitSuffixBy' p (FL.toList) (S.fromList xs)
1236+--
1237+-- >>> splitSuffixBy'_ (== '.') ""
1238+-- []
1239+--
1240+-- >>> splitSuffixBy'_ (== '.') "."
1241+-- ["."]
1242+--
1243+-- >>> splitSuffixBy'_ (== '.') "a"
1244+-- ["a"]
1245+--
1246+-- >>> splitSuffixBy'_ (== '.') ".a"
1247+-- > [".","a"]
1248+--
1249+-- >>> splitSuffixBy'_ (== '.') "a."
1250+-- > ["a."]
1251+--
1252+-- >>> splitSuffixBy'_ (== '.') "a.b"
1253+-- > ["a.","b"]
1254+--
1255+-- >>> splitSuffixBy'_ (== '.') "a.b."
1256+-- > ["a.","b."]
1257+--
1258+-- >>> splitSuffixBy'_ (== '.') "a..b.."
1259+-- > ["a.",".","b.","."]
1260+--
1261+-- @since 0.7.0
1262+{-# INLINE _splitSuffixBy' #-}
1263+_splitSuffixBy'
1264+ :: (IsStream t, Monad m)
1265+ => (a -> Bool) -> Fold m a b -> t m a -> t m b
1266+_splitSuffixBy' predicate f m = grouped f (S.map (\a -> (a, predicate a)) m)
1267+
1268+------------------------------------------------------------------------------
1269+-- Split on a delimiter
1270+------------------------------------------------------------------------------
1271+
1272+-- Int list examples for splitOn:
1273+--
1274+-- >>> splitList [] [1,2,3,3,4]
1275+-- > [[1],[2],[3],[3],[4]]
1276+--
1277+-- >>> splitList [5] [1,2,3,3,4]
1278+-- > [[1,2,3,3,4]]
1279+--
1280+-- >>> splitList [1] [1,2,3,3,4]
1281+-- > [[],[2,3,3,4]]
1282+--
1283+-- >>> splitList [4] [1,2,3,3,4]
1284+-- > [[1,2,3,3],[]]
1285+--
1286+-- >>> splitList [2] [1,2,3,3,4]
1287+-- > [[1],[3,3,4]]
1288+--
1289+-- >>> splitList [3] [1,2,3,3,4]
1290+-- > [[1,2],[],[4]]
1291+--
1292+-- >>> splitList [3,3] [1,2,3,3,4]
1293+-- > [[1,2],[4]]
1294+--
1295+-- >>> splitList [1,2,3,3,4] [1,2,3,3,4]
1296+-- > [[],[]]
1297+
1298+-- | Split the stream on both sides of a separator sequence, dropping the
1299+-- separator.
1300+--
1301+-- For illustration, let's define a function that operates on pure lists:
1302+--
1303+-- @
1304+-- splitOn_ pat xs = S.toList $ FL.splitOn (A.fromList pat) (FL.toList) (S.fromList xs)
1305+-- @
1306+--
1307+-- >>> splitOn_ "" "hello"
1308+-- > ["h","e","l","l","o"]
1309+--
1310+-- >>> splitOn_ "hello" ""
1311+-- > [""]
1312+--
1313+-- >>> splitOn_ "hello" "hello"
1314+-- > ["",""]
1315+--
1316+-- >>> splitOn_ "x" "hello"
1317+-- > ["hello"]
1318+--
1319+-- >>> splitOn_ "h" "hello"
1320+-- > ["","ello"]
1321+--
1322+-- >>> splitOn_ "o" "hello"
1323+-- > ["hell",""]
1324+--
1325+-- >>> splitOn_ "e" "hello"
1326+-- > ["h","llo"]
1327+--
1328+-- >>> splitOn_ "l" "hello"
1329+-- > ["he","","o"]
1330+--
1331+-- >>> splitOn_ "ll" "hello"
1332+-- > ["he","o"]
1333+--
1334+-- 'splitOn' is an inverse of 'intercalate'. The following law always holds:
1335+--
1336+-- > intercalate . splitOn == id
1337+--
1338+-- The following law holds when the separator is non-empty and contains none of
1339+-- the elements present in the input lists:
1340+--
1341+-- > splitOn . intercalate == id
1342+--
1343+-- The following law always holds:
1344+--
1345+-- > concat . splitOn . intercalate == concat
1346+--
1347+-- @since 0.7.0
1348+{-# INLINE splitOn #-}
1349+splitOn
1350+ :: (IsStream t, Monad m, Storable a, Enum a, Eq a)
1351+ => Array a -> Fold m a b -> t m a -> t m b
1352+splitOn patt f m = D.fromStreamD $ D.splitOn patt f (D.toStreamD m)
1353+
1354+{-
1355+-- This can be implemented easily using Rabin Karp
1356+-- | Split on any one of the given patterns.
1357+{-# INLINE splitOnAny #-}
1358+splitOnAny
1359+ :: (IsStream t, Monad m, Storable a, Integral a)
1360+ => [Array a] -> Fold m a b -> t m a -> t m b
1361+splitOnAny subseq f m = undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.toStreamD m)
1362+-}
1363+
1364+-- | Like 'splitSuffixBy' but the separator is a sequence of elements, instead
1365+-- of a predicate for a single element.
1366+--
1367+-- > splitSuffixOn_ pat xs = S.toList $ FL.splitSuffixOn (A.fromList pat) (FL.toList) (S.fromList xs)
1368+--
1369+-- >>> splitSuffixOn_ "." ""
1370+-- [""]
1371+--
1372+-- >>> splitSuffixOn_ "." "."
1373+-- [""]
1374+--
1375+-- >>> splitSuffixOn_ "." "a"
1376+-- ["a"]
1377+--
1378+-- >>> splitSuffixOn_ "." ".a"
1379+-- > ["","a"]
1380+--
1381+-- >>> splitSuffixOn_ "." "a."
1382+-- > ["a"]
1383+--
1384+-- >>> splitSuffixOn_ "." "a.b"
1385+-- > ["a","b"]
1386+--
1387+-- >>> splitSuffixOn_ "." "a.b."
1388+-- > ["a","b"]
1389+--
1390+-- >>> splitSuffixOn_ "." "a..b.."
1391+-- > ["a","","b",""]
1392+--
1393+-- > lines = splitSuffixOn "\n"
1394+--
1395+-- @since 0.7.0
1396+{-# INLINE splitSuffixOn #-}
1397+splitSuffixOn
1398+ :: (IsStream t, Monad m, Storable a, Enum a, Eq a)
1399+ => Array a -> Fold m a b -> t m a -> t m b
1400+splitSuffixOn patt f m =
1401+ D.fromStreamD $ D.splitSuffixOn False patt f (D.toStreamD m)
1402+
1403+{-
1404+-- | Like 'splitOn' but drops any empty splits.
1405+--
1406+{-# INLINE wordsOn #-}
1407+wordsOn
1408+ :: (IsStream t, Monad m, Storable a, Eq a)
1409+ => Array a -> Fold m a b -> t m a -> t m b
1410+wordsOn subseq f m = undefined -- D.fromStreamD $ D.wordsOn f subseq (D.toStreamD m)
1411+-}
1412+
1413+-- XXX use a non-monadic intersperse to remove the MonadAsync constraint.
1414+--
1415+-- | Like 'splitOn' but splits the separator as well, as an infix token.
1416+--
1417+-- > splitOn'_ pat xs = S.toList $ FL.splitOn' (A.fromList pat) (FL.toList) (S.fromList xs)
1418+--
1419+-- >>> splitOn'_ "" "hello"
1420+-- > ["h","","e","","l","","l","","o"]
1421+--
1422+-- >>> splitOn'_ "hello" ""
1423+-- > [""]
1424+--
1425+-- >>> splitOn'_ "hello" "hello"
1426+-- > ["","hello",""]
1427+--
1428+-- >>> splitOn'_ "x" "hello"
1429+-- > ["hello"]
1430+--
1431+-- >>> splitOn'_ "h" "hello"
1432+-- > ["","h","ello"]
1433+--
1434+-- >>> splitOn'_ "o" "hello"
1435+-- > ["hell","o",""]
1436+--
1437+-- >>> splitOn'_ "e" "hello"
1438+-- > ["h","e","llo"]
1439+--
1440+-- >>> splitOn'_ "l" "hello"
1441+-- > ["he","l","","l","o"]
1442+--
1443+-- >>> splitOn'_ "ll" "hello"
1444+-- > ["he","ll","o"]
1445+--
1446+-- @since 0.7.0
1447+{-# INLINE splitOn' #-}
1448+splitOn'
1449+ :: (IsStream t, MonadAsync m, Storable a, Enum a, Eq a)
1450+ => Array a -> Fold m a b -> t m a -> t m b
1451+splitOn' patt f m = S.intersperseM
1452+ (foldl' f (A.toStream patt)) $ splitOn patt f m
1453+
1454+-- | Like 'splitSuffixOn' but keeps the suffix intact in the splits.
1455+--
1456+-- > splitSuffixOn'_ pat xs = S.toList $ FL.splitSuffixOn' (A.fromList pat) (FL.toList) (S.fromList xs)
1457+--
1458+-- >>> splitSuffixOn'_ "." ""
1459+-- [""]
1460+--
1461+-- >>> splitSuffixOn'_ "." "."
1462+-- ["."]
1463+--
1464+-- >>> splitSuffixOn'_ "." "a"
1465+-- ["a"]
1466+--
1467+-- >>> splitSuffixOn'_ "." ".a"
1468+-- > [".","a"]
1469+--
1470+-- >>> splitSuffixOn'_ "." "a."
1471+-- > ["a."]
1472+--
1473+-- >>> splitSuffixOn'_ "." "a.b"
1474+-- > ["a.","b"]
1475+--
1476+-- >>> splitSuffixOn'_ "." "a.b."
1477+-- > ["a.","b."]
1478+--
1479+-- >>> splitSuffixOn'_ "." "a..b.."
1480+-- > ["a.",".","b.","."]
1481+--
1482+-- @since 0.7.0
1483+{-# INLINE splitSuffixOn' #-}
1484+splitSuffixOn'
1485+ :: (IsStream t, Monad m, Storable a, Enum a, Eq a)
1486+ => Array a -> Fold m a b -> t m a -> t m b
1487+splitSuffixOn' patt f m =
1488+ D.fromStreamD $ D.splitSuffixOn True patt f (D.toStreamD m)
1489+
1490+{-
1491+-- This can be implemented easily using Rabin Karp
1492+-- | Split post any one of the given patterns.
1493+{-# INLINE splitSuffixOnAny #-}
1494+splitSuffixOnAny
1495+ :: (IsStream t, Monad m, Storable a, Integral a)
1496+ => [Array a] -> Fold m a b -> t m a -> t m b
1497+splitSuffixOnAny subseq f m = undefined
1498+ -- D.fromStreamD $ D.splitPostAny f subseq (D.toStreamD m)
1499+-}
1500+
1501+------------------------------------------------------------------------------
1502+-- Grouped by order
1503+------------------------------------------------------------------------------
1504+
1505+{-
1506+-- Buffer until the next element in sequence arrives. The function argument
1507+-- determines the difference in sequence numbers. This could be useful in
1508+-- implementing sequenced streams, for example, TCP reassembly.
1509+{-# INLINE foldOrderedBy #-}
1510+foldOrderedBy
1511+ :: (IsStream t, Monad m)
1512+ => (forall n. Monad n => Fold n a b)
1513+ -> (a -> a -> Int)
1514+ -> t m a
1515+ -> t m b
1516+foldOrderedBy = undefined
1517+-}
1518+
1519+------------------------------------------------------------------------------
1520+-- Distributing
1521+------------------------------------------------------------------------------
1522+--
1523+-- | Distribute one copy of the stream to each fold and zip the results.
1524+--
1525+-- @
1526+-- |-------Fold m a b--------|
1527+-- ---stream m a---| |---m (b,c)
1528+-- |-------Fold m a c--------|
1529+-- @
1530+-- >>> FL.foldl' (FL.tee FL.sum FL.length) (S.enumerateFromTo 1.0 100.0)
1531+-- (5050.0,100)
1532+--
1533+-- @since 0.7.0
1534+{-# INLINE tee #-}
1535+tee :: Monad m => Fold m a b -> Fold m a c -> Fold m a (b,c)
1536+tee f1 f2 = (,) <$> f1 <*> f2
1537+
1538+{-# INLINE foldNil #-}
1539+foldNil :: Monad m => Fold m a [b]
1540+foldNil = Fold step begin done where
1541+ begin = return []
1542+ step _ _ = return []
1543+ done = return
1544+
1545+{-# INLINE foldCons #-}
1546+foldCons :: Monad m => Fold m a b -> Fold m a [b] -> Fold m a [b]
1547+foldCons (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
1548+ Fold step begin done
1549+
1550+ where
1551+
1552+ begin = Tuple' <$> beginL <*> beginR
1553+ step (Tuple' xL xR) a = Tuple' <$> stepL xL a <*> stepR xR a
1554+ done (Tuple' xL xR) = (:) <$> (doneL xL) <*> (doneR xR)
1555+
1556+-- XXX use "List" instead of "[]"?, use Array for output to scale it to a large
1557+-- number of consumers?
1558+--
1559+-- | Distribute one copy of the stream to each fold and collect the results in
1560+-- a container.
1561+--
1562+-- @
1563+--
1564+-- |-------Fold m a b--------|
1565+-- ---stream m a---| |---m [b]
1566+-- |-------Fold m a b--------|
1567+-- | |
1568+-- ...
1569+-- @
1570+--
1571+-- >>> FL.foldl' (FL.distribute [FL.sum, FL.length]) (S.enumerateFromTo 1 5)
1572+-- [15,5]
1573+--
1574+-- This is the consumer side dual of the producer side 'sequence' operation.
1575+--
1576+-- @since 0.7.0
1577+{-# INLINE distribute #-}
1578+distribute :: Monad m => [Fold m a b] -> Fold m a [b]
1579+distribute [] = foldNil
1580+distribute (x:xs) = foldCons x (distribute xs)
1581+
1582+------------------------------------------------------------------------------
1583+-- Partitioning
1584+------------------------------------------------------------------------------
1585+--
1586+-- | Partition the input over two folds using an 'Either' partitioning
1587+-- predicate.
1588+--
1589+-- @
1590+--
1591+-- |-------Fold b x--------|
1592+-- -----stream m a --> (Either b c)----| |----(x,y)
1593+-- |-------Fold c y--------|
1594+-- @
1595+--
1596+-- Send input to either fold randomly:
1597+--
1598+-- >>> import System.Random (randomIO)
1599+-- >>> randomly a = randomIO >>= \x -> return $ if x then Left a else Right a
1600+-- >>> FL.foldl' (FL.partitionByM randomly FL.length FL.length) (S.enumerateFromTo 1 100)
1601+-- (59,41)
1602+--
1603+-- Send input to the two folds in a proportion of 2:1:
1604+--
1605+-- @
1606+-- import Data.IORef (newIORef, readIORef, writeIORef)
1607+-- proportionately m n = do
1608+-- ref <- newIORef $ cycle $ concat [replicate m Left, replicate n Right]
1609+-- return $ \\a -> do
1610+-- r <- readIORef ref
1611+-- writeIORef ref $ tail r
1612+-- return $ head r a
1613+--
1614+-- main = do
1615+-- f <- proportionately 2 1
1616+-- r <- FL.foldl' (FL.partitionByM f FL.length FL.length) (S.enumerateFromTo (1 :: Int) 100)
1617+-- print r
1618+-- @
1619+-- @
1620+-- (67,33)
1621+-- @
1622+--
1623+-- This is the consumer side dual of the producer side 'mergeBy' operation.
1624+--
1625+-- @since 0.7.0
1626+{-# INLINE partitionByM #-}
1627+partitionByM :: Monad m
1628+ => (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)
1629+partitionByM f (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
1630+
1631+ Fold step begin done
1632+
1633+ where
1634+
1635+ begin = Tuple' <$> beginL <*> beginR
1636+ step (Tuple' xL xR) a = do
1637+ r <- f a
1638+ case r of
1639+ Left b -> Tuple' <$> stepL xL b <*> return xR
1640+ Right c -> Tuple' <$> return xL <*> stepR xR c
1641+ done (Tuple' xL xR) = (,) <$> doneL xL <*> doneR xR
1642+
1643+-- Note: we could use (a -> Bool) instead of (a -> Either b c), but the latter
1644+-- makes the signature clearer as to which case belongs to which fold.
1645+-- XXX need to check the performance in both cases.
1646+
1647+-- | Same as 'partitionByM' but with a pure partition function.
1648+--
1649+-- Count even and odd numbers in a stream:
1650+--
1651+-- @
1652+-- >>> let f = FL.partitionBy (\\n -> if even n then Left n else Right n)
1653+-- (fmap (("Even " ++) . show) FL.length)
1654+-- (fmap (("Odd " ++) . show) FL.length)
1655+-- in FL.foldl' f (S.enumerateFromTo 1 100)
1656+-- ("Even 50","Odd 50")
1657+-- @
1658+--
1659+-- @since 0.7.0
1660+{-# INLINE partitionBy #-}
1661+partitionBy :: Monad m
1662+ => (a -> Either b c) -> Fold m b x -> Fold m c y -> Fold m a (x, y)
1663+partitionBy f = partitionByM (return . f)
1664+
1665+{-
1666+-- | Send one item to each fold in a round-robin fashion. This is the consumer
1667+-- side dual of producer side 'mergeN' operation.
1668+--
1669+-- partitionN :: Monad m => [Fold m a b] -> Fold m a [b]
1670+-- partitionN fs = Fold step begin done
1671+
1672+-- | Demultiplex an input element into a number of typed variants. We want to
1673+-- statically restrict the target values within a set of predefined types, an
1674+-- enumeration of a GADT. We also want to make sure that the Map contains only
1675+-- those types and the full set of those types. Instead of Map it should
1676+-- probably be a lookup-table using an array and not in GC memory.
1677+--
1678+-- This is the consumer side dual of the producer side 'mux' operation.
1679+--
1680+-- demux :: (Monad m, Ord k)
1681+-- => (a -> k) -> Map k (Fold m a b) -> Fold m a (Map k b)
1682+-- demux f kv = Fold step begin done
1683+-}
1684+
1685+------------------------------------------------------------------------------
1686+-- Unzipping
1687+------------------------------------------------------------------------------
1688+--
1689+-- | Like 'unzipWith' but with a monadic splitter function.
1690+--
1691+-- @since 0.7.0
1692+{-# INLINE unzipWithM #-}
1693+unzipWithM :: Monad m
1694+ => (a -> m (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)
1695+unzipWithM f (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
1696+ Fold step begin done
1697+
1698+ where
1699+
1700+ step (Tuple' xL xR) a = do
1701+ (b,c) <- f a
1702+ Tuple' <$> stepL xL b <*> stepR xR c
1703+ begin = Tuple' <$> beginL <*> beginR
1704+ done (Tuple' xL xR) = (,) <$> doneL xL <*> doneR xR
1705+
1706+-- | Split elements in the input stream into two parts using a pure splitter
1707+-- function, direct each part to a different fold and zip the results.
1708+--
1709+-- @since 0.7.0
1710+{-# INLINE unzipWith #-}
1711+unzipWith :: Monad m
1712+ => (a -> (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)
1713+unzipWith f = unzipWithM (return . f)
1714+
1715+-- | Send the elements of tuples in a stream of tuples through two different
1716+-- folds.
1717+--
1718+-- @
1719+--
1720+-- |-------Fold a x--------|
1721+-- -----Stream m x----(a,b)--| |----m (x,y)
1722+-- |-------Fold b y--------|
1723+--
1724+-- @
1725+--
1726+-- This is the consumer side dual of the producer side 'zip' operation.
1727+--
1728+-- @since 0.7.0
1729+unzip :: Monad m => Fold m a x -> Fold m b y -> Fold m (a,b) (x,y)
1730+unzip = unzipWith id
1731+
1732+------------------------------------------------------------------------------
1733+-- Nesting
1734+------------------------------------------------------------------------------
1735+--
1736+-- | Modify the fold such that when the fold is done, instead of returning the
1737+-- accumulator, it returns a fold. The returned fold starts from where we left
1738+-- i.e. it uses the last accumulator value as the initial value of the
1739+-- accumulator. Thus we can resume the fold later and feed it more input.
1740+--
1741+-- >> do
1742+-- > more <- FL.foldl (FL.duplicate FL.sum) (S.enumerateFromTo 1 10)
1743+-- > evenMore <- FL.foldl (FL.duplicate more) (S.enumerateFromTo 11 20)
1744+-- > FL.foldl evenMore (S.enumerateFromTo 21 30)
1745+-- > 465
1746+--
1747+-- @since 0.7.0
1748+{-# INLINABLE duplicate #-}
1749+duplicate :: Applicative m => Fold m a b -> Fold m a (Fold m a b)
1750+duplicate (Fold step begin done) =
1751+ Fold step begin (\x -> pure (Fold step (pure x) done))
1752+
1753+{-
1754+-- All the stream flattening transformations can also be applied to a fold
1755+-- input stream.
1756+
1757+-- | This can be used to apply all the stream generation operations on folds.
1758+lconcatMap ::(IsStream t, Monad m) => (a -> t m b)
1759+ -> Fold m b c
1760+ -> Fold m a c
1761+lconcatMap s f1 f2 = undefined
1762+
1763+-- All the grouping transformation that we apply to a stream can also be
1764+-- applied to a fold input stream.
1765+
1766+-- | Group the input elements of a fold input stream by some criterion and fold
1767+-- each group using a given fold before its fed to the fold.
1768+--
1769+-- For example, we can copy and distribute a stream to multiple folds and then
1770+-- in each fold we can group the input differently e.g. by one second, one
1771+-- minute and one hour windows respectively and fold each resulting stream of
1772+-- folds.
1773+--
1774+-- @
1775+--
1776+-- -----Fold m a b----|-Fold n a c-|-Fold n a c-|-...-|----Fold m a c
1777+--
1778+-- @
1779+lgroupsOf :: Int -> Fold m a b -> Fold m b c -> Fold m a c
1780+lgroupsOf n f1 f2 = undefined
1781+-}
src/Streamly/Fold/Types.hsnew file+172
@@ -0,0 +1,172 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ExistentialQuantification #-}
3+
4+-- |
5+-- Module : Streamly.Fold.Types
6+-- Copyright : (c) 2019 Composewell Technologies
7+-- (c) 2013 Gabriel Gonzalez
8+-- License : BSD3
9+-- Maintainer : harendra.kumar@gmail.com
10+-- Stability : experimental
11+-- Portability : GHC
12+
13+module Streamly.Fold.Types
14+ ( Fold (..)
15+ )
16+where
17+
18+import Control.Applicative (liftA2)
19+import Data.Semigroup (Semigroup(..))
20+import Streamly.Strict (Tuple'(..))
21+
22+------------------------------------------------------------------------------
23+-- Monadic left folds
24+------------------------------------------------------------------------------
25+
26+-- | Represents a left fold over an input stream of values of type @a@ to a
27+-- single value of type @b@ in 'Monad' @m@.
28+
29+-- The fold uses an intermediate type @x@ as accumulator. The fold accumulator
30+-- is initialized by calling the @init@ function and is then driven by calling
31+-- the step function repeatedly. When the fold is done the @extract@ function
32+-- is used to map the intermediate type @x@ to the final type @b@. This allows
33+-- the state of the fold to be embedded in an arbitrary type @x@.
34+--
35+data Fold m a b =
36+ -- | @Fold @ @ step @ @ initial @ @ extract@
37+ forall x. Fold (x -> a -> m x) (m x) (x -> m b)
38+
39+-- | Maps a function on the output of the fold (the type @b@).
40+instance Applicative m => Functor (Fold m a) where
41+ {-# INLINE fmap #-}
42+ fmap f (Fold step start done) = Fold step start done'
43+ where
44+ done' x = fmap f $! done x
45+
46+ {-# INLINE (<$) #-}
47+ (<$) b = \_ -> pure b
48+
49+-- | The input (type @a@) of the composed fold is distributed to both the
50+-- constituent folds. The outputs of the constituent folds (type @b@) are
51+-- applied to a function.
52+instance Applicative m => Applicative (Fold m a) where
53+ {-# INLINE pure #-}
54+ pure b = Fold (\() _ -> pure ()) (pure ()) (\() -> pure b)
55+
56+ {-# INLINE (<*>) #-}
57+ (Fold stepL beginL doneL) <*> (Fold stepR beginR doneR) =
58+ let step (Tuple' xL xR) a = Tuple' <$> stepL xL a <*> stepR xR a
59+ begin = Tuple' <$> beginL <*> beginR
60+ done (Tuple' xL xR) = doneL xL <*> doneR xR
61+ in Fold step begin done
62+
63+ {-# INLINE (<*) #-}
64+ (<*) m = \_ -> m
65+
66+ {-# INLINE (*>) #-}
67+ _ *> m = m
68+
69+-- | Combines the outputs of the folds (the type @b@) using their 'Semigroup'
70+-- instances.
71+instance (Semigroup b, Monad m) => Semigroup (Fold m a b) where
72+ {-# INLINE (<>) #-}
73+ (<>) = liftA2 (<>)
74+
75+-- | Combines the outputs of the folds (the type @b@) using their 'Monoid'
76+-- instances.
77+instance (Semigroup b, Monoid b, Monad m) => Monoid (Fold m a b) where
78+ {-# INLINE mempty #-}
79+ mempty = pure mempty
80+
81+ {-# INLINE mappend #-}
82+ mappend = (<>)
83+
84+-- | Combines the fold outputs (type @b@) using their 'Num' instances.
85+instance (Monad m, Num b) => Num (Fold m a b) where
86+ {-# INLINE fromInteger #-}
87+ fromInteger = pure . fromInteger
88+
89+ {-# INLINE negate #-}
90+ negate = fmap negate
91+
92+ {-# INLINE abs #-}
93+ abs = fmap abs
94+
95+ {-# INLINE signum #-}
96+ signum = fmap signum
97+
98+ {-# INLINE (+) #-}
99+ (+) = liftA2 (+)
100+
101+ {-# INLINE (*) #-}
102+ (*) = liftA2 (*)
103+
104+ {-# INLINE (-) #-}
105+ (-) = liftA2 (-)
106+
107+-- | Combines the fold outputs (type @b@) using their 'Fractional' instances.
108+instance (Monad m, Fractional b) => Fractional (Fold m a b) where
109+ {-# INLINE fromRational #-}
110+ fromRational = pure . fromRational
111+
112+ {-# INLINE recip #-}
113+ recip = fmap recip
114+
115+ {-# INLINE (/) #-}
116+ (/) = liftA2 (/)
117+
118+-- | Combines the fold outputs using their 'Floating' instances.
119+instance (Monad m, Floating b) => Floating (Fold m a b) where
120+ {-# INLINE pi #-}
121+ pi = pure pi
122+
123+ {-# INLINE exp #-}
124+ exp = fmap exp
125+
126+ {-# INLINE sqrt #-}
127+ sqrt = fmap sqrt
128+
129+ {-# INLINE log #-}
130+ log = fmap log
131+
132+ {-# INLINE sin #-}
133+ sin = fmap sin
134+
135+ {-# INLINE tan #-}
136+ tan = fmap tan
137+
138+ {-# INLINE cos #-}
139+ cos = fmap cos
140+
141+ {-# INLINE asin #-}
142+ asin = fmap asin
143+
144+ {-# INLINE atan #-}
145+ atan = fmap atan
146+
147+ {-# INLINE acos #-}
148+ acos = fmap acos
149+
150+ {-# INLINE sinh #-}
151+ sinh = fmap sinh
152+
153+ {-# INLINE tanh #-}
154+ tanh = fmap tanh
155+
156+ {-# INLINE cosh #-}
157+ cosh = fmap cosh
158+
159+ {-# INLINE asinh #-}
160+ asinh = fmap asinh
161+
162+ {-# INLINE atanh #-}
163+ atanh = fmap atanh
164+
165+ {-# INLINE acosh #-}
166+ acosh = fmap acosh
167+
168+ {-# INLINE (**) #-}
169+ (**) = liftA2 (**)
170+
171+ {-# INLINE logBase #-}
172+ logBase = liftA2 logBase
src/Streamly/Prelude.hs+71−42
@@ -102,7 +102,6 @@ module Streamly.Prelude
102 -- these can be expressed in terms of primitives.
102 -- these can be expressed in terms of primitives.
103 , P.fromList
103 , P.fromList
104 , fromListM
104 , fromListM
105− -- , P.fromArray
106 , K.fromFoldable
105 , K.fromFoldable
107 , fromFoldableM
106 , fromFoldableM
108
107
@@ -183,16 +182,17 @@ module Streamly.Prelude
183 , foldl1'
182 , foldl1'
184 , foldlM'
183 , foldlM'
185
184
186− -- ** Strict Full Folds
185+ -- ** Full Folds
187 -- | Folds that are guaranteed to evaluate the whole stream.
186 -- | Folds that are guaranteed to evaluate the whole stream.
188
187
189 -- -- ** To Summary (Full Folds)
188 -- -- ** To Summary (Full Folds)
190 -- -- | Folds that summarize the stream to a single value.
189 -- -- | Folds that summarize the stream to a single value.
191− , runStream
190+ , drain
192 , last
191 , last
193 , length
192 , length
194 , sum
193 , sum
195 , product
194 , product
195+ --, mconcat
196
196
197 -- -- ** To Summary (Maybe) (Full Folds)
197 -- -- ** To Summary (Maybe) (Full Folds)
198 -- -- | Folds that summarize a non-empty stream to a 'Just' value and return
198 -- -- | Folds that summarize a non-empty stream to a 'Just' value and return
@@ -202,7 +202,7 @@ module Streamly.Prelude
202 , minimumBy
202 , minimumBy
203 , minimum
203 , minimum
204 , the
204 , the
205− , toRevList
205+ -- , toListRev -- experimental
206
206
207 -- ** Lazy Folds
207 -- ** Lazy Folds
208 --
208 --
@@ -213,7 +213,6 @@ module Streamly.Prelude
213 -- -- | Convert or divert a stream into an output structure, container or
213 -- -- | Convert or divert a stream into an output structure, container or
214 -- sink.
214 -- sink.
215 , toList
215 , toList
216− -- , toArrayN
217 , toHandle
216 , toHandle
218
217
219 -- ** Partial Folds
218 -- ** Partial Folds
@@ -221,8 +220,8 @@ module Streamly.Prelude
221 -- folds strictly evaluate the stream until the result is determined.
220 -- folds strictly evaluate the stream until the result is determined.
222
221
223 -- -- ** To Elements (Partial Folds)
222 -- -- ** To Elements (Partial Folds)
224− , runN
223+ , drainN
225− , runWhile
224+ , drainWhile
226
225
227 -- -- | Folds that extract selected elements of a stream or their properties.
226 -- -- | Folds that extract selected elements of a stream or their properties.
228 , (!!)
227 , (!!)
@@ -372,6 +371,7 @@ module Streamly.Prelude
372
371
373 -- ** Reordering
372 -- ** Reordering
374 , reverse
373 , reverse
374+ , reverse'
375
375
376 -- * Multi-Stream Operations
376 -- * Multi-Stream Operations
377 -- | New streams can be constructed by appending, merging or zipping
377 -- | New streams can be constructed by appending, merging or zipping
@@ -462,6 +462,9 @@ module Streamly.Prelude
462 , foldx
462 , foldx
463 , foldxM
463 , foldxM
464 , foldr1
464 , foldr1
465+ , runStream
466+ , runN
467+ , runWhile
465 )
468 )
466where
469where
467
470
@@ -493,7 +496,6 @@ import qualified Streamly.Streams.Prelude as P
493import qualified Streamly.Streams.StreamK as K
496import qualified Streamly.Streams.StreamK as K
494import qualified Streamly.Streams.StreamD as D
497import qualified Streamly.Streams.StreamD as D
495import qualified Streamly.Streams.Zip as Z
498import qualified Streamly.Streams.Zip as Z
496−import qualified Streamly.Array.Types as A
497
499
498#ifdef USE_STREAMK_ONLY
500#ifdef USE_STREAMK_ONLY
499import qualified Streamly.Streams.StreamK as S
501import qualified Streamly.Streams.StreamK as S
@@ -568,7 +570,7 @@ unfoldr step seed = fromStreamS (S.unfoldr step seed)
568-- if b > 3
570-- if b > 3
569-- then return Nothing
571-- then return Nothing
570-- else print b >> return (Just (b, b + 1))
572-- else print b >> return (Just (b, b + 1))
571−-- in runStream $ unfoldrM f 0
573+-- in drain $ unfoldrM f 0
572-- @
574-- @
573-- @
575-- @
574-- 0
576-- 0
@@ -687,8 +689,8 @@ fromIndicesM = fromStreamD . D.fromIndicesM
687-- Generate a stream by performing a monadic action @n@ times. Same as:
689-- Generate a stream by performing a monadic action @n@ times. Same as:
688--
690--
689-- @
691-- @
690−-- runStream $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
692+-- drain $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
691−-- runStream $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
693+-- drain $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
692-- @
694-- @
693--
695--
694-- /Concurrent/
696-- /Concurrent/
@@ -723,8 +725,8 @@ replicate n = fromStreamS . S.replicate n
723-- Generate a stream by repeatedly executing a monadic action forever.
725-- Generate a stream by repeatedly executing a monadic action forever.
724--
726--
725-- @
727-- @
726−-- runStream $ serially $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
728+-- drain $ serially $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
727−-- runStream $ asyncly $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
729+-- drain $ asyncly $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
728-- @
730-- @
729--
731--
730-- /Concurrent, infinite (do not use with 'parallely')/
732-- /Concurrent, infinite (do not use with 'parallely')/
@@ -769,10 +771,10 @@ iterate step = fromStream . go
769-- previous iteration.
771-- previous iteration.
770--
772--
771-- @
773-- @
772−-- runStream $ serially $ S.take 10 $ S.iterateM
774+-- drain $ serially $ S.take 10 $ S.iterateM
773-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
775-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
774--
776--
775−-- runStream $ asyncly $ S.take 10 $ S.iterateM
777+-- drain $ asyncly $ S.take 10 $ S.iterateM
776-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
778-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
777-- @
779-- @
778--
780--
@@ -813,8 +815,8 @@ fromListM = fromStreamD . D.fromListM
813-- Construct a stream from a 'Foldable' containing monadic actions.
815-- Construct a stream from a 'Foldable' containing monadic actions.
814--
816--
815-- @
817-- @
816−-- runStream $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
818+-- drain $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
817−-- runStream $ asyncly $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
819+-- drain $ asyncly $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
818-- @
820-- @
819--
821--
820-- /Concurrent (do not use with 'parallely' on infinite containers)/
822-- /Concurrent (do not use with 'parallely' on infinite containers)/
@@ -1165,36 +1167,51 @@ foldlM' step begin m = S.foldlM' step begin $ toStreamS m
1165------------------------------------------------------------------------------
1167------------------------------------------------------------------------------
1166
1168
1167-- |
1169-- |
1168−-- > runStream = mapM_ (\_ -> return ())
1170+-- > drain = mapM_ (\_ -> return ())
1169--
1171--
1170-- Run a stream, discarding the results. By default it interprets the stream
1172-- Run a stream, discarding the results. By default it interprets the stream
1171-- as 'SerialT', to run other types of streams use the type adapting
1173-- as 'SerialT', to run other types of streams use the type adapting
1172−-- combinators for example @runStream . 'asyncly'@.
1174+-- combinators for example @drain . 'asyncly'@.
1173--
1175--
1174-- @since 0.2.0
1176-- @since 0.2.0
1177+{-# INLINE drain #-}
1178+drain :: Monad m => SerialT m a -> m ()
1179+drain = P.drain
1180+
1181+{-# DEPRECATED runStream "Please use \"drain\" instead" #-}
1175{-# INLINE runStream #-}
1182{-# INLINE runStream #-}
1176runStream :: Monad m => SerialT m a -> m ()
1183runStream :: Monad m => SerialT m a -> m ()
1177−runStream = P.runStream
1184+runStream = drain
1178
1185
1179-- |
1186-- |
1180−-- > runN n = runStream . take n
1187+-- > drainN n = drain . take n
1181--
1188--
1182-- Run maximum up to @n@ iterations of a stream.
1189-- Run maximum up to @n@ iterations of a stream.
1183--
1190--
1184−-- @since 0.6.0
1191+-- @since 0.7.0
1192+{-# INLINE drainN #-}
1193+drainN :: Monad m => Int -> SerialT m a -> m ()
1194+drainN n = drain . take n
1195+
1196+{-# DEPRECATED runN "Please use \"drainN\" instead" #-}
1185{-# INLINE runN #-}
1197{-# INLINE runN #-}
1186runN :: Monad m => Int -> SerialT m a -> m ()
1198runN :: Monad m => Int -> SerialT m a -> m ()
1187−runN n = runStream . take n
1199+runN = drainN
1188
1200
1189-- |
1201-- |
1190−-- > runWhile p = runStream . takeWhile p
1202+-- > drainWhile p = drain . takeWhile p
1191--
1203--
1192-- Run a stream as long as the predicate holds true.
1204-- Run a stream as long as the predicate holds true.
1193--
1205--
1194-- @since 0.6.0
1206-- @since 0.6.0
1207+{-# INLINE drainWhile #-}
1208+drainWhile :: Monad m => (a -> Bool) -> SerialT m a -> m ()
1209+drainWhile p = drain . takeWhile p
1210+
1211+{-# DEPRECATED runWhile "Please use \"drainWhile\" instead" #-}
1195{-# INLINE runWhile #-}
1212{-# INLINE runWhile #-}
1196runWhile :: Monad m => (a -> Bool) -> SerialT m a -> m ()
1213runWhile :: Monad m => (a -> Bool) -> SerialT m a -> m ()
1197−runWhile p = runStream . takeWhile p
1214+runWhile = drainWhile
1198
1215
1199-- | Determine whether the stream is empty.
1216-- | Determine whether the stream is empty.
1200--
1217--
@@ -1456,9 +1473,9 @@ stripPrefix m1 m2 = fmap fromStreamD <$>
1456-- Map and Fold
1473-- Map and Fold
1457------------------------------------------------------------------------------
1474------------------------------------------------------------------------------
1458
1475
1459−-- XXX this can utilize parallel mapping if we implement it as runStream . mapM
1476+-- XXX this can utilize parallel mapping if we implement it as drain . mapM
1460-- |
1477-- |
1461−-- > mapM_ = runStream . mapM
1478+-- > mapM_ = drain . mapM
1462--
1479--
1463-- Apply a monadic action to each element of the stream and discard the output
1480-- Apply a monadic action to each element of the stream and discard the output
1464-- of the action. This is not really a pure transformation operation but a
1481-- of the action. This is not really a pure transformation operation but a
@@ -1480,28 +1497,30 @@ mapM_ f m = S.mapM_ f $ toStreamS m
1480--
1497--
1481-- Convert a stream into a list in the underlying monad. The list can be
1498-- Convert a stream into a list in the underlying monad. The list can be
1482-- consumed lazily in a lazy monad (e.g. 'Identity'). In a strict monad (e.g.
1499-- consumed lazily in a lazy monad (e.g. 'Identity'). In a strict monad (e.g.
1483−-- IO) the whole list is generated before it can be consumed.
1500+-- IO) the whole list is generated and buffered before it can be consumed.
1501+--
1502+-- /Warning!/ working on large lists accumulated as buffers in memory could be
1503+-- very inefficient, consider using "Streamly.Array" instead.
1484--
1504--
1485-- @since 0.1.0
1505-- @since 0.1.0
1486{-# INLINE toList #-}
1506{-# INLINE toList #-}
1487toList :: Monad m => SerialT m a -> m [a]
1507toList :: Monad m => SerialT m a -> m [a]
1488toList = P.toList
1508toList = P.toList
1489
1509
1490−{-# INLINE _toArrayN #-}
1491−_toArrayN :: (Monad m, Storable a) => Int -> SerialT m a -> m (A.Array a)
1492−_toArrayN = P.toArrayN
1493−
1494-- |
1510-- |
1495-- @
1511-- @
1496−-- toRevList = S.foldl' (flip (:)) []
1512+-- toListRev = S.foldl' (flip (:)) []
1497-- @
1513-- @
1498--
1514--
1499-- Convert a stream into a list in reverse order in the underlying monad.
1515-- Convert a stream into a list in reverse order in the underlying monad.
1500--
1516--
1517+-- /Warning!/ working on large lists accumulated as buffers in memory could be
1518+-- very inefficient, consider using "Streamly.Array" instead.
1519+--
1501-- @since 0.7.0
1520-- @since 0.7.0
1502−{-# INLINE toRevList #-}
1521+{-# INLINE _toListRev #-}
1503−toRevList :: Monad m => SerialT m a -> m [a]
1522+_toListRev :: Monad m => SerialT m a -> m [a]
1504−toRevList = D.toRevList . toStreamD
1523+_toListRev = D.toListRev . toStreamD
1505
1524
1506-- |
1525-- |
1507-- @
1526-- @
@@ -1756,13 +1775,13 @@ dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
1756-- the output of the resulting action.
1775-- the output of the resulting action.
1757--
1776--
1758-- @
1777-- @
1759−-- > runStream $ S.mapM putStr $ S.fromList ["a", "b", "c"]
1778+-- > drain $ S.mapM putStr $ S.fromList ["a", "b", "c"]
1760-- abc
1779-- abc
1761--
1780--
1762−-- runStream $ S.replicateM 10 (return 1)
1781+-- drain $ S.replicateM 10 (return 1)
1763-- & (serially . S.mapM (\\x -> threadDelay 1000000 >> print x))
1782-- & (serially . S.mapM (\\x -> threadDelay 1000000 >> print x))
1764--
1783--
1765−-- runStream $ S.replicateM 10 (return 1)
1784+-- drain $ S.replicateM 10 (return 1)
1766-- & (asyncly . S.mapM (\\x -> threadDelay 1000000 >> print x))
1785-- & (asyncly . S.mapM (\\x -> threadDelay 1000000 >> print x))
1767-- @
1786-- @
1768--
1787--
@@ -1787,13 +1806,13 @@ mapMSerial = Serial.mapM
1787-- those actions.
1806-- those actions.
1788--
1807--
1789-- @
1808-- @
1790−-- > runStream $ S.sequence $ S.fromList [putStr "a", putStr "b", putStrLn "c"]
1809+-- > drain $ S.sequence $ S.fromList [putStr "a", putStr "b", putStrLn "c"]
1791-- abc
1810-- abc
1792--
1811--
1793−-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1812+-- drain $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1794-- & (serially . S.sequence)
1813-- & (serially . S.sequence)
1795--
1814--
1796−-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1815+-- drain $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1797-- & (asyncly . S.sequence)
1816-- & (asyncly . S.sequence)
1798-- @
1817-- @
1799--
1818--
@@ -1844,11 +1863,21 @@ mapMaybeMSerial f m = fromStreamD $ D.mapMaybeM f $ toStreamD m
1844-- Returns the elements of the stream in reverse order. The stream must be
1863-- Returns the elements of the stream in reverse order. The stream must be
1845-- finite.
1864-- finite.
1846--
1865--
1866+-- /Note:/ 'reverse'' is much faster than this, use that when performance
1867+-- matters.
1868+--
1847-- @since 0.1.1
1869-- @since 0.1.1
1848{-# INLINE reverse #-}
1870{-# INLINE reverse #-}
1849reverse :: (IsStream t, Monad m) => t m a -> t m a
1871reverse :: (IsStream t, Monad m) => t m a -> t m a
1850reverse s = fromStreamS $ S.reverse $ toStreamS s
1872reverse s = fromStreamS $ S.reverse $ toStreamS s
1851
1873
1874+-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
1875+--
1876+-- @since 0.7.0
1877+{-# INLINE reverse' #-}
1878+reverse' :: (IsStream t, Monad m, Storable a) => t m a -> t m a
1879+reverse' s = fromStreamD $ D.reverse' $ toStreamD s
1880+
1852------------------------------------------------------------------------------
1881------------------------------------------------------------------------------
1853-- Transformation by Inserting
1882-- Transformation by Inserting
1854------------------------------------------------------------------------------
1883------------------------------------------------------------------------------
src/Streamly/RingBuffer.hsnew file+183
@@ -0,0 +1,183 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE RecordWildCards #-}
3+{-# LANGUAGE BangPatterns #-}
4+{-# LANGUAGE ExistentialQuantification #-}
5+
6+-- |
7+-- Module : Streamly.RingBuffer
8+-- Copyright : (c) 2019 Composewell Technologies
9+-- License : BSD3
10+-- Maintainer : harendra.kumar@gmail.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+
15+module Streamly.RingBuffer
16+ ( RingBuffer(..)
17+
18+ -- * Construction
19+ , unsafeNew
20+
21+ -- * Modification
22+ , insert
23+
24+ -- * Folds
25+ , foldRing
26+ , foldRingM
27+ , foldRingFullM
28+
29+ -- * Fast Byte Comparisons
30+ , unsafeEqArray
31+ , unsafeEqArrayN
32+ ) where
33+
34+import Control.Exception (assert)
35+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
36+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
37+import Foreign.Ptr (plusPtr, minusPtr, castPtr)
38+import Foreign.Storable (Storable(..))
39+import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
40+import GHC.Ptr (Ptr(..))
41+import Prelude hiding (length, concat)
42+
43+import qualified Streamly.Array.Types as A
44+
45+-- | A ring buffer is a mutable array of fixed size. Initially the array is
46+-- empty, with ringStart pointing at the start of allocated memory. We call the
47+-- next location to be written in the ring as ringHead. Initially ringHead ==
48+-- ringStart. When the first item is added ringHead points to ringStart +
49+-- sizeof item. When the buffer becomes full ringHead would wrap around to
50+-- ringStart. When the buffer is full, ringHead always points at the oldest
51+-- item in the ring and the newest item added will always overwrite the oldest
52+-- item.
53+--
54+-- When using it we should keep in mind that a ringBuffer is a mutable data
55+-- structure. We should not leak out references to it for immutable use.
56+--
57+data RingBuffer a = RingBuffer
58+ { ringStart :: !(ForeignPtr a) -- first address
59+ , ringBound :: !(Ptr a) -- first address beyond allocated memory
60+ }
61+
62+-- | Create a new ringbuffer and return the ring buffer and the ringHead.
63+{-# INLINE unsafeNew #-}
64+unsafeNew :: forall a. Storable a => Int -> IO (RingBuffer a, Ptr a)
65+unsafeNew count = do
66+ let size = count * sizeOf (undefined :: a)
67+ fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
68+ let p = unsafeForeignPtrToPtr fptr
69+ return $ (RingBuffer
70+ { ringStart = fptr
71+ , ringBound = p `plusPtr` size
72+ }, p)
73+
74+-- | Advance the ringHead by 1 item, wrap around if we hit the end of the
75+-- array.
76+{-# INLINE advance #-}
77+advance :: forall a. Storable a => RingBuffer a -> Ptr a -> Ptr a
78+advance RingBuffer{..} ringHead =
79+ let ptr = ringHead `plusPtr` sizeOf (undefined :: a)
80+ in if ptr < ringBound
81+ then ptr
82+ else unsafeForeignPtrToPtr ringStart
83+
84+-- | Insert an item at the head of the ring, when the ring is full this
85+-- replaces the oldest item in the ring with the new item.
86+{-# INLINE insert #-}
87+insert :: Storable a => RingBuffer a -> Ptr a -> a -> IO (Ptr a)
88+insert rb ringHead newVal = do
89+ poke ringHead newVal
90+ -- touchForeignPtr (ringStart rb)
91+ return $ advance rb ringHead
92+
93+-- | Like 'unsafeEqArray' but compares only N bytes instead of entire length of
94+-- the ring buffer.
95+{-# INLINE unsafeEqArrayN #-}
96+unsafeEqArrayN :: RingBuffer a -> Ptr a -> A.Array a -> Int -> Bool
97+unsafeEqArrayN RingBuffer{..} rh A.Array{..} n =
98+ let !res = A.unsafeInlineIO $ do
99+ let rs = unsafeForeignPtrToPtr ringStart
100+ let as = unsafeForeignPtrToPtr aStart
101+ assert (aBound `minusPtr` as >= ringBound `minusPtr` rs) (return ())
102+ let len = ringBound `minusPtr` rh
103+ r1 <- A.memcmp (castPtr rh) (castPtr as) (min len n)
104+ r2 <- if n > len
105+ then A.memcmp (castPtr rs) (castPtr (as `plusPtr` len))
106+ (min (rh `minusPtr` rs) (n - len))
107+ else return True
108+ -- touchForeignPtr ringStart
109+ -- touchForeignPtr aStart
110+ return (r1 && r2)
111+ in res
112+
113+-- | Byte compare the entire length of ringBuffer with the given array,
114+-- starting at the supplied pointer. The supplied pointer is usually the
115+-- ringHead, therefore it would compare the ringBuffer starting at the oldest
116+-- item in the ring up to the newest item in the ring.
117+--
118+-- Returns true if the Array and the ringBuffer have identical contents. The
119+-- supplied array must be equal to or bigger than the ringBuffer, ARRAY BOUNDS
120+-- ARE NOT CHECKED.
121+{-# INLINE unsafeEqArray #-}
122+unsafeEqArray :: RingBuffer a -> Ptr a -> A.Array a -> Bool
123+unsafeEqArray RingBuffer{..} rh A.Array{..} =
124+ let !res = A.unsafeInlineIO $ do
125+ let rs = unsafeForeignPtrToPtr ringStart
126+ let as = unsafeForeignPtrToPtr aStart
127+ assert (aBound `minusPtr` as >= ringBound `minusPtr` rs)
128+ (return ())
129+ let len = ringBound `minusPtr` rh
130+ r1 <- A.memcmp (castPtr rh) (castPtr as) len
131+ r2 <- A.memcmp (castPtr rs) (castPtr (as `plusPtr` len))
132+ (rh `minusPtr` rs)
133+ -- touchForeignPtr ringStart
134+ -- touchForeignPtr aStart
135+ return (r1 && r2)
136+ in res
137+
138+-- | Fold the buffer starting from ringStart to a given position using a pure
139+-- step function. This is useful to fold the items in the ring when the ring is
140+-- not full. The supplied pointer is usually the end of the ring.
141+{-# INLINE foldRing #-}
142+foldRing :: forall a b. Storable a
143+ => Ptr a -> (b -> a -> b) -> b -> RingBuffer a -> b
144+foldRing ptr f z RingBuffer{..} =
145+ let !res = A.unsafeInlineIO $ withForeignPtr ringStart $ \p ->
146+ go z p ptr
147+ in res
148+ where
149+ go !acc !p !q
150+ | p == q = return acc
151+ | otherwise = do
152+ x <- peek p
153+ go (f acc x) (p `plusPtr` sizeOf (undefined :: a)) q
154+
155+-- | Like foldRing but with a monadic step function.
156+{-# INLINE foldRingM #-}
157+foldRingM :: forall m a b. (Monad m, Storable a)
158+ => Ptr a -> (b -> a -> m b) -> b -> RingBuffer a -> m b
159+foldRingM ptr f z RingBuffer{..} = go z (unsafeForeignPtrToPtr ringStart) ptr
160+ where
161+ go !acc !start !end
162+ | start == end = return acc
163+ | otherwise = do
164+ let !x = A.unsafeInlineIO $ peek start
165+ acc' <- f acc x
166+ go acc' (start `plusPtr` sizeOf (undefined :: a)) end
167+
168+-- | Fold the entire length of a ring buffer starting at the supplied ringHead
169+-- pointer. Assuming the supplied ringHead pointer points to the oldest item,
170+-- this would fold the ring starting from the oldest item to the newest item in
171+-- the ring.
172+{-# INLINE foldRingFullM #-}
173+foldRingFullM :: forall m a b. (Monad m, Storable a)
174+ => Ptr a -> (b -> a -> m b) -> b -> RingBuffer a -> m b
175+foldRingFullM rh f z rb@RingBuffer{..} = go z rh
176+ where
177+ go !acc !start = do
178+ let !x = A.unsafeInlineIO $ peek start
179+ acc' <- f acc x
180+ let ptr = advance rb start
181+ if ptr == rh
182+ then return acc'
183+ else go acc' ptr
src/Streamly/Sink.hsnew file+286
@@ -0,0 +1,286 @@
1+-- |
2+-- Module : Streamly.Sink
3+-- Copyright : (c) 2019 Composewell Technologies
4+-- License : BSD3
5+-- Maintainer : harendra.kumar@gmail.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+--
10+-- The 'Sink' type is a just a special case of 'Fold' and we can do without
11+-- it. However, in some cases 'Sink' is a simpler type and may provide better
12+-- performance than 'Fold' because it does not maintain any state. Folds can
13+-- be used for both pure and monadic computations. Sinks are not applicable to
14+-- pure computations.
15+
16+module Streamly.Sink
17+ (
18+ Sink (..)
19+
20+ -- * Running
21+ , sink
22+
23+ -- * Upgrading
24+ , toFold
25+
26+ -- * Composing Sinks
27+ -- ** Distribute
28+ , tee
29+ , distribute
30+
31+ -- ** Demultiplex
32+ , demux
33+
34+ -- ** Unzip
35+ , unzipM
36+ , unzip
37+
38+ -- -- ** Group
39+ -- , grouped
40+
41+ -- -- ** Nest
42+ -- , concatFold
43+
44+ -- -- * Comonad
45+ -- , duplicate
46+
47+ -- * Utilities
48+ , tap
49+
50+ -- * Input Transformation
51+ -- | These are contravariant operations i.e. they apply on the input of the
52+ -- 'Sink', for this reason they are prefixed with 'l' for 'left'.
53+ , lmap
54+ , lmapM
55+ , lfilter
56+ , lfilterM
57+
58+ -- * Sinks
59+ , drain
60+ , drainM
61+ -- , drainN
62+ -- , drainWhile
63+ )
64+where
65+
66+import Control.Monad (when, void)
67+import Data.Map.Strict (Map)
68+import Prelude
69+ hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
70+ foldl, map, mapM, sequence, all, any, sum, product, elem,
71+ notElem, maximum, minimum, head, last, tail, length, null,
72+ reverse, iterate, init, and, or, lookup, foldr1, (!!),
73+ scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip)
74+
75+import Streamly.Fold.Types (Fold(..))
76+import Streamly.Sink.Types (Sink(..))
77+import Streamly.Streams.Serial (SerialT)
78+
79+import qualified Data.Map.Strict as Map
80+
81+import qualified Streamly.Fold as FL
82+import qualified Streamly.Streams.StreamD as D
83+
84+------------------------------------------------------------------------------
85+-- Conversion
86+------------------------------------------------------------------------------
87+
88+-- | Convert a 'Sink' to a 'Fold'. When you want to compose sinks and folds
89+-- together, upgrade a sink to a fold before composing.
90+toFold :: Monad m => Sink m a -> Fold m a ()
91+toFold (Sink f) = Fold step begin done
92+ where
93+ begin = return ()
94+ step _ = f
95+ done _ = return ()
96+
97+------------------------------------------------------------------------------
98+-- Running a sink
99+------------------------------------------------------------------------------
100+
101+-- | Drain a stream to a 'Sink'.
102+{-# INLINE sink #-}
103+sink :: Monad m => Sink m a -> SerialT m a -> m ()
104+sink = FL.foldl' . toFold
105+
106+------------------------------------------------------------------------------
107+-- Composing with sinks
108+------------------------------------------------------------------------------
109+
110+-- | Distribute one copy each of the input to both the sinks.
111+--
112+-- @
113+-- |-------Sink m a
114+-- ---stream m a---|
115+-- |-------Sink m a
116+-- @
117+-- @
118+-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
119+-- > sink (Sink.tee (pr \"L") (pr \"R")) (S.enumerateFromTo 1 2)
120+-- L 1
121+-- R 1
122+-- L 2
123+-- R 2
124+-- @
125+--
126+tee :: Monad m => Sink m a -> Sink m a -> Sink m a
127+tee (Sink fL) (Sink fR) = Sink (\a -> fL a >> fR a)
128+
129+-- | Distribute copies of the input to all the sinks in a container.
130+--
131+-- @
132+-- |-------Sink m a
133+-- ---stream m a---|
134+-- |-------Sink m a
135+-- |
136+-- ...
137+-- @
138+-- @
139+-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
140+-- > sink (Sink.distribute [(pr \"L"), (pr \"R")]) (S.enumerateFromTo 1 2)
141+-- L 1
142+-- R 1
143+-- L 2
144+-- R 2
145+-- @
146+--
147+-- This is the consumer side dual of the producer side 'sequence_' operation.
148+{-# INLINE distribute #-}
149+distribute :: Monad m => [Sink m a] -> Sink m a
150+distribute ss = Sink (\a -> Prelude.mapM_ (\(Sink f) -> f a) ss)
151+
152+-- | Demultiplex to multiple consumers without collecting the results. Useful
153+-- to run different effectful computations depending on the value of the stream
154+-- elements, for example handling network packets of different types using
155+-- different handlers.
156+--
157+-- @
158+--
159+-- |-------Sink m a
160+-- -----stream m a-----Map-----|
161+-- |-------Sink m a
162+-- |
163+-- ...
164+-- @
165+--
166+-- @
167+-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
168+-- > let table = Data.Map.fromList [(1, pr \"One"), (2, pr \"Two")]
169+-- in Sink.sink (Sink.demux id table) (S.enumerateFromTo 1 100)
170+-- One 1
171+-- Two 2
172+-- @
173+{-
174+demux :: (Monad m, Ord k) => (a -> k) -> Map k (Sink m a) -> Sink m a
175+demux f kv = Sink step
176+
177+ where
178+
179+ step a =
180+ -- XXX should we raise an exception in Nothing case?
181+ -- Ideally we should enforce that it is a total map over k so that look
182+ -- up never fails
183+ case Map.lookup (f a) kv of
184+ Nothing -> return ()
185+ Just (Sink g) -> g a
186+-}
187+
188+demux :: (Monad m, Ord k) => Map k (Sink m a) -> Sink m (a, k)
189+demux kv = Sink step
190+
191+ where
192+
193+ step (a, k) =
194+ -- XXX should we raise an exception in Nothing case?
195+ -- Ideally we should enforce that it is a total map over k so that look
196+ -- up never fails
197+ case Map.lookup k kv of
198+ Nothing -> return ()
199+ Just (Sink g) -> g a
200+
201+-- | Split elements in the input stream into two parts using a monadic unzip
202+-- function, direct each part to a different sink.
203+--
204+-- @
205+--
206+-- |-------Sink m b
207+-- -----Stream m a----(b,c)--|
208+-- |-------Sink m c
209+-- @
210+-- @
211+-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
212+-- in Sink.sink (Sink.unzip return (pr \"L") (pr \"R")) (S.yield (1,2))
213+-- L 1
214+-- R 2
215+-- @
216+{-# INLINE unzipM #-}
217+unzipM :: Monad m => (a -> m (b,c)) -> Sink m b -> Sink m c -> Sink m a
218+unzipM f (Sink stepB) (Sink stepC) =
219+ Sink (\a -> f a >>= \(b,c) -> stepB b >> stepC c)
220+
221+-- | Same as 'unzipM' but with a pure unzip function.
222+{-# INLINE unzip #-}
223+unzip :: Monad m => (a -> (b,c)) -> Sink m b -> Sink m c -> Sink m a
224+unzip f = unzipM (return . f)
225+
226+-- | Tap a stream to a 'Sink'. For example, you may add a tap to log the
227+-- contents flowing through the stream.
228+--
229+-- @
230+-- Sink m a
231+-- |
232+-- -----stream m a ---------------stream m a-----
233+--
234+-- @
235+--
236+-- @
237+-- > let pr x = Sink.drainM (putStrLn . ((x ++ " ") ++) . show)
238+-- > S.drain $ Sink.tap (pr \"L") (S.enumerateFromTo 1 2)
239+-- L 1
240+-- L 2
241+-- @
242+--
243+-- Compare with the 'trace' function.
244+--
245+tap :: Monad m => Sink m a -> SerialT m a -> SerialT m a
246+tap s m = D.fromStreamD $ D.tap s (D.toStreamD m)
247+
248+------------------------------------------------------------------------------
249+-- Input transformation
250+------------------------------------------------------------------------------
251+
252+-- | Map a pure function on the input of a 'Sink'.
253+{-# INLINABLE lmap #-}
254+lmap :: (a -> b) -> Sink m b -> Sink m a
255+lmap f (Sink step) = Sink (step . f)
256+
257+-- | Map a monadic function on the input of a 'Sink'.
258+{-# INLINABLE lmapM #-}
259+lmapM :: Monad m => (a -> m b) -> Sink m b -> Sink m a
260+lmapM f (Sink step) = Sink (\x -> f x >>= step)
261+
262+-- | Filter the input of a 'Sink' using a pure predicate function.
263+{-# INLINABLE lfilter #-}
264+lfilter :: Monad m => (a -> Bool) -> Sink m a -> Sink m a
265+lfilter f (Sink step) = Sink (\a -> when (f a) $ step a)
266+
267+-- | Filter the input of a 'Sink' using a monadic predicate function.
268+{-# INLINABLE lfilterM #-}
269+lfilterM :: Monad m => (a -> m Bool) -> Sink m a -> Sink m a
270+lfilterM f (Sink step) = Sink (\a -> f a >>= \use -> when use $ step a)
271+
272+------------------------------------------------------------------------------
273+-- Sinks
274+------------------------------------------------------------------------------
275+
276+-- | Drain all input, running the effects and discarding the results.
277+drain :: Monad m => Sink m a
278+drain = Sink (\_ -> return ())
279+
280+-- |
281+-- > drainM f = lmapM f drain
282+--
283+-- Drain all input after passing it through a monadic function.
284+{-# INLINABLE drainM #-}
285+drainM :: Monad m => (a -> m b) -> Sink m a
286+drainM f = Sink (void . f)
src/Streamly/Sink/Types.hsnew file+24
@@ -0,0 +1,24 @@
1+-- |
2+-- Module : Streamly.Sink.Types
3+-- Copyright : (c) 2019 Composewell Technologies
4+-- License : BSD3
5+-- Maintainer : harendra.kumar@gmail.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Sink.Types
10+ (
11+ Sink (..)
12+ )
13+where
14+
15+------------------------------------------------------------------------------
16+-- Sink
17+------------------------------------------------------------------------------
18+
19+-- | A 'Sink' is a special type of 'Foldl' that does not accumulate any value,
20+-- but runs only effects. A 'Sink' has no state to maintain therefore can be a
21+-- bit more efficient than a 'Foldl' with '()' as the state, especially when
22+-- 'Sink's are composed with other operations. A Sink can be upgraded to a
23+-- 'Foldl', but a 'Foldl' cannot be converted into a Sink.
24+data Sink m a = Sink (a -> m ())
src/Streamly/Streams/Ahead.hs+1−1
@@ -613,7 +613,7 @@ consMAhead m r = fromStream $ K.yieldM m `ahead` (toStream r)
613-- import qualified "Streamly.Prelude" as S
613-- import qualified "Streamly.Prelude" as S
614-- import Control.Concurrent
614-- import Control.Concurrent
615--
615--
616−-- main = 'runStream' . 'aheadly' $ do
616+-- main = 'drain' . 'aheadly' $ do
617-- n <- return 3 \<\> return 2 \<\> return 1
617-- n <- return 3 \<\> return 2 \<\> return 1
618-- S.yieldM $ do
618-- S.yieldM $ do
619-- threadDelay (n * 1000000)
619-- threadDelay (n * 1000000)
src/Streamly/Streams/Async.hs+2−2
@@ -618,7 +618,7 @@ consMAsync m r = fromStream $ K.yieldM m `async` (toStream r)
618-- import qualified "Streamly.Prelude" as S
618-- import qualified "Streamly.Prelude" as S
619-- import Control.Concurrent
619-- import Control.Concurrent
620--
620--
621−-- main = 'runStream' . 'asyncly' $ do
621+-- main = 'drain' . 'asyncly' $ do
622-- n <- return 3 \<\> return 2 \<\> return 1
622-- n <- return 3 \<\> return 2 \<\> return 1
623-- S.yieldM $ do
623-- S.yieldM $ do
624-- threadDelay (n * 1000000)
624-- threadDelay (n * 1000000)
@@ -754,7 +754,7 @@ wAsync = joinStreamVarAsync WAsyncVar
754-- import qualified "Streamly.Prelude" as S
754-- import qualified "Streamly.Prelude" as S
755-- import Control.Concurrent
755-- import Control.Concurrent
756--
756--
757−-- main = 'runStream' . 'wAsyncly' $ do
757+-- main = 'drain' . 'wAsyncly' $ do
758-- n <- return 3 \<\> return 2 \<\> return 1
758-- n <- return 3 \<\> return 2 \<\> return 1
759-- S.yieldM $ do
759-- S.yieldM $ do
760-- threadDelay (n * 1000000)
760-- threadDelay (n * 1000000)
src/Streamly/Streams/Parallel.hs+3−3
@@ -169,7 +169,7 @@ infixl 1 |&.
169--
169--
170--
170--
171-- @
171-- @
172−-- runStream $
172+-- drain $
173-- S.mapM (\\x -> threadDelay 1000000 >> print x)
173-- S.mapM (\\x -> threadDelay 1000000 >> print x)
174-- |$ S.repeatM (threadDelay 1000000 >> return 1)
174-- |$ S.repeatM (threadDelay 1000000 >> return 1)
175-- @
175-- @
@@ -186,7 +186,7 @@ f |$ x = applyWith f x
186-- concurrent.
186-- concurrent.
187--
187--
188-- @
188-- @
189−-- runStream $
189+-- drain $
190-- S.repeatM (threadDelay 1000000 >> return 1)
190-- S.repeatM (threadDelay 1000000 >> return 1)
191-- |& S.mapM (\\x -> threadDelay 1000000 >> print x)
191-- |& S.mapM (\\x -> threadDelay 1000000 >> print x)
192-- @
192-- @
@@ -304,7 +304,7 @@ x |&. f = f |$. x
304-- import qualified "Streamly.Prelude" as S
304-- import qualified "Streamly.Prelude" as S
305-- import Control.Concurrent
305-- import Control.Concurrent
306--
306--
307−-- main = 'runStream' . 'parallely' $ do
307+-- main = 'drain' . 'parallely' $ do
308-- n <- return 3 \<\> return 2 \<\> return 1
308-- n <- return 3 \<\> return 2 \<\> return 1
309-- S.yieldM $ do
309-- S.yieldM $ do
310-- threadDelay (n * 1000000)
310-- threadDelay (n * 1000000)
src/Streamly/Streams/Prelude.hs+6−24
@@ -23,13 +23,11 @@ module Streamly.Streams.Prelude
23 , toStreamS
23 , toStreamS
24
24
25 -- * Running Effects
25 -- * Running Effects
26− , runStream
26+ , drain
27
27
28 -- * Conversion operations
28 -- * Conversion operations
29 , fromList
29 , fromList
30− , fromArray
31 , toList
30 , toList
32− , toArrayN
33
31
34 -- * Fold operations
32 -- * Fold operations
35 , foldrM
33 , foldrM
@@ -61,7 +59,6 @@ module Streamly.Streams.Prelude
61where
59where
62
60
63import Control.Monad.Trans (MonadTrans(..))
61import Control.Monad.Trans (MonadTrans(..))
64−import Foreign.Storable (Storable)
65import Prelude hiding (foldr)
62import Prelude hiding (foldr)
66import qualified Prelude
63import qualified Prelude
67
64
@@ -74,7 +71,6 @@ import qualified Streamly.Streams.StreamD as S
74import Streamly.Streams.StreamK (IsStream(..))
71import Streamly.Streams.StreamK (IsStream(..))
75import qualified Streamly.Streams.StreamK as K
72import qualified Streamly.Streams.StreamK as K
76import qualified Streamly.Streams.StreamD as D
73import qualified Streamly.Streams.StreamD as D
77−import qualified Streamly.Array.Types as A
78
74
79------------------------------------------------------------------------------
75------------------------------------------------------------------------------
80-- Conversion to and from direct style stream
76-- Conversion to and from direct style stream
@@ -93,11 +89,11 @@ toStreamS = S.fromStreamK . toStream
93-- Conversions
89-- Conversions
94------------------------------------------------------------------------------
90------------------------------------------------------------------------------
95
91
96−{-# INLINE_EARLY runStream #-}
92+{-# INLINE_EARLY drain #-}
97−runStream :: (IsStream t, Monad m) => t m a -> m ()
93+drain :: (IsStream t, Monad m) => t m a -> m ()
98−runStream m = D.runStream $ D.fromStreamK (toStream m)
94+drain m = D.drain $ D.fromStreamK (toStream m)
99−{-# RULES "runStream fallback to CPS" [1]
95+{-# RULES "drain fallback to CPS" [1]
100− forall a. D.runStream (D.fromStreamK a) = K.runStream a #-}
96+ forall a. D.drain (D.fromStreamK a) = K.drain a #-}
101
97
102------------------------------------------------------------------------------
98------------------------------------------------------------------------------
103-- Conversions
99-- Conversions
@@ -118,16 +114,6 @@ fromList = fromStreamS . S.fromList
118{-# RULES "fromList fallback to StreamK" [1]
114{-# RULES "fromList fallback to StreamK" [1]
119 forall a. S.toStreamK (S.fromList a) = K.fromFoldable a #-}
115 forall a. S.toStreamK (S.fromList a) = K.fromFoldable a #-}
120
116
121−-- XXX add fallback to StreamK rule
122−--
123−-- | Construct a stream from an 'Array'.
124−--
125−{-# INLINE_EARLY fromArray #-}
126−fromArray :: (IsStream t, Monad m, Storable a) => A.Array a -> t m a
127−fromArray = D.fromStreamD . D.fromArray
128−-- {-# RULES "fromArray fallback to StreamK" [1]
129−-- forall a. S.toStreamK (S.fromArray a) = K.fromArray a #-}
130−
131-- | Convert a stream into a list in the underlying monad.
117-- | Convert a stream into a list in the underlying monad.
132--
118--
133-- @since 0.1.0
119-- @since 0.1.0
@@ -135,10 +121,6 @@ fromArray = D.fromStreamD . D.fromArray
135toList :: (Monad m, IsStream t) => t m a -> m [a]
121toList :: (Monad m, IsStream t) => t m a -> m [a]
136toList m = S.toList $ toStreamS m
122toList m = S.toList $ toStreamS m
137
123
138−{-# INLINE toArrayN #-}
139−toArrayN :: (Monad m, IsStream t, Storable a) => Int -> t m a -> m (A.Array a)
140−toArrayN n m = D.toArrayN n $ D.toStreamD m
141−
142------------------------------------------------------------------------------
124------------------------------------------------------------------------------
143-- Folds
125-- Folds
144------------------------------------------------------------------------------
126------------------------------------------------------------------------------
src/Streamly/Streams/Serial.hs+3−3
@@ -92,7 +92,7 @@ import qualified Streamly.Streams.StreamD as D
92-- element of the stream, serially.
92-- element of the stream, serially.
93--
93--
94-- @
94-- @
95−-- main = 'runStream' . 'serially' $ do
95+-- main = 'drain' . 'serially' $ do
96-- x <- return 1 \<\> return 2
96-- x <- return 1 \<\> return 2
97-- S.yieldM $ print x
97-- S.yieldM $ print x
98-- @
98-- @
@@ -104,7 +104,7 @@ import qualified Streamly.Streams.StreamD as D
104-- 'SerialT' nests streams serially in a depth first manner.
104-- 'SerialT' nests streams serially in a depth first manner.
105--
105--
106-- @
106-- @
107−-- main = 'runStream' . 'serially' $ do
107+-- main = 'drain' . 'serially' $ do
108-- x <- return 1 \<\> return 2
108-- x <- return 1 \<\> return 2
109-- y <- return 3 \<\> return 4
109-- y <- return 3 \<\> return 4
110-- S.yieldM $ print (x, y)
110-- S.yieldM $ print (x, y)
@@ -223,7 +223,7 @@ TRAVERSABLE_INSTANCE(SerialT)
223--
223--
224--
224--
225-- @
225-- @
226−-- main = 'runStream' . 'wSerially' $ do
226+-- main = 'drain' . 'wSerially' $ do
227-- x <- return 1 \<\> return 2
227-- x <- return 1 \<\> return 2
228-- y <- return 3 \<\> return 4
228-- y <- return 3 \<\> return 4
229-- S.yieldM $ print (x, y)
229-- S.yieldM $ print (x, y)
src/Streamly/Streams/StreamD.hs+760−114
@@ -83,7 +83,6 @@ module Streamly.Streams.StreamD
83 , yieldM
83 , yieldM
84 , fromList
84 , fromList
85 , fromListM
85 , fromListM
86− , fromArray
87 , fromStreamK
86 , fromStreamK
88 , fromStreamD
87 , fromStreamD
89
88
@@ -100,12 +99,14 @@ module Streamly.Streams.StreamD
100 , foldlS
99 , foldlS
101 , foldlT
100 , foldlT
102 , reverse
101 , reverse
102+ , reverse'
103
103
104 , foldlx'
104 , foldlx'
105 , foldlMx'
105 , foldlMx'
106
106
107 -- ** Specialized Folds
107 -- ** Specialized Folds
108− , runStream
108+ , tap
109+ , drain
109 , null
110 , null
110 , head
111 , head
111 , tail
112 , tail
@@ -129,6 +130,20 @@ module Streamly.Streams.StreamD
129 , concatMap
130 , concatMap
130 , flattenArrays
131 , flattenArrays
131
132
133+ -- ** Grouping
134+ , groupsOf
135+ , grouped
136+ , groupsBy
137+
138+ -- ** Splitting
139+ , splitBy
140+ , splitSuffixBy
141+ , wordsBy
142+ , splitSuffixBy'
143+
144+ , splitOn
145+ , splitSuffixOn
146+
132 -- ** Substreams
147 -- ** Substreams
133 , isPrefixOf
148 , isPrefixOf
134 , isSubsequenceOf
149 , isSubsequenceOf
@@ -140,10 +155,9 @@ module Streamly.Streams.StreamD
140 -- ** Conversions
155 -- ** Conversions
141 -- | Transform a stream into another type.
156 -- | Transform a stream into another type.
142 , toList
157 , toList
143− , toRevList
158+ , toListRev
144 , toStreamK
159 , toStreamK
145 , toStreamD
160 , toStreamD
146− , toArrayN
147
161
148 -- * Transformation
162 -- * Transformation
149 -- ** By folding (scans)
163 -- ** By folding (scans)
@@ -215,10 +229,10 @@ module Streamly.Streams.StreamD
215where
229where
216
230
217import Control.Monad.Trans (MonadTrans(lift))
231import Control.Monad.Trans (MonadTrans(lift))
232+import Data.Word (Word32)
233+import Data.Bits (shiftR, shiftL, (.|.), (.&.))
218import Data.Maybe (fromJust, isJust)
234import Data.Maybe (fromJust, isJust)
219−import Foreign.ForeignPtr (ForeignPtr, touchForeignPtr)
235+import Foreign.Ptr (Ptr)
220−import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
221−import Foreign.Ptr (Ptr, plusPtr)
222import Foreign.Storable (Storable(..))
236import Foreign.Storable (Storable(..))
223import GHC.Types (SPEC(..))
237import GHC.Types (SPEC(..))
224import Prelude
238import Prelude
@@ -227,14 +241,18 @@ import Prelude
227 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
241 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
228 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
242 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
229 reverse)
243 reverse)
244+import System.IO.Unsafe (unsafeDupablePerformIO)
230
245
231import Streamly.Array.Types
246import Streamly.Array.Types
232− (Array(..), Array, unsafeInlineIO)
247+ (Array(..), unsafeInlineIO, unsafeIndex, flattenArrays)
248+import Streamly.Fold.Types (Fold(..))
233import Streamly.SVar (MonadAsync, defState, adaptState)
249import Streamly.SVar (MonadAsync, defState, adaptState)
250+import Streamly.Sink.Types (Sink(..))
234
251
235import Streamly.Streams.StreamD.Type
252import Streamly.Streams.StreamD.Type
236import qualified Streamly.Streams.StreamK as K
253import qualified Streamly.Streams.StreamK as K
237import qualified Streamly.Array.Types as A
254import qualified Streamly.Array.Types as A
255+import qualified Streamly.RingBuffer as RB
238
256
239------------------------------------------------------------------------------
257------------------------------------------------------------------------------
240-- Construction
258-- Construction
@@ -530,20 +548,6 @@ fromListM = Stream step
530 step _ (m:ms) = m >>= \x -> return $ Yield x ms
548 step _ (m:ms) = m >>= \x -> return $ Yield x ms
531 step _ [] = return Stop
549 step _ [] = return Stop
532
550
533−{-# INLINE_NORMAL fromArray #-}
534−fromArray :: (Monad m, Storable a) => Array a -> Stream m a
535−fromArray = A.toStreamD
536−
537−{-# INLINE_LATE fromStreamK #-}
538−fromStreamK :: Monad m => K.Stream m a -> Stream m a
539−fromStreamK = Stream step
540− where
541− step gst m1 =
542− let stop = return Stop
543− single a = return $ Yield a K.nil
544− yieldk a r = return $ Yield a r
545− in K.foldStreamShared gst yieldk single stop m1
546−
547{-# INLINE toStreamD #-}
551{-# INLINE toStreamD #-}
548toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
552toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
549toStreamD = fromStreamK . K.toStream
553toStreamD = fromStreamK . K.toStream
@@ -568,27 +572,6 @@ foldr1 f m = do
568-- Left Folds
572-- Left Folds
569------------------------------------------------------------------------------
573------------------------------------------------------------------------------
570
574
571−-- XXX run begin action only if the stream is not empty.
572−{-# INLINE_NORMAL foldlMx' #-}
573−foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
574−foldlMx' fstep begin done (Stream step state) =
575− begin >>= \x -> go SPEC x state
576− where
577− -- XXX !acc?
578− go !_ acc st = acc `seq` do
579− r <- step defState st
580− case r of
581− Yield x s -> do
582− acc' <- fstep acc x
583− go SPEC acc' s
584− Skip s -> go SPEC acc s
585− Stop -> done acc
586−
587−{-# INLINE foldlx' #-}
588−foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
589−foldlx' fstep begin done m =
590− foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
591−
592{-# INLINE_NORMAL foldlT #-}
575{-# INLINE_NORMAL foldlT #-}
593foldlT :: (Monad m, Monad (s m), MonadTrans s)
576foldlT :: (Monad m, Monad (s m), MonadTrans s)
594 => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
577 => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
@@ -628,10 +611,10 @@ foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin))
628------------------------------------------------------------------------------
611------------------------------------------------------------------------------
629
612
630-- | Run a streaming composition, discard the results.
613-- | Run a streaming composition, discard the results.
631−{-# INLINE_LATE runStream #-}
614+{-# INLINE_LATE drain #-}
632−runStream :: Monad m => Stream m a -> m ()
615+drain :: Monad m => Stream m a -> m ()
633−-- runStream m = foldrM (\x xs -> x `seq` xs) (return ()) m
616+-- drain m = foldrM (\x xs -> x `seq` xs) (return ()) m
634−runStream (Stream step state) = go SPEC state
617+drain (Stream step state) = go SPEC state
635 where
618 where
636 go !_ st = do
619 go !_ st = do
637 r <- step defState st
620 r <- step defState st
@@ -827,9 +810,9 @@ findIndices p (Stream step state) = Stream step' (state, 0)
827 Skip s -> Skip (s, i+1)
810 Skip s -> Skip (s, i+1)
828 Stop -> Stop
811 Stop -> Stop
829
812
830−{-# INLINE toRevList #-}
813+{-# INLINE toListRev #-}
831−toRevList :: Monad m => Stream m a -> m [a]
814+toListRev :: Monad m => Stream m a -> m [a]
832−toRevList = foldl' (flip (:)) []
815+toListRev = foldl' (flip (:)) []
833
816
834-- We can implement reverse as:
817-- We can implement reverse as:
835--
818--
@@ -844,16 +827,50 @@ toRevList = foldl' (flip (:)) []
844reverse :: Monad m => Stream m a -> Stream m a
827reverse :: Monad m => Stream m a -> Stream m a
845reverse m = Stream step Nothing
828reverse m = Stream step Nothing
846 where
829 where
830+ {-# INLINE_LATE step #-}
847− step _ Nothing = do
831+ step _ Nothing = do
848− xs <- toRevList m
832+ xs <- toListRev m
849− return $ Skip (Just xs)
833+ return $ Skip (Just xs)
850− step _ (Just (x:xs)) = return $ Yield x (Just xs)
834+ step _ (Just (x:xs)) = return $ Yield x (Just xs)
851− step _ (Just []) = return Stop
835+ step _ (Just []) = return Stop
836+
837+-- Much faster reverse for Storables
838+{-# INLINE_NORMAL reverse' #-}
839+reverse' :: forall m a. (Monad m, Storable a) => Stream m a -> Stream m a
840+{-
841+-- This commented implementation copies the whole stream into one single array
842+-- and then streams from that array, this is 3-4x faster than the chunked code
843+-- that follows. Though this could be problematic due to unbounded large
844+-- allocations. We need to figure out why the chunked code is slower and if we
845+-- can optimize the chunked code to work as fast as this one. It may be a
846+-- fusion issue?
847+import Foreign.ForeignPtr (touchForeignPtr)
848+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
849+import Foreign.Ptr (Ptr, plusPtr)
850+reverse' m = Stream step Nothing
851+ where
852+ {-# INLINE_LATE step #-}
853+ step _ Nothing = do
854+ arr <- A.fromStreamD m
855+ let p = aEnd arr `plusPtr` negate (sizeOf (undefined :: a))
856+ return $ Skip $ Just (aStart arr, p)
852
857
853−{-# INLINE_NORMAL toArrayN #-}
858+ step _ (Just (start, p)) | p < unsafeForeignPtrToPtr start = return Stop
854−toArrayN :: forall m a. (Monad m, Storable a)
859+
855− => Int -> Stream m a -> m (Array a)
860+ step _ (Just (start, p)) = do
856−toArrayN = A.fromStreamDN
861+ let !x = A.unsafeInlineIO $ do
862+ r <- peek p
863+ touchForeignPtr start
864+ return r
865+ next = p `plusPtr` negate (sizeOf (undefined :: a))
866+ return $ Yield x (Just (start, next))
867+-}
868+reverse' m =
869+ A.flattenArraysRev
870+ $ fromStreamK
871+ $ K.reverse
872+ $ toStreamK
873+ $ A.fromStreamDArraysOf A.defaultChunkSize m
857
874
858------------------------------------------------------------------------------
875------------------------------------------------------------------------------
859-- concatMap
876-- concatMap
@@ -877,8 +894,8 @@ concatMapM f (Stream step state) = Stream step' (Left state)
877 -- can get inner_step to inline and fuse here we can perhaps get the same
894 -- can get inner_step to inline and fuse here we can perhaps get the same
878 -- performance using "concatMap fromArray".
895 -- performance using "concatMap fromArray".
879 --
896 --
880− -- XXX using the pattern synonym Stream causes a major performance issue
897+ -- XXX using the pattern synonym "Stream" causes a major performance issue
881− -- here even if the synonym does not include a adaptState call. Need to
898+ -- here even if the synonym does not include an adaptState call. Need to
882 -- find out why. Is that something to be fixed in GHC?
899 -- find out why. Is that something to be fixed in GHC?
883 step' gst (Right (UnStream inner_step inner_st, st)) = do
900 step' gst (Right (UnStream inner_step inner_st, st)) = do
884 r <- inner_step (adaptState gst) inner_st
901 r <- inner_step (adaptState gst) inner_st
@@ -889,38 +906,6 @@ concatMapM f (Stream step state) = Stream step' (Left state)
889 return $ Skip (Right (Stream inner_step inner_s, st))
906 return $ Skip (Right (Stream inner_step inner_s, st))
890 Stop -> return $ Skip (Left st)
907 Stop -> return $ Skip (Left st)
891
908
892−-- XXX concatMap does not seem to have the best possible performance so we have
893−-- a custom way to concat arrays.
894−data CAState s a = CAParent s | CANested s (ForeignPtr a) (Ptr a) (Ptr a)
895−
896−{-# INLINE_NORMAL flattenArrays #-}
897−flattenArrays :: forall m a. (Monad m, Storable a)
898− => Stream m (Array a) -> Stream m a
899−flattenArrays (Stream step state) = Stream step' (CAParent state)
900−
901− where
902−
903− {-# INLINE_LATE step' #-}
904− step' gst (CAParent st) = do
905− r <- step (adaptState gst) st
906− return $ case r of
907− Yield Array{..} s ->
908− let p = unsafeForeignPtrToPtr aStart
909− in Skip (CANested s aStart p aEnd)
910− Skip s -> Skip (CAParent s)
911− Stop -> Stop
912−
913− step' _ (CANested st _ p end) | p == end =
914− return $ Skip $ CAParent st
915−
916− step' _ (CANested st startf p end) = do
917− let !x = unsafeInlineIO $ do
918− r <- peek p
919− touchForeignPtr startf
920− return r
921− return $ Yield x (CANested st startf
922− (p `plusPtr` (sizeOf (undefined :: a))) end)
923−
924{-# INLINE concatMap #-}
909{-# INLINE concatMap #-}
925concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
910concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
926concatMap f = concatMapM (return . f)
911concatMap f = concatMapM (return . f)
@@ -932,8 +917,673 @@ concatMap f = concatMapM (return . f)
932-- "concatMap fromArray" (without the RULE) become much slower, this means
917-- "concatMap fromArray" (without the RULE) become much slower, this means
933-- "concatMap f" in general would become slower. Need to find a solution to
918-- "concatMap f" in general would become slower. Need to find a solution to
934-- this.
919-- this.
920+
921+------------------------------------------------------------------------------
922+-- Grouping/Splitting
923+------------------------------------------------------------------------------
924+
925+{-# INLINE_NORMAL grouped #-}
926+grouped :: Monad m => Fold m a b -> Stream m (a, Bool) -> Stream m b
927+grouped f (Stream step state) = Stream (stepOuter f) (Just state)
928+
929+ where
930+
931+ {-# INLINE_LATE stepOuter #-}
932+ stepOuter (Fold fstep initial done) gst (Just st) = do
933+ res <- step (adaptState gst) st
934+ case res of
935+ Yield (x,r) s -> do
936+ acc <- initial
937+ acc' <- fstep acc x
938+ if r
939+ then done acc' >>= \val -> return $ Yield val (Just s)
940+ else go SPEC s acc'
941+
942+ Skip s -> return $ Skip $ Just s
943+ Stop -> return Stop
944+
945+ where
946+
947+ go !_ stt !acc = do
948+ res <- step (adaptState gst) stt
949+ case res of
950+ Yield (x,r) s -> do
951+ acc' <- fstep acc x
952+ if r
953+ then done acc' >>= \val -> return $ Yield val (Just s)
954+ else go SPEC s acc'
955+ Skip s -> go SPEC s acc
956+ Stop -> done acc >>= \val -> return $ Yield val Nothing
957+
958+ stepOuter _ _ Nothing = return Stop
959+
960+{-# INLINE_NORMAL groupsBy #-}
961+groupsBy :: Monad m
962+ => (a -> a -> Bool)
963+ -> Fold m a b
964+ -> Stream m a
965+ -> Stream m b
966+groupsBy cmp f (Stream step state) = Stream (stepOuter f) (Just state, Nothing)
967+
968+ where
969+
970+ {-# INLINE_LATE stepOuter #-}
971+ stepOuter (Fold fstep initial done) gst (Just st, Nothing) = do
972+ res <- step (adaptState gst) st
973+ case res of
974+ Yield x s -> do
975+ acc <- initial
976+ acc' <- fstep acc x
977+ go SPEC x s acc'
978+
979+ Skip s -> return $ Skip $ (Just s, Nothing)
980+ Stop -> return Stop
981+
982+ where
983+
984+ go !_ prev stt !acc = do
985+ res <- step (adaptState gst) stt
986+ case res of
987+ Yield x s -> do
988+ if cmp x prev
989+ then do
990+ acc' <- fstep acc x
991+ go SPEC prev s acc'
992+ else done acc >>= \r -> return $ Yield r (Just s, Just x)
993+ Skip s -> go SPEC prev s acc
994+ Stop -> done acc >>= \r -> return $ Yield r (Nothing, Nothing)
995+
996+ stepOuter (Fold fstep initial done) gst (Just st, Just prev) = do
997+ acc <- initial
998+ acc' <- fstep acc prev
999+ go SPEC st acc'
1000+
1001+ where
1002+
1003+ -- XXX code duplicated from the previous equation
1004+ go !_ stt !acc = do
1005+ res <- step (adaptState gst) stt
1006+ case res of
1007+ Yield x s -> do
1008+ if cmp x prev
1009+ then do
1010+ acc' <- fstep acc x
1011+ go SPEC s acc'
1012+ else done acc >>= \r -> return $ Yield r (Just s, Just x)
1013+ Skip s -> go SPEC s acc
1014+ Stop -> done acc >>= \r -> return $ Yield r (Nothing, Nothing)
1015+
1016+ stepOuter _ _ (Nothing,_) = return Stop
1017+
1018+{-# INLINE_EARLY splitSuffixBy' #-}
1019+splitSuffixBy' :: Monad m
1020+ => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
1021+splitSuffixBy' predicate f m = grouped f (map (\a -> (a, predicate a)) m)
1022+
1023+{-# INLINE_NORMAL splitBy #-}
1024+splitBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
1025+splitBy predicate f (Stream step state) = Stream (step' f) (Just state)
1026+
1027+ where
1028+
1029+ {-# INLINE_LATE step' #-}
1030+ step' (Fold fstep initial done) gst (Just st) = initial >>= go SPEC st
1031+
1032+ where
1033+
1034+ go !_ stt !acc = do
1035+ res <- step (adaptState gst) stt
1036+ case res of
1037+ Yield x s -> do
1038+ if predicate x
1039+ then done acc >>= \r -> return $ Yield r (Just s)
1040+ else do
1041+ acc' <- fstep acc x
1042+ go SPEC s acc'
1043+ Skip s -> go SPEC s acc
1044+ Stop -> done acc >>= \r -> return $ Yield r Nothing
1045+
1046+ step' _ _ Nothing = return Stop
1047+
1048+{-# INLINE_NORMAL splitSuffixBy #-}
1049+splitSuffixBy :: Monad m
1050+ => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
1051+splitSuffixBy predicate f (Stream step state) = Stream (step' f) (Just state)
1052+
1053+ where
1054+
1055+ {-# INLINE_LATE step' #-}
1056+ step' (Fold fstep initial done) gst (Just st) = do
1057+ res <- step (adaptState gst) st
1058+ case res of
1059+ Yield x s -> do
1060+ acc <- initial
1061+ if predicate x
1062+ then done acc >>= \val -> return $ Yield val (Just s)
1063+ else do
1064+ acc' <- fstep acc x
1065+ go SPEC s acc'
1066+
1067+ Skip s -> return $ Skip $ Just s
1068+ Stop -> return Stop
1069+
1070+ where
1071+
1072+ go !_ stt !acc = do
1073+ res <- step (adaptState gst) stt
1074+ case res of
1075+ Yield x s -> do
1076+ if predicate x
1077+ then done acc >>= \r -> return $ Yield r (Just s)
1078+ else do
1079+ acc' <- fstep acc x
1080+ go SPEC s acc'
1081+ Skip s -> go SPEC s acc
1082+ Stop -> done acc >>= \r -> return $ Yield r Nothing
1083+
1084+ step' _ _ Nothing = return Stop
1085+
1086+{-# INLINE_NORMAL wordsBy #-}
1087+wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
1088+wordsBy predicate f (Stream step state) = Stream (stepOuter f) (Just state)
1089+
1090+ where
1091+
1092+ {-# INLINE_LATE stepOuter #-}
1093+ stepOuter (Fold fstep initial done) gst (Just st) = do
1094+ res <- step (adaptState gst) st
1095+ case res of
1096+ Yield x s -> do
1097+ if predicate x
1098+ then return $ Skip (Just s)
1099+ else do
1100+ acc <- initial
1101+ acc' <- fstep acc x
1102+ go SPEC s acc'
1103+
1104+ Skip s -> return $ Skip $ Just s
1105+ Stop -> return Stop
1106+
1107+ where
1108+
1109+ go !_ stt !acc = do
1110+ res <- step (adaptState gst) stt
1111+ case res of
1112+ Yield x s -> do
1113+ if predicate x
1114+ then done acc >>= \r -> return $ Yield r (Just s)
1115+ else do
1116+ acc' <- fstep acc x
1117+ go SPEC s acc'
1118+ Skip s -> go SPEC s acc
1119+ Stop -> done acc >>= \r -> return $ Yield r Nothing
1120+
1121+ stepOuter _ _ Nothing = return Stop
1122+
1123+-- String search algorithms:
1124+-- http://www-igm.univ-mlv.fr/~lecroq/string/index.html
1125+
1126+{-
1127+-- TODO can we unify the splitting operations using a splitting configuration
1128+-- like in the split package.
935--
1129--
936−-- {-# RULES "concatMap fromArray" concatMap fromArray = flattenArrays #-}
1130+data SplitStyle = Infix | Suffix | Prefix deriving (Eq, Show)
1131+
1132+data SplitOptions = SplitOptions
1133+ { style :: SplitStyle
1134+ , withSep :: Bool -- ^ keep the separators in output
1135+ -- , compact :: Bool -- ^ treat multiple consecutive separators as one
1136+ -- , trimHead :: Bool -- ^ drop blank at head
1137+ -- , trimTail :: Bool -- ^ drop blank at tail
1138+ }
1139+-}
1140+
1141+data SplitOnState s a =
1142+ GO_START
1143+ | GO_EMPTY_PAT s
1144+ | GO_SINGLE_PAT s a
1145+ | GO_SHORT_PAT s
1146+ | GO_KARP_RABIN s !(RB.RingBuffer a) !(Ptr a)
1147+ | GO_DONE
1148+
1149+{-# INLINE_NORMAL splitOn #-}
1150+splitOn
1151+ :: forall m a b. (Monad m, Storable a, Enum a, Eq a)
1152+ => Array a
1153+ -> Fold m a b
1154+ -> Stream m a
1155+ -> Stream m b
1156+splitOn patArr@Array{..} (Fold fstep initial done) (Stream step state) =
1157+ Stream stepOuter GO_START
1158+
1159+ where
1160+
1161+ patLen = A.length patArr
1162+ maxIndex = patLen - 1
1163+ elemBits = sizeOf (undefined :: a) * 8
1164+
1165+ {-# INLINE_LATE stepOuter #-}
1166+ stepOuter _ GO_START =
1167+ if patLen == 0
1168+ then return $ Skip $ GO_EMPTY_PAT state
1169+ else if patLen == 1
1170+ then return $ Skip $ GO_SINGLE_PAT state (unsafeIndex patArr 0)
1171+ else if sizeOf (undefined :: a) * patLen
1172+ <= sizeOf (undefined :: Word)
1173+ then return $ Skip $ GO_SHORT_PAT state
1174+ else do
1175+ let !(rb, rhead) = unsafeDupablePerformIO $
1176+ RB.unsafeNew patLen
1177+ return $ Skip $ GO_KARP_RABIN state rb rhead
1178+
1179+ stepOuter gst (GO_SINGLE_PAT stt pat) = initial >>= go SPEC stt
1180+
1181+ where
1182+
1183+ go !_ st !acc = do
1184+ res <- step (adaptState gst) st
1185+ case res of
1186+ Yield x s -> do
1187+ if pat == x
1188+ then do
1189+ r <- done acc
1190+ return $ Yield r (GO_SINGLE_PAT s pat)
1191+ else fstep acc x >>= go SPEC s
1192+ Skip s -> go SPEC s acc
1193+ Stop -> done acc >>= \r -> return $ Yield r GO_DONE
1194+
1195+ stepOuter gst (GO_SHORT_PAT stt) = initial >>= go0 SPEC 0 (0 :: Word) stt
1196+
1197+ where
1198+
1199+ mask :: Word
1200+ mask = (1 `shiftL` (elemBits * patLen)) - 1
1201+
1202+ addToWord wrd a = (wrd `shiftL` elemBits) .|. fromIntegral (fromEnum a)
1203+
1204+ patWord :: Word
1205+ patWord = mask .&. A.foldl' addToWord 0 patArr
1206+
1207+ go0 !_ !idx wrd st !acc = do
1208+ res <- step (adaptState gst) st
1209+ case res of
1210+ Yield x s -> do
1211+ let wrd' = addToWord wrd x
1212+ if idx == maxIndex
1213+ then do
1214+ if wrd' .&. mask == patWord
1215+ then do
1216+ r <- done acc
1217+ return $ Yield r (GO_SHORT_PAT s)
1218+ else go1 SPEC wrd' s acc
1219+ else go0 SPEC (idx + 1) wrd' s acc
1220+ Skip s -> go0 SPEC idx wrd s acc
1221+ Stop -> do
1222+ acc' <- if idx /= 0
1223+ then go2 wrd idx acc
1224+ else return acc
1225+ done acc' >>= \r -> return $ Yield r GO_DONE
1226+
1227+ {-# INLINE go1 #-}
1228+ go1 !_ wrd st !acc = do
1229+ res <- step (adaptState gst) st
1230+ case res of
1231+ Yield x s -> do
1232+ let wrd' = addToWord wrd x
1233+ old = (mask .&. wrd) `shiftR` (elemBits * (patLen - 1))
1234+ acc' <- fstep acc (toEnum $ fromIntegral old)
1235+ if wrd' .&. mask == patWord
1236+ then done acc' >>= \r -> return $ Yield r (GO_SHORT_PAT s)
1237+ else go1 SPEC wrd' s acc'
1238+ Skip s -> go1 SPEC wrd s acc
1239+ Stop -> do
1240+ acc' <- go2 wrd patLen acc
1241+ done acc' >>= \r -> return $ Yield r GO_DONE
1242+
1243+ go2 !wrd !n !acc | n > 0 = do
1244+ let old = (mask .&. wrd) `shiftR` (elemBits * (n - 1))
1245+ fstep acc (toEnum $ fromIntegral old) >>= go2 wrd (n - 1)
1246+ go2 _ _ acc = return acc
1247+
1248+ stepOuter gst (GO_KARP_RABIN stt rb rhead) = do
1249+ initial >>= go0 SPEC 0 rhead stt
1250+
1251+ where
1252+
1253+ k = 2891336453 :: Word32
1254+ coeff = k ^ patLen
1255+ addCksum cksum a = cksum * k + fromIntegral (fromEnum a)
1256+ deltaCksum cksum old new =
1257+ addCksum cksum new - coeff * fromIntegral (fromEnum old)
1258+
1259+ -- XXX shall we use a random starting hash or 1 instead of 0?
1260+ patHash = A.foldl' addCksum 0 patArr
1261+
1262+ -- rh == ringHead
1263+ go0 !_ !idx !rh st !acc = do
1264+ res <- step (adaptState gst) st
1265+ case res of
1266+ Yield x s -> do
1267+ let !rh' = unsafeInlineIO (RB.insert rb rh x)
1268+ if idx == maxIndex
1269+ then do
1270+ let fold = RB.foldRing (RB.ringBound rb)
1271+ let !ringHash = fold addCksum 0 rb
1272+ if ringHash == patHash
1273+ then go2 SPEC ringHash rh' s acc
1274+ else go1 SPEC ringHash rh' s acc
1275+ else go0 SPEC (idx + 1) rh' s acc
1276+ Skip s -> go0 SPEC idx rh s acc
1277+ Stop -> do
1278+ !acc' <- if idx /= 0
1279+ then RB.foldRingM rh fstep acc rb
1280+ else return acc
1281+ done acc' >>= \r -> return $ Yield r GO_DONE
1282+
1283+ -- XXX Theoretically this code can do 4 times faster if GHC generates
1284+ -- optimal code. If we use just "(cksum' == patHash)" condition it goes
1285+ -- 4x faster, as soon as we add the "RB.unsafeEqArray rb v" condition
1286+ -- the generated code changes drastically and becomes 4x slower. Need
1287+ -- to investigate what is going on with GHC.
1288+ {-# INLINE go1 #-}
1289+ go1 !_ !cksum !rh st !acc = do
1290+ res <- step (adaptState gst) st
1291+ case res of
1292+ Yield x s -> do
1293+ let !old = unsafeInlineIO $ peek rh
1294+ cksum' = deltaCksum cksum old x
1295+ acc' <- fstep acc old
1296+
1297+ if (cksum' == patHash)
1298+ then do
1299+ let !rh'= unsafeInlineIO (RB.insert rb rh x)
1300+ go2 SPEC cksum' rh' s acc'
1301+ else do
1302+ let !rh'= unsafeInlineIO (RB.insert rb rh x)
1303+ go1 SPEC cksum' rh' s acc'
1304+ Skip s -> go1 SPEC cksum rh s acc
1305+ Stop -> do
1306+ acc' <- RB.foldRingFullM rh fstep acc rb
1307+ done acc' >>= \r -> return $ Yield r GO_DONE
1308+
1309+ go2 !_ !cksum' !rh' s !acc' = do
1310+ if RB.unsafeEqArray rb rh' patArr
1311+ then do
1312+ r <- done acc'
1313+ return $ Yield r (GO_KARP_RABIN s rb rhead)
1314+ else go1 SPEC cksum' rh' s acc'
1315+
1316+ stepOuter gst (GO_EMPTY_PAT st) = do
1317+ res <- step (adaptState gst) st
1318+ case res of
1319+ Yield x s -> do
1320+ acc <- initial
1321+ acc' <- fstep acc x
1322+ done acc' >>= \r -> return $ Yield r (GO_EMPTY_PAT s)
1323+ Skip s -> return $ Skip (GO_EMPTY_PAT s)
1324+ Stop -> return Stop
1325+
1326+ stepOuter _ GO_DONE = return Stop
1327+
1328+{-# INLINE_NORMAL splitSuffixOn #-}
1329+splitSuffixOn
1330+ :: forall m a b. (Monad m, Storable a, Enum a, Eq a)
1331+ => Bool
1332+ -> Array a
1333+ -> Fold m a b
1334+ -> Stream m a
1335+ -> Stream m b
1336+splitSuffixOn withSep patArr@Array{..} (Fold fstep initial done)
1337+ (Stream step state) =
1338+ Stream stepOuter GO_START
1339+
1340+ where
1341+
1342+ patLen = A.length patArr
1343+ maxIndex = patLen - 1
1344+ elemBits = sizeOf (undefined :: a) * 8
1345+
1346+ {-# INLINE_LATE stepOuter #-}
1347+ stepOuter _ GO_START =
1348+ if patLen == 0
1349+ then return $ Skip $ GO_EMPTY_PAT state
1350+ else if patLen == 1
1351+ then return $ Skip $ GO_SINGLE_PAT state (unsafeIndex patArr 0)
1352+ else if sizeOf (undefined :: a) * patLen
1353+ <= sizeOf (undefined :: Word)
1354+ then return $ Skip $ GO_SHORT_PAT state
1355+ else do
1356+ let !(rb, rhead) = unsafeDupablePerformIO $
1357+ RB.unsafeNew patLen
1358+ return $ Skip $ GO_KARP_RABIN state rb rhead
1359+
1360+ stepOuter gst (GO_SINGLE_PAT stt pat) = do
1361+ -- This first part is the only difference between splitOn and
1362+ -- splitSuffixOn.
1363+ -- If the last element is a separator do not issue a blank segment.
1364+ res <- step (adaptState gst) stt
1365+ case res of
1366+ Yield x s -> do
1367+ acc <- initial
1368+ if pat == x
1369+ then do
1370+ acc' <- if withSep then fstep acc x else return acc
1371+ done acc' >>= \r -> return $ Yield r (GO_SINGLE_PAT s pat)
1372+ else fstep acc x >>= go SPEC s
1373+ Skip s -> return $ Skip $ (GO_SINGLE_PAT s pat)
1374+ Stop -> return Stop
1375+
1376+ where
1377+
1378+ -- This is identical for splitOn and splitSuffixOn
1379+ go !_ st !acc = do
1380+ res <- step (adaptState gst) st
1381+ case res of
1382+ Yield x s -> do
1383+ if pat == x
1384+ then do
1385+ acc' <- if withSep then fstep acc x else return acc
1386+ r <- done acc'
1387+ return $ Yield r (GO_SINGLE_PAT s pat)
1388+ else fstep acc x >>= go SPEC s
1389+ Skip s -> go SPEC s acc
1390+ Stop -> done acc >>= \r -> return $ Yield r GO_DONE
1391+
1392+ stepOuter gst (GO_SHORT_PAT stt) = do
1393+
1394+ -- Call "initial" only if the stream yields an element, otherwise we
1395+ -- may call "initial" but never yield anything. initial may produce a
1396+ -- side effect, therefore we will end up doing and discard a side
1397+ -- effect.
1398+
1399+ let idx = 0
1400+ let wrd = 0
1401+ res <- step (adaptState gst) stt
1402+ case res of
1403+ Yield x s -> do
1404+ acc <- initial
1405+ let wrd' = addToWord wrd x
1406+ acc' <- if withSep then fstep acc x else return acc
1407+ if idx == maxIndex
1408+ then do
1409+ if wrd' .&. mask == patWord
1410+ then done acc' >>= \r -> return $ Yield r (GO_SHORT_PAT s)
1411+ else go0 SPEC (idx + 1) wrd' s acc'
1412+ else go0 SPEC (idx + 1) wrd' s acc'
1413+ Skip s -> return $ Skip (GO_SHORT_PAT s)
1414+ Stop -> return Stop
1415+
1416+ where
1417+
1418+ mask :: Word
1419+ mask = (1 `shiftL` (elemBits * patLen)) - 1
1420+
1421+ addToWord wrd a = (wrd `shiftL` elemBits) .|. fromIntegral (fromEnum a)
1422+
1423+ patWord :: Word
1424+ patWord = mask .&. A.foldl' addToWord 0 patArr
1425+
1426+ go0 !_ !idx wrd st !acc = do
1427+ res <- step (adaptState gst) st
1428+ case res of
1429+ Yield x s -> do
1430+ let wrd' = addToWord wrd x
1431+ acc' <- if withSep then fstep acc x else return acc
1432+ if idx == maxIndex
1433+ then do
1434+ if wrd' .&. mask == patWord
1435+ then do
1436+ r <- done acc'
1437+ return $ Yield r (GO_SHORT_PAT s)
1438+ else go1 SPEC wrd' s acc'
1439+ else go0 SPEC (idx + 1) wrd' s acc'
1440+ Skip s -> go0 SPEC idx wrd s acc
1441+ Stop -> do
1442+ if (idx == maxIndex) && (wrd .&. mask == patWord)
1443+ then return Stop
1444+ else do
1445+ acc' <- if idx /= 0 && not withSep
1446+ then go2 wrd idx acc
1447+ else return acc
1448+ done acc' >>= \r -> return $ Yield r GO_DONE
1449+
1450+ {-# INLINE go1 #-}
1451+ go1 !_ wrd st !acc = do
1452+ res <- step (adaptState gst) st
1453+ case res of
1454+ Yield x s -> do
1455+ let wrd' = addToWord wrd x
1456+ old = (mask .&. wrd) `shiftR` (elemBits * (patLen - 1))
1457+ acc' <- if withSep
1458+ then fstep acc x
1459+ else fstep acc (toEnum $ fromIntegral old)
1460+ if wrd' .&. mask == patWord
1461+ then done acc' >>= \r -> return $ Yield r (GO_SHORT_PAT s)
1462+ else go1 SPEC wrd' s acc'
1463+ Skip s -> go1 SPEC wrd s acc
1464+ Stop ->
1465+ -- If the last sequence is a separator do not issue a blank
1466+ -- segment.
1467+ if wrd .&. mask == patWord
1468+ then return Stop
1469+ else do
1470+ acc' <- if withSep
1471+ then return acc
1472+ else go2 wrd patLen acc
1473+ done acc' >>= \r -> return $ Yield r GO_DONE
1474+
1475+ go2 !wrd !n !acc | n > 0 = do
1476+ let old = (mask .&. wrd) `shiftR` (elemBits * (n - 1))
1477+ fstep acc (toEnum $ fromIntegral old) >>= go2 wrd (n - 1)
1478+ go2 _ _ acc = return acc
1479+
1480+ stepOuter gst (GO_KARP_RABIN stt rb rhead) = do
1481+ let idx = 0
1482+ res <- step (adaptState gst) stt
1483+ case res of
1484+ Yield x s -> do
1485+ acc <- initial
1486+ acc' <- if withSep then fstep acc x else return acc
1487+ let !rh' = unsafeInlineIO (RB.insert rb rhead x)
1488+ if idx == maxIndex
1489+ then do
1490+ let fold = RB.foldRing (RB.ringBound rb)
1491+ let !ringHash = fold addCksum 0 rb
1492+ if ringHash == patHash
1493+ then go2 SPEC ringHash rh' s acc'
1494+ else go0 SPEC (idx + 1) rh' s acc'
1495+ else go0 SPEC (idx + 1) rh' s acc'
1496+ Skip s -> return $ Skip (GO_KARP_RABIN s rb rhead)
1497+ Stop -> return Stop
1498+
1499+ where
1500+
1501+ k = 2891336453 :: Word32
1502+ coeff = k ^ patLen
1503+ addCksum cksum a = cksum * k + fromIntegral (fromEnum a)
1504+ deltaCksum cksum old new =
1505+ addCksum cksum new - coeff * fromIntegral (fromEnum old)
1506+
1507+ -- XXX shall we use a random starting hash or 1 instead of 0?
1508+ patHash = A.foldl' addCksum 0 patArr
1509+
1510+ -- rh == ringHead
1511+ go0 !_ !idx !rh st !acc = do
1512+ res <- step (adaptState gst) st
1513+ case res of
1514+ Yield x s -> do
1515+ acc' <- if withSep then fstep acc x else return acc
1516+ let !rh' = unsafeInlineIO (RB.insert rb rh x)
1517+ if idx == maxIndex
1518+ then do
1519+ let fold = RB.foldRing (RB.ringBound rb)
1520+ let !ringHash = fold addCksum 0 rb
1521+ if ringHash == patHash
1522+ then go2 SPEC ringHash rh' s acc'
1523+ else go1 SPEC ringHash rh' s acc'
1524+ else go0 SPEC (idx + 1) rh' s acc'
1525+ Skip s -> go0 SPEC idx rh s acc
1526+ Stop -> do
1527+ -- do not issue a blank segment when we end at pattern
1528+ if (idx == maxIndex) && RB.unsafeEqArray rb rh patArr
1529+ then return Stop
1530+ else do
1531+ !acc' <- if idx /= 0 && not withSep
1532+ then RB.foldRingM rh fstep acc rb
1533+ else return acc
1534+ done acc' >>= \r -> return $ Yield r GO_DONE
1535+
1536+ -- XXX Theoretically this code can do 4 times faster if GHC generates
1537+ -- optimal code. If we use just "(cksum' == patHash)" condition it goes
1538+ -- 4x faster, as soon as we add the "RB.unsafeEqArray rb v" condition
1539+ -- the generated code changes drastically and becomes 4x slower. Need
1540+ -- to investigate what is going on with GHC.
1541+ {-# INLINE go1 #-}
1542+ go1 !_ !cksum !rh st !acc = do
1543+ res <- step (adaptState gst) st
1544+ case res of
1545+ Yield x s -> do
1546+ let !old = unsafeInlineIO $ peek rh
1547+ cksum' = deltaCksum cksum old x
1548+ acc' <- if withSep
1549+ then fstep acc x
1550+ else fstep acc old
1551+
1552+ if (cksum' == patHash)
1553+ then do
1554+ let !rh'= unsafeInlineIO (RB.insert rb rh x)
1555+ go2 SPEC cksum' rh' s acc'
1556+ else do
1557+ let !rh'= unsafeInlineIO (RB.insert rb rh x)
1558+ go1 SPEC cksum' rh' s acc'
1559+ Skip s -> go1 SPEC cksum rh s acc
1560+ Stop -> do
1561+ if RB.unsafeEqArray rb rh patArr
1562+ then return Stop
1563+ else do
1564+ acc' <- if withSep
1565+ then return acc
1566+ else RB.foldRingFullM rh fstep acc rb
1567+ done acc' >>= \r -> return $ Yield r GO_DONE
1568+
1569+ go2 !_ !cksum' !rh' s !acc' = do
1570+ if RB.unsafeEqArray rb rh' patArr
1571+ then do
1572+ r <- done acc'
1573+ return $ Yield r (GO_KARP_RABIN s rb rhead)
1574+ else go1 SPEC cksum' rh' s acc'
1575+
1576+ stepOuter gst (GO_EMPTY_PAT st) = do
1577+ res <- step (adaptState gst) st
1578+ case res of
1579+ Yield x s -> do
1580+ acc <- initial
1581+ acc' <- fstep acc x
1582+ done acc' >>= \r -> return $ Yield r (GO_EMPTY_PAT s)
1583+ Skip s -> return $ Skip (GO_EMPTY_PAT s)
1584+ Stop -> return Stop
1585+
1586+ stepOuter _ GO_DONE = return Stop
937
1587
938------------------------------------------------------------------------------
1588------------------------------------------------------------------------------
939-- Substreams
1589-- Substreams
@@ -1011,31 +1661,12 @@ stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
1011-- | Execute a monadic action for each element of the 'Stream'
1661-- | Execute a monadic action for each element of the 'Stream'
1012{-# INLINE_NORMAL mapM_ #-}
1662{-# INLINE_NORMAL mapM_ #-}
1013mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()
1663mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()
1014−mapM_ m = runStream . mapM m
1664+mapM_ m = drain . mapM m
1015
1665
1016------------------------------------------------------------------------------
1666------------------------------------------------------------------------------
1017-- Converting folds
1667-- Converting folds
1018------------------------------------------------------------------------------
1668------------------------------------------------------------------------------
1019
1669
1020−-- Convert a direct stream to and from CPS encoded stream
1021−{-# INLINE_LATE toStreamK #-}
1022−toStreamK :: Monad m => Stream m a -> K.Stream m a
1023−toStreamK (Stream step state) = go state
1024− where
1025− go st = K.mkStream $ \gst yld sng stp -> do
1026− r <- step gst st
1027− case r of
1028− Yield x s -> yld x (go s)
1029− Skip s -> K.foldStreamShared gst yld sng stp $ go s
1030− Stop -> stp
1031−
1032−#ifndef DISABLE_FUSION
1033−{-# RULES "fromStreamK/toStreamK fusion"
1034− forall s. toStreamK (fromStreamK s) = s #-}
1035−{-# RULES "toStreamK/fromStreamK fusion"
1036− forall s. fromStreamK (toStreamK s) = s #-}
1037−#endif
1038−
1039{-# INLINE fromStreamD #-}
1670{-# INLINE fromStreamD #-}
1040fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
1671fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
1041fromStreamD = K.fromStream . toStreamK
1672fromStreamD = K.fromStream . toStreamK
@@ -1238,6 +1869,21 @@ scanl1M' fstep (Stream step state) = Stream step' (state, Nothing)
1238scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
1869scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
1239scanl1' f = scanl1M' (\x y -> return (f x y))
1870scanl1' f = scanl1M' (\x y -> return (f x y))
1240
1871
1872+{-# INLINE tap #-}
1873+tap :: Monad m => Sink m a -> Stream m a -> Stream m a
1874+tap (Sink fstep) (Stream step state) = Stream step' state
1875+
1876+ where
1877+
1878+ step' gst st = do
1879+ r <- step gst st
1880+ case r of
1881+ Yield x s -> do
1882+ fstep x
1883+ return $ Yield x s
1884+ Skip s -> return $ Skip s
1885+ Stop -> return $ Stop
1886+
1241-------------------------------------------------------------------------------
1887-------------------------------------------------------------------------------
1242-- Filtering
1888-- Filtering
1243-------------------------------------------------------------------------------
1889-------------------------------------------------------------------------------
src/Streamly/Streams/StreamD/Type.hs+127
@@ -32,6 +32,8 @@ module Streamly.Streams.StreamD.Type
32 , Stream (UnStream)
32 , Stream (UnStream)
33 , pattern Stream
33 , pattern Stream
34#endif
34#endif
35+ , fromStreamK
36+ , toStreamK
35 , map
37 , map
36 , mapM
38 , mapM
37
39
@@ -42,6 +44,8 @@ module Streamly.Streams.StreamD.Type
42
44
43 , foldl'
45 , foldl'
44 , foldlM'
46 , foldlM'
47+ , foldlx'
48+ , foldlMx'
45
49
46 , toList
50 , toList
47 , fromList
51 , fromList
@@ -49,6 +53,7 @@ module Streamly.Streams.StreamD.Type
49 , eqBy
53 , eqBy
50 , cmpBy
54 , cmpBy
51 , take
55 , take
56+ , groupsOf
52 )
57 )
53where
58where
54
59
@@ -56,7 +61,9 @@ import Control.Applicative (liftA2)
56import Control.Monad.Trans (lift, MonadTrans)
61import Control.Monad.Trans (lift, MonadTrans)
57import GHC.Types (SPEC(..))
62import GHC.Types (SPEC(..))
58import Prelude hiding (map, mapM, foldr, take)
63import Prelude hiding (map, mapM, foldr, take)
64+
59import Streamly.SVar (State(..), adaptState, defState)
65import Streamly.SVar (State(..), adaptState, defState)
66+import Streamly.Fold.Types (Fold(..))
60
67
61import qualified Streamly.Streams.StreamK as K
68import qualified Streamly.Streams.StreamK as K
62
69
@@ -93,6 +100,35 @@ pattern Stream step state <- (unShare -> UnStream step state)
93{-# COMPLETE Stream #-}
100{-# COMPLETE Stream #-}
94#endif
101#endif
95
102
103+{-# INLINE_LATE fromStreamK #-}
104+fromStreamK :: Monad m => K.Stream m a -> Stream m a
105+fromStreamK = Stream step
106+ where
107+ step gst m1 =
108+ let stop = return Stop
109+ single a = return $ Yield a K.nil
110+ yieldk a r = return $ Yield a r
111+ in K.foldStreamShared gst yieldk single stop m1
112+
113+-- Convert a direct stream to and from CPS encoded stream
114+{-# INLINE_LATE toStreamK #-}
115+toStreamK :: Monad m => Stream m a -> K.Stream m a
116+toStreamK (Stream step state) = go state
117+ where
118+ go st = K.mkStream $ \gst yld sng stp -> do
119+ r <- step gst st
120+ case r of
121+ Yield x s -> yld x (go s)
122+ Skip s -> K.foldStreamShared gst yld sng stp $ go s
123+ Stop -> stp
124+
125+#ifndef DISABLE_FUSION
126+{-# RULES "fromStreamK/toStreamK fusion"
127+ forall s. toStreamK (fromStreamK s) = s #-}
128+{-# RULES "toStreamK/fromStreamK fusion"
129+ forall s. fromStreamK (toStreamK s) = s #-}
130+#endif
131+
96------------------------------------------------------------------------------
132------------------------------------------------------------------------------
97-- Instances
133-- Instances
98------------------------------------------------------------------------------
134------------------------------------------------------------------------------
@@ -190,6 +226,27 @@ foldrT f final (Stream step state) = go SPEC state
190toList :: Monad m => Stream m a -> m [a]
226toList :: Monad m => Stream m a -> m [a]
191toList = foldr (:) []
227toList = foldr (:) []
192
228
229+-- XXX run begin action only if the stream is not empty.
230+{-# INLINE_NORMAL foldlMx' #-}
231+foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
232+foldlMx' fstep begin done (Stream step state) =
233+ begin >>= \x -> go SPEC x state
234+ where
235+ -- XXX !acc?
236+ go !_ acc st = acc `seq` do
237+ r <- step defState st
238+ case r of
239+ Yield x s -> do
240+ acc' <- fstep acc x
241+ go SPEC acc' s
242+ Skip s -> go SPEC acc s
243+ Stop -> done acc
244+
245+{-# INLINE foldlx' #-}
246+foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
247+foldlx' fstep begin done m =
248+ foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
249+
193-- XXX implement in terms of foldlMx'?
250-- XXX implement in terms of foldlMx'?
194{-# INLINE_NORMAL foldlM' #-}
251{-# INLINE_NORMAL foldlM' #-}
195foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
252foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
@@ -290,3 +347,73 @@ take n (Stream step state) = n `seq` Stream step' (state, 0)
290 Skip s -> Skip (s, i)
347 Skip s -> Skip (s, i)
291 Stop -> Stop
348 Stop -> Stop
292 step' _ (_, _) = return Stop
349 step' _ (_, _) = return Stop
350+
351+------------------------------------------------------------------------------
352+-- Grouping/Splitting
353+------------------------------------------------------------------------------
354+
355+{-# INLINE_LATE foldOneGroup #-}
356+foldOneGroup
357+ :: Monad m
358+ => Int
359+ -> Fold m a b
360+ -> a
361+ -> State K.Stream m a
362+ -> (State K.Stream m a -> s -> m (Step s a))
363+ -> s
364+ -> m (b, Maybe s)
365+foldOneGroup n (Fold fstep begin done) x gst step state = do
366+ acc0 <- begin
367+ acc <- fstep acc0 x
368+ go SPEC state acc 1
369+
370+ where
371+
372+ -- XXX is it strict enough?
373+ go !_ st !acc i | i < n = do
374+ r <- step gst st
375+ case r of
376+ Yield y s -> do
377+ acc1 <- fstep acc y
378+ go SPEC s acc1 (i + 1)
379+ Skip s -> go SPEC s acc i
380+ Stop -> do
381+ res <- done acc
382+ return (res, Nothing)
383+ go !_ st acc _ = do
384+ r <- done acc
385+ return (r, Just st)
386+
387+-- groupsOf takes 11 sec to write the file from a stream, whereas just
388+-- reading in the same file as a stream and folding each element of the stream
389+-- using (+) takes just 1.5 sec, so we are still pretty slow (7x slow), there
390+-- is scope to make it faster. There is a possibility of better fusion here.
391+--
392+{-# INLINE_NORMAL groupsOf #-}
393+groupsOf
394+ :: Monad m
395+ => Int
396+ -> Fold m a b
397+ -> Stream m a
398+ -> Stream m b
399+groupsOf n f (Stream step state) =
400+ n `seq` Stream stepOuter (Just state)
401+
402+ where
403+
404+ {-# INLINE_LATE stepOuter #-}
405+ stepOuter gst (Just st) = do
406+ -- We retrieve the first element of the stream before we start to fold
407+ -- a chunk so that we do not return an empty chunk in case the stream
408+ -- is empty.
409+ res <- step (adaptState gst) st
410+ case res of
411+ Yield x s -> do
412+ -- XXX how to make sure that stepInner and f get fused
413+ -- This problem seems to be similar to the concatMap problem
414+ (r, s1) <- foldOneGroup n f x (adaptState gst) step s
415+ return $ Yield r s1
416+ Skip s -> return $ Skip $ Just s
417+ Stop -> return Stop
418+
419+ stepOuter _ Nothing = return Stop
src/Streamly/Streams/StreamK.hs+6−6
@@ -87,7 +87,7 @@ module Streamly.Streams.StreamK
87 , foldlMx'
87 , foldlMx'
88
88
89 -- ** Specialized Folds
89 -- ** Specialized Folds
90− , runStream
90+ , drain
91 , null
91 , null
92 , head
92 , head
93 , tail
93 , tail
@@ -490,11 +490,11 @@ foldlT step begin m = go begin m
490------------------------------------------------------------------------------
490------------------------------------------------------------------------------
491
491
492-- |
492-- |
493−-- > runStream = foldl' (\_ _ -> ()) ()
493+-- > drain = foldl' (\_ _ -> ()) ()
494−-- > runStream = mapM_ (\_ -> return ())
494+-- > drain = mapM_ (\_ -> return ())
495−{-# INLINE runStream #-}
495+{-# INLINE drain #-}
496−runStream :: (Monad m, IsStream t) => t m a -> m ()
496+drain :: (Monad m, IsStream t) => t m a -> m ()
497−runStream = go
497+drain = go
498 where
498 where
499 go m1 =
499 go m1 =
500 let stop = return ()
500 let stop = return ()
src/Streamly/Streams/StreamK/Type.hs+2−2
@@ -141,8 +141,8 @@ class IsStream t where
141 --
141 --
142 -- @
142 -- @
143 -- let delay = threadDelay 1000000 >> print 1
143 -- let delay = threadDelay 1000000 >> print 1
144− -- runStream $ serially $ delay |: delay |: delay |: nil
144+ -- drain $ serially $ delay |: delay |: delay |: nil
145− -- runStream $ parallely $ delay |: delay |: delay |: nil
145+ -- drain $ parallely $ delay |: delay |: delay |: nil
146 -- @
146 -- @
147 --
147 --
148 -- /Concurrent (do not use 'parallely' to construct infinite streams)/
148 -- /Concurrent (do not use 'parallely' to construct infinite streams)/
src/Streamly/Strict.hsnew file+57
@@ -0,0 +1,57 @@
1+-- |
2+-- Module : Streamly.Strict
3+-- Copyright : (c) 2019 Composewell Technologies
4+-- (c) 2013 Gabriel Gonzalez
5+-- License : BSD3
6+-- Maintainer : harendra.kumar@gmail.com
7+-- Stability : experimental
8+-- Portability : GHC
9+--
10+-- | Strict data types to be used as accumulator for strict left folds and
11+-- scans. For more comprehensive strict data types see
12+-- https://hackage.haskell.org/package/strict-base-types . The names have been
13+-- suffixed by a prime so that programmers can easily distinguish the strict
14+-- versions from the lazy ones.
15+--
16+-- One major advantage of strict data structures as accumulators in folds and
17+-- scans is that it helps the compiler optimize the code much better by
18+-- unboxing. In a big tight loop the difference could be huge.
19+--
20+module Streamly.Strict
21+ (
22+ Tuple' (..)
23+ , Tuple3' (..)
24+ , Maybe' (..)
25+ , fromStrictMaybe
26+ , Either' (..)
27+ )
28+where
29+
30+-------------------------------------------------------------------------------
31+-- Tuples
32+-------------------------------------------------------------------------------
33+--
34+data Tuple' a b = Tuple' !a !b
35+data Tuple3' a b c = Tuple3' !a !b !c
36+
37+-------------------------------------------------------------------------------
38+-- Maybe
39+-------------------------------------------------------------------------------
40+--
41+-- | A strict 'Maybe'
42+data Maybe' a = Just' !a | Nothing'
43+
44+-- XXX perhaps we can use a type class having fromStrict/toStrict operations.
45+--
46+-- | Convert strict Maybe' to lazy Maybe
47+{-# INLINABLE fromStrictMaybe #-}
48+fromStrictMaybe :: Monad m => Maybe' a -> m (Maybe a)
49+fromStrictMaybe Nothing' = return $ Nothing
50+fromStrictMaybe (Just' a) = return $ Just a
51+
52+-------------------------------------------------------------------------------
53+-- Either
54+-------------------------------------------------------------------------------
55+--
56+-- | A strict 'Either'
57+data Either' a b = Left' !a | Right' !b
src/Streamly/Tutorial.hs+80−73
@@ -6,19 +6,20 @@
6-- License : BSD3
6-- License : BSD3
7-- Maintainer : harendra.kumar@gmail.com
7-- Maintainer : harendra.kumar@gmail.com
8--
8--
9−-- Streamly is a general computing framework based on streaming IO. The IO
9+-- Streamly is a general computing framework based on concurrent data flow
10−-- monad and pure lists are a special case of streamly. On one hand, streamly
10+-- programming. The IO monad and pure lists are a special case of streamly. On
11−-- extends the lists of pure values to lists of monadic actions, on the other
11+-- one hand, streamly extends the lists of pure values to lists of monadic
12−-- hand it extends the IO monad with concurrent non-determinism. In simple
12+-- actions, on the other hand it extends the IO monad with concurrent
13−-- imperative terms we can say that streamly extends the IO monad with @for@
13+-- non-determinism. In simple imperative terms we can say that streamly extends
14−-- loops and nested @for@ loops with concurrency support. You can understand
14+-- the IO monad with @for@ loops and nested @for@ loops with concurrency
15−-- this analogy better once you can go through this tutorial.
15+-- support. Hopefully, this analogy becomes clearer once you go through this
16+-- tutorial.
16--
17--
17-- Streaming in general enables writing modular, composable and scalable
18-- Streaming in general enables writing modular, composable and scalable
18-- applications with ease, and concurrency allows you to make them scale and
19-- applications with ease, and concurrency allows you to make them scale and
19-- perform well. Streamly enables writing scalable concurrent applications
20-- perform well. Streamly enables writing scalable concurrent applications
20-- without being aware of threads or synchronization. No explicit thread
21-- without being aware of threads or synchronization. No explicit thread
21−-- control is needed, where applicable the concurrency rate is automatically
22+-- control is needed. Where applicable, concurrency rate is automatically
22-- controlled based on the demand by the consumer. However, combinators can be
23-- controlled based on the demand by the consumer. However, combinators can be
23-- used to fine tune the concurrency control.
24-- used to fine tune the concurrency control.
24--
25--
@@ -29,12 +30,19 @@
29-- programming in an elegant and simple API that is a natural extension of pure
30-- programming in an elegant and simple API that is a natural extension of pure
30-- lists to monadic streams.
31-- lists to monadic streams.
31--
32--
32−-- In this tutorial we will go over the basic concepts and how to
33+-- In this tutorial we will go over the basic concepts and how to use the
34+-- library. Before you go through this tutorial we recommend that you take a
35+-- look at:
36+--
37+-- * The quick overview in the package <README.md README file>.
38+-- * The overview of streams and folds in the "Streamly" module.
39+--
33−-- use the library. See the last section for further reading resources.
40+-- Once you finish this tutorial, see the last section for further reading
41+-- resources.
34
42
35module Streamly.Tutorial
43module Streamly.Tutorial
36 (
44 (
37− -- * Streams
45+ -- * Stream Types
38 -- $streams
46 -- $streams
39
47
40 -- * Concurrent Streams
48 -- * Concurrent Streams
@@ -75,7 +83,7 @@ module Streamly.Tutorial
75 -- *** Wide Serial Composition ('WSerial')
83 -- *** Wide Serial Composition ('WSerial')
76 -- $interleaved
84 -- $interleaved
77
85
78− -- *** Deep Ahead Composition ('Ahead')
86+ -- *** Deep Speculative Composition ('Ahead')
79 -- $ahead
87 -- $ahead
80
88
81 -- *** Deep Asynchronous Composition ('Async')
89 -- *** Deep Asynchronous Composition ('Async')
@@ -106,7 +114,7 @@ module Streamly.Tutorial
106 -- *** Wide Serial Nesting ('WSerial')
114 -- *** Wide Serial Nesting ('WSerial')
107 -- $interleavedNesting
115 -- $interleavedNesting
108
116
109− -- *** Deep Ahead Nesting ('Ahead')
117+ -- *** Deep Speculative Nesting ('Ahead')
110 -- $aheadNesting
118 -- $aheadNesting
111
119
112 -- *** Deep Asynchronous Nesting ('Async')
120 -- *** Deep Asynchronous Nesting ('Async')
@@ -172,22 +180,20 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
172
180
173-- $streams
181-- $streams
174--
182--
175−-- The way a list represents a sequence of pure values, a stream represents a
176−-- sequence of monadic actions. The monadic stream API offered by Streamly is
183+-- The monadic stream API offered by Streamly is very close to the Haskell
177−-- very close to the Haskell "Prelude" pure lists' API, it can be considered as a
184+-- "Prelude" pure lists' API, it can be considered as a natural extension of
178−-- natural extension of lists to monadic actions. Streamly streams provide
185+-- lists to monadic actions. Streamly streams provide concurrent composition
179−-- concurrent composition and merging of streams. It can be considered as a
186+-- and merging of streams. It can be considered as a concurrent list
180−-- concurrent list transformer. In contrast to the "Prelude" lists, merging or
187+-- transformer.
181−-- appending streams of arbitrary length is scalable and inexpensive.
182--
188--
183-- The basic stream type is 'Serial', it represents a sequence of IO actions,
189-- The basic stream type is 'Serial', it represents a sequence of IO actions,
184-- and is a 'Monad'. The 'Serial' monad is almost a drop in replacement for
190-- and is a 'Monad'. The 'Serial' monad is almost a drop in replacement for
185-- the 'IO' monad, IO monad is a special case of the 'Serial' monad; IO monad
191-- the 'IO' monad, IO monad is a special case of the 'Serial' monad; IO monad
186-- represents a single IO action whereas the 'Serial' monad represents a series
192-- represents a single IO action whereas the 'Serial' monad represents a series
187-- of IO actions. The only change you need to make to go from 'IO' to 'Serial'
193-- of IO actions. The only change you need to make to go from 'IO' to 'Serial'
188−-- is to use 'runStream' to run the monad and to prefix the IO actions with
194+-- is to use 'drain' to run the monad and to prefix the IO actions with
189-- either 'yieldM' or 'liftIO'. If you use liftIO you can switch from 'Serial'
195-- either 'yieldM' or 'liftIO'. If you use liftIO you can switch from 'Serial'
190−-- to IO monad by simply removing the 'runStream' function; no other changes
196+-- to IO monad by simply removing the 'drain' function; no other changes
191-- are needed unless you have used some stream specific composition or
197-- are needed unless you have used some stream specific composition or
192-- combinators.
198-- combinators.
193--
199--
@@ -326,9 +332,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
326-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
332-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
327--
333--
328-- @
334-- @
329−-- > 'runStream' $ liftIO $ putStrLn "Hello world!"
335+-- > 'drain' $ liftIO $ putStrLn "Hello world!"
330-- Hello world!
336-- Hello world!
331−-- > 'runStream' $ lift $ putStrLn "Hello world!"
337+-- > 'drain' $ lift $ putStrLn "Hello world!"
332-- Hello world!
338-- Hello world!
333-- @
339-- @
334--
340--
@@ -385,7 +391,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
385-- right fold using 'consM' and 'nil':
391-- right fold using 'consM' and 'nil':
386--
392--
387-- @
393-- @
388−-- > 'runStream' $ 'Prelude.foldr' ('|:') S.'nil' ['putStr' "Hello ", 'putStrLn' "world!"]
394+-- > 'drain' $ 'Prelude.foldr' ('|:') S.'nil' ['putStr' "Hello ", 'putStrLn' "world!"]
389-- Hello world!
395-- Hello world!
390-- @
396-- @
391--
397--
@@ -412,14 +418,14 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
412-- The following finishes in 10 seconds (100 seconds when serial):
418-- The following finishes in 10 seconds (100 seconds when serial):
413--
419--
414-- @
420-- @
415−-- > 'runStream' $ 'asyncly' $ S.'replicateM' 10 $ p 10
421+-- > 'drain' $ 'asyncly' $ S.'replicateM' 10 $ p 10
416-- @
422-- @
417--
423--
418
424
419-- $eliminating
425-- $eliminating
420--
426--
421−-- We have already seen 'runStream' and 'toList' to eliminate a stream in the
427+-- We have already seen 'drain' and 'toList' to eliminate a stream in the
422−-- examples above. 'runStream' runs a stream discarding the results i.e. only
428+-- examples above. 'drain' runs a stream discarding the results i.e. only
423-- for effects. 'toList' runs the stream and collects the results in a list.
429-- for effects. 'toList' runs the stream and collects the results in a list.
424--
430--
425-- For other ways to eliminate a stream see the @Folding@ section in
431-- For other ways to eliminate a stream see the @Folding@ section in
@@ -439,7 +445,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
439-- forever:
445-- forever:
440--
446--
441-- @
447-- @
442−-- > 'runStream' $ S.'repeatM' getLine & S.'mapM' putStrLn
448+-- > 'drain' $ S.'repeatM' getLine & S.'mapM' putStrLn
443-- @
449-- @
444--
450--
445-- The following code snippet reads lines from standard input, filters blank
451-- The following code snippet reads lines from standard input, filters blank
@@ -452,7 +458,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
452-- import Data.Char (toUpper)
458-- import Data.Char (toUpper)
453-- import Data.Function ((&))
459-- import Data.Function ((&))
454--
460--
455−-- main = 'runStream' $
461+-- main = 'drain' $
456-- S.'repeatM' getLine
462-- S.'repeatM' getLine
457-- & S.'filter' (not . null)
463-- & S.'filter' (not . null)
458-- & S.'drop' 1
464-- & S.'drop' 1
@@ -473,7 +479,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
473--
479--
474-- @
480-- @
475-- > let p n = threadDelay (n * 1000000) >> return n
481-- > let p n = threadDelay (n * 1000000) >> return n
476−-- > runStream $ aheadly $ S.'mapM' (\\x -> p 1 >> print x) (serially $ repeatM (p 1))
482+-- > drain $ aheadly $ S.'mapM' (\\x -> p 1 >> print x) (serially $ repeatM (p 1))
477-- @
483-- @
478--
484--
479
485
@@ -488,7 +494,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
488--
494--
489-- @
495-- @
490-- > let p n = threadDelay (n * 1000000) >> return n
496-- > let p n = threadDelay (n * 1000000) >> return n
491−-- > runStream $ S.'repeatM' (p 1) '|&' S.'mapM' (\\x -> p 1 >> print x)
497+-- > drain $ S.'repeatM' (p 1) '|&' S.'mapM' (\\x -> p 1 >> print x)
492-- @
498-- @
493
499
494-- $semigroup
500-- $semigroup
@@ -544,7 +550,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
544-- 3, 4:
550-- 3, 4:
545--
551--
546-- @
552-- @
547−-- main = 'runStream' $ (print 1 |: print 2 |: nil) <> (print 3 |: print 4 |: nil)
553+-- main = 'drain' $ (print 1 |: print 2 |: nil) <> (print 3 |: print 4 |: nil)
548-- @
554-- @
549-- @
555-- @
550-- 1
556-- 1
@@ -558,7 +564,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
558-- same thread and take a combined total of @3 + 2 + 1 = 6@ seconds:
564-- same thread and take a combined total of @3 + 2 + 1 = 6@ seconds:
559--
565--
560-- @
566-- @
561−-- main = 'runStream' $ delay 3 <> delay 2 <> delay 1
567+-- main = 'drain' $ delay 3 <> delay 2 <> delay 1
562-- @
568-- @
563-- @
569-- @
564-- ThreadId 36: Delay 3
570-- ThreadId 36: Delay 3
@@ -571,7 +577,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
571-- irrespective of the type of stream:
577-- irrespective of the type of stream:
572--
578--
573-- @
579-- @
574−-- main = 'runStream' $ (print 1 |: print 2 |: nil) \`serial` (print 3 |: print 4 |: nil)
580+-- main = 'drain' $ (print 1 |: print 2 |: nil) \`serial` (print 3 |: print 4 |: nil)
575-- @
581-- @
576
582
577-- $interleaved
583-- $interleaved
@@ -588,7 +594,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
588-- The following example prints the sequence 1, 3, 2, 4
594-- The following example prints the sequence 1, 3, 2, 4
589--
595--
590-- @
596-- @
591−-- main = 'runStream' . 'wSerially' $ (print 1 |: print 2 |: nil) <> (print 3 |: print 4 |: nil)
597+-- main = 'drain' . 'wSerially' $ (print 1 |: print 2 |: nil) <> (print 3 |: print 4 |: nil)
592-- @
598-- @
593-- @
599-- @
594-- 1
600-- 1
@@ -603,7 +609,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
603-- thread and take a combined total of @3 + 2 + 1 = 6@ seconds:
609-- thread and take a combined total of @3 + 2 + 1 = 6@ seconds:
604--
610--
605-- @
611-- @
606−-- main = 'runStream' . 'wSerially' $ delay 3 <> delay 2 <> delay 1
612+-- main = 'drain' . 'wSerially' $ delay 3 <> delay 2 <> delay 1
607-- @
613-- @
608-- @
614-- @
609-- ThreadId 36: Delay 3
615-- ThreadId 36: Delay 3
@@ -617,7 +623,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
617-- combinator in the following example:
623-- combinator in the following example:
618--
624--
619-- @
625-- @
620−-- main = 'runStream' $ (print 1 |: print 2 |: nil) \`wSerial` (print 3 |: print 4 |: nil)
626+-- main = 'drain' $ (print 1 |: print 2 |: nil) \`wSerial` (print 3 |: print 4 |: nil)
621-- @
627-- @
622-- @
628-- @
623-- 1
629-- 1
@@ -684,7 +690,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
684--
690--
685-- @
691-- @
686-- main = do
692-- main = do
687−-- xs \<- 'runStream' . 'asyncly' $ (p 1 |: p 2 |: nil) <> (p 3 |: p 4 |: nil)
693+-- xs \<- 'drain' . 'asyncly' $ (p 1 |: p 2 |: nil) <> (p 3 |: p 4 |: nil)
688-- print xs
694-- print xs
689-- where p n = threadDelay 1000000 >> return n
695-- where p n = threadDelay 1000000 >> return n
690-- @
696-- @
@@ -699,7 +705,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
699--
705--
700-- @
706-- @
701-- main = do
707-- main = do
702−-- xs \<- 'runStream' . 'asyncly' $ (serially $ p 1 |: p 2 |: nil) <> (serially $ p 3 |: p 4 |: nil)
708+-- xs \<- 'drain' . 'asyncly' $ (serially $ p 1 |: p 2 |: nil) <> (serially $ p 3 |: p 4 |: nil)
703-- print xs
709-- print xs
704-- where p n = threadDelay 1000000 >> return n
710-- where p n = threadDelay 1000000 >> return n
705-- @
711-- @
@@ -713,7 +719,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
713-- (3, 2, 1) = 3@ seconds:
719-- (3, 2, 1) = 3@ seconds:
714--
720--
715-- @
721-- @
716−-- main = 'runStream' . 'asyncly' $ delay 3 '<>' delay 2 '<>' delay 1
722+-- main = 'drain' . 'asyncly' $ delay 3 '<>' delay 2 '<>' delay 1
717-- @
723-- @
718-- @
724-- @
719-- ThreadId 42: Delay 1
725-- ThreadId 42: Delay 1
@@ -732,7 +738,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
732-- second as all of the actions are concurrent.
738-- second as all of the actions are concurrent.
733--
739--
734-- @
740-- @
735−-- main = 'runStream' . 'asyncly' $ (delay 1 <> delay 2) <> (delay 3 <> delay 4)
741+-- main = 'drain' . 'asyncly' $ (delay 1 <> delay 2) <> (delay 3 <> delay 4)
736-- @
742-- @
737-- @
743-- @
738-- 1
744-- 1
@@ -749,7 +755,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
749-- even if none of them blocks:
755-- even if none of them blocks:
750--
756--
751-- @
757-- @
752−-- main = 'runStream' . 'asyncly' $ traced (sqrt 9) '<>' traced (sqrt 16) '<>' traced (sqrt 25)
758+-- main = 'drain' . 'asyncly' $ traced (sqrt 9) '<>' traced (sqrt 16) '<>' traced (sqrt 25)
753-- where traced m = S.'yieldM' (myThreadId >>= print) >> return m
759-- where traced m = S.'yieldM' (myThreadId >>= print) >> return m
754-- @
760-- @
755-- @
761-- @
@@ -767,7 +773,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
767-- not used the 'asyncly' combinator in the following example:
773-- not used the 'asyncly' combinator in the following example:
768--
774--
769-- @
775-- @
770−-- main = 'runStream' $ delay 3 \`async` delay 2 \`async` delay 1
776+-- main = 'drain' $ delay 3 \`async` delay 2 \`async` delay 1
771-- @
777-- @
772-- @
778-- @
773-- ThreadId 42: Delay 1
779-- ThreadId 42: Delay 1
@@ -810,7 +816,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
810-- first traversal order but this is not guaranteed.
816-- first traversal order but this is not guaranteed.
811--
817--
812-- @
818-- @
813−-- main = 'runStream' . 'wAsyncly' $ (serially $ print 1 |: print 2 |: nil) <> (serially $ print 3 |: print 4 |: nil)
819+-- main = 'drain' . 'wAsyncly' $ (serially $ print 1 |: print 2 |: nil) <> (serially $ print 3 |: print 4 |: nil)
814-- @
820-- @
815-- @
821-- @
816-- 1
822-- 1
@@ -825,7 +831,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
825-- the 'wAsyncly' combinator in the following example:
831-- the 'wAsyncly' combinator in the following example:
826--
832--
827-- @
833-- @
828−-- main = 'runStream' $ delay 3 \`wAsync` delay 2 \`wAsync` delay 1
834+-- main = 'drain' $ delay 3 \`wAsync` delay 2 \`wAsync` delay 1
829-- @
835-- @
830-- @
836-- @
831-- ThreadId 42: Delay 1
837-- ThreadId 42: Delay 1
@@ -867,7 +873,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
867-- import qualified Streamly.Prelude as S
873-- import qualified Streamly.Prelude as S
868-- import Network.HTTP.Simple
874-- import Network.HTTP.Simple
869--
875--
870−-- main = 'runStream' . 'parallely' $ google \<> bing \<> duckduckgo
876+-- main = 'drain' . 'parallely' $ google \<> bing \<> duckduckgo
871-- where
877-- where
872-- google = get "https://www.google.com/search?q=haskell"
878-- google = get "https://www.google.com/search?q=haskell"
873-- bing = get "https://www.bing.com/search?q=haskell"
879-- bing = get "https://www.bing.com/search?q=haskell"
@@ -881,7 +887,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
881-- 'parallely' combinator in the following example:
887-- 'parallely' combinator in the following example:
882--
888--
883-- @
889-- @
884−-- main = 'runStream' $ delay 3 \`parallel` delay 2 \`wAsync` delay 1
890+-- main = 'drain' $ delay 3 \`parallel` delay 2 \`wAsync` delay 1
885-- @
891-- @
886-- @
892-- @
887-- ThreadId 42: Delay 1
893-- ThreadId 42: Delay 1
@@ -928,10 +934,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
928-- import Control.Concurrent
934-- import Control.Concurrent
929--
935--
930-- main = do
936-- main = do
931−-- 'runStream' $ 'asyncly' $ foldMap delay [1..10]
937+-- 'drain' $ 'asyncly' $ foldMap delay [1..10]
932−-- 'runStream' $ 'foldWith' 'async' (map delay [1..10])
938+-- 'drain' $ 'foldWith' 'async' (map delay [1..10])
933−-- 'runStream' $ 'foldMapWith' 'async' delay [1..10]
939+-- 'drain' $ 'foldMapWith' 'async' delay [1..10]
934−-- 'runStream' $ 'forEachWith' 'async' [1..10] delay
940+-- 'drain' $ 'forEachWith' 'async' [1..10] delay
935-- where delay n = S.'yieldM' $ threadDelay (n * 1000000) >> print n
941-- where delay n = S.'yieldM' $ threadDelay (n * 1000000) >> print n
936-- @
942-- @
937
943
@@ -980,7 +986,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
980-- import "Streamly"
986-- import "Streamly"
981-- import qualified "Streamly.Prelude" as S
987-- import qualified "Streamly.Prelude" as S
982--
988--
983−-- main = 'runStream' $ do
989+-- main = 'drain' $ do
984-- x <- S.'fromFoldable' [3,2,1]
990-- x <- S.'fromFoldable' [3,2,1]
985-- delay x
991-- delay x
986-- @
992-- @
@@ -1002,7 +1008,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1002-- import "Streamly"
1008-- import "Streamly"
1003-- import qualified "Streamly.Prelude" as S
1009-- import qualified "Streamly.Prelude" as S
1004--
1010--
1005−-- main = 'runStream' $ forever $ S.yieldM getLine >>= S.yieldM . putStrLn
1011+-- main = 'drain' $ forever $ S.yieldM getLine >>= S.yieldM . putStrLn
1006-- @
1012-- @
1007--
1013--
1008-- When multiple streams are composed using this style they nest in a DFS
1014-- When multiple streams are composed using this style they nest in a DFS
@@ -1014,7 +1020,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1014-- import "Streamly"
1020-- import "Streamly"
1015-- import qualified "Streamly.Prelude" as S
1021-- import qualified "Streamly.Prelude" as S
1016--
1022--
1017−-- main = 'runStream' $ do
1023+-- main = 'drain' $ do
1018-- x <- S.'fromFoldable' [1,2]
1024-- x <- S.'fromFoldable' [1,2]
1019-- y <- S.'fromFoldable' [3,4]
1025-- y <- S.'fromFoldable' [3,4]
1020-- S.'yieldM' $ putStrLn $ show (x, y)
1026-- S.'yieldM' $ putStrLn $ show (x, y)
@@ -1084,7 +1090,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1084-- import "Streamly"
1090-- import "Streamly"
1085-- import "Streamly.Prelude"
1091-- import "Streamly.Prelude"
1086--
1092--
1087−-- main = 'runStream' . 'asyncly' $ do
1093+-- main = 'drain' . 'asyncly' $ do
1088-- x <- S.'fromFoldable' [3,2,1]
1094-- x <- S.'fromFoldable' [3,2,1]
1089-- delay x
1095-- delay x
1090-- @
1096-- @
@@ -1111,7 +1117,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1111-- import "Streamly"
1117-- import "Streamly"
1112-- import qualified "Streamly.Prelude" as S
1118-- import qualified "Streamly.Prelude" as S
1113--
1119--
1114−-- main = 'runStream' . 'asyncly' $ do
1120+-- main = 'drain' . 'asyncly' $ do
1115-- x <- S.'fromFoldable' [1,2]
1121-- x <- S.'fromFoldable' [1,2]
1116-- y <- S.'fromFoldable' [3,4]
1122-- y <- S.'fromFoldable' [3,4]
1117-- S.'yieldM' $ putStrLn $ show (x, y)
1123-- S.'yieldM' $ putStrLn $ show (x, y)
@@ -1136,7 +1142,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1136-- import "Streamly"
1142-- import "Streamly"
1137-- import qualified "Streamly.Prelude" as S
1143-- import qualified "Streamly.Prelude" as S
1138--
1144--
1139−-- main = 'runStream' . 'wSerially' $ do
1145+-- main = 'drain' . 'wSerially' $ do
1140-- x <- S.'fromFoldable' [1,2]
1146-- x <- S.'fromFoldable' [1,2]
1141-- y <- S.'fromFoldable' [3,4]
1147-- y <- S.'fromFoldable' [3,4]
1142-- S.yieldM $ putStrLn $ show (x, y)
1148-- S.yieldM $ putStrLn $ show (x, y)
@@ -1164,7 +1170,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1164-- import "Streamly"
1170-- import "Streamly"
1165-- import qualified "Streamly.Prelude" as S
1171-- import qualified "Streamly.Prelude" as S
1166--
1172--
1167−-- main = 'runStream' . 'wAsyncly' $ do
1173+-- main = 'drain' . 'wAsyncly' $ do
1168-- x <- S.'fromFoldable' [1,2]
1174-- x <- S.'fromFoldable' [1,2]
1169-- y <- S.'fromFoldable' [3,4]
1175-- y <- S.'fromFoldable' [3,4]
1170-- S.'yieldM' $ putStrLn $ show (x, y)
1176-- S.'yieldM' $ putStrLn $ show (x, y)
@@ -1190,7 +1196,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1190-- import "Streamly"
1196-- import "Streamly"
1191-- import qualified "Streamly.Prelude" as S
1197-- import qualified "Streamly.Prelude" as S
1192--
1198--
1193−-- main = 'runStream' . 'parallely' $ do
1199+-- main = 'drain' . 'parallely' $ do
1194-- x <- S.'fromFoldable' [3,2,1]
1200-- x <- S.'fromFoldable' [3,2,1]
1195-- delay x
1201-- delay x
1196-- @
1202-- @
@@ -1228,11 +1234,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1228-- Now we can interpret this in whatever way we want:
1234-- Now we can interpret this in whatever way we want:
1229--
1235--
1230-- @
1236-- @
1231−-- main = 'runStream' . 'serially' $ composed
1237+-- main = 'drain' . 'serially' $ composed
1232−-- main = 'runStream' . 'wSerially' $ composed
1238+-- main = 'drain' . 'wSerially' $ composed
1233−-- main = 'runStream' . 'asyncly' $ composed
1239+-- main = 'drain' . 'asyncly' $ composed
1234−-- main = 'runStream' . 'wAsyncly' $ composed
1240+-- main = 'drain' . 'wAsyncly' $ composed
1235−-- main = 'runStream' . 'parallely' $ composed
1241+-- main = 'drain' . 'parallely' $ composed
1236-- @
1242-- @
1237--
1243--
1238-- As an exercise try to figure out the output of this code for each mode of
1244-- As an exercise try to figure out the output of this code for each mode of
@@ -1513,7 +1519,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1513-- main = do
1519-- main = do
1514-- putStrLn "Your health is deteriorating due to acid rain,\\
1520-- putStrLn "Your health is deteriorating due to acid rain,\\
1515-- \\ type \\"potion\\" or \\"quit\\""
1521-- \\ type \\"potion\\" or \\"quit\\""
1516−-- let runGame = S.'runWhile' (== Alive) $ S.'mapM' getStatus runEvents
1522+-- let runGame = S.'drainWhile' (== Alive) $ S.'mapM' getStatus runEvents
1517-- void $ runStateT runGame 60
1523-- void $ runStateT runGame 60
1518-- @
1524-- @
1519--
1525--
@@ -1713,13 +1719,14 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1713-- that and takes a lazy pull approach versus transient's strict push approach.
1719-- that and takes a lazy pull approach versus transient's strict push approach.
1714--
1720--
1715-- The non-determinism, concurrency and streaming combination make streamly a
1721-- The non-determinism, concurrency and streaming combination make streamly a
1722+-- strong reactive programming library as well. Reactive programming is
1716−-- strong FRP capable library as well. FRP is fundamentally stream of events
1723+-- fundamentally stream of events that can be processed concurrently. The
1717−-- that can be processed concurrently. The example in this tutorial as well as
1724+-- example in this tutorial as well as the
1718−-- the "Streamly.Examples.CirclingSquare" example from Yampa demonstrate the
1725+-- <examples/CirclingSquare.hs CirclingSquare> example from Yampa demonstrate
1719−-- basic FRP capability of streamly. In core concepts streamly is strikingly
1726+-- the basic reactive capability of streamly. In core concepts streamly is
1720−-- similar to @dunai@. dunai was designed from a FRP perspective and streamly
1727+-- strikingly similar to @dunai@. dunai was designed from a FRP perspective
1721−-- was originally designed from a concurrency perspective. However, both have
1728+-- and streamly was originally designed from a concurrency perspective.
1722−-- similarity at the core.
1729+-- However, both have similarity at the core.
1723
1730
1724-- $furtherReading
1731-- $furtherReading
1725--
1732--
streamly.cabal+8
@@ -172,11 +172,18 @@ library
172 , Streamly.String
172 , Streamly.String
173 , Streamly.Enumeration
173 , Streamly.Enumeration
174 , Streamly.Array.Types
174 , Streamly.Array.Types
175+ , Streamly.RingBuffer
176+ , Streamly.Strict
177+ , Streamly.Fold.Types
178+ , Streamly.Sink.Types
179+ , Streamly.Sink
175
180
176 exposed-modules: Streamly.Prelude
181 exposed-modules: Streamly.Prelude
177 , Streamly.Array
182 , Streamly.Array
178 , Streamly.Time
183 , Streamly.Time
179 , Streamly
184 , Streamly
185+ , Streamly.Fold
186+
180 , Streamly.Tutorial
187 , Streamly.Tutorial
181 , Streamly.Internal
188 , Streamly.Internal
182
189
@@ -220,6 +227,7 @@ library
220 , mtl >= 2.2 && < 3
227 , mtl >= 2.2 && < 3
221 , transformers >= 0.4 && < 0.6
228 , transformers >= 0.4 && < 0.6
222 , transformers-base >= 0.4 && < 0.5
229 , transformers-base >= 0.4 && < 0.5
230+ , random
223
231
224 if impl(ghc < 8.0)
232 if impl(ghc < 8.0)
225 build-depends:
233 build-depends:
test/Main.hs+15−15
@@ -153,7 +153,7 @@ checkCleanup :: IsStream t
153 -> IO ()
153 -> IO ()
154checkCleanup d t op = do
154checkCleanup d t op = do
155 r <- newIORef (-1 :: Int)
155 r <- newIORef (-1 :: Int)
156− S.runStream . serially $ do
156+ S.drain . serially $ do
157 _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
157 _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
158 return ()
158 return ()
159 performMajorGC
159 performMajorGC
@@ -193,9 +193,9 @@ parallelTests = H.parallel $ do
193 -- XXX move these to property tests
193 -- XXX move these to property tests
194 -- XXX use an IORef to store and check the side effects
194 -- XXX use an IORef to store and check the side effects
195 it "simple serially" $
195 it "simple serially" $
196− (S.runStream . serially) (return (0 :: Int)) `shouldReturn` ()
196+ (S.drain . serially) (return (0 :: Int)) `shouldReturn` ()
197 it "simple serially with IO" $
197 it "simple serially with IO" $
198− (S.runStream . serially) (S.yieldM $ putStrLn "hello") `shouldReturn` ()
198+ (S.drain . serially) (S.yieldM $ putStrLn "hello") `shouldReturn` ()
199
199
200 describe "Empty" $ -- do
200 describe "Empty" $ -- do
201 it "Monoid - mempty" $
201 it "Monoid - mempty" $
@@ -543,12 +543,12 @@ parallelTests = H.parallel $ do
543 ---------------------------------------------------------------------------
543 ---------------------------------------------------------------------------
544
544
545 it "asyncly crosses thread limit (2000 threads)" $
545 it "asyncly crosses thread limit (2000 threads)" $
546− S.runStream (asyncly $ fold $
546+ S.drain (asyncly $ fold $
547 replicate 2000 $ S.yieldM $ threadDelay 1000000)
547 replicate 2000 $ S.yieldM $ threadDelay 1000000)
548 `shouldReturn` ()
548 `shouldReturn` ()
549
549
550 it "aheadly crosses thread limit (4000 threads)" $
550 it "aheadly crosses thread limit (4000 threads)" $
551− S.runStream (aheadly $ fold $
551+ S.drain (aheadly $ fold $
552 replicate 4000 $ S.yieldM $ threadDelay 1000000)
552 replicate 4000 $ S.yieldM $ threadDelay 1000000)
553 `shouldReturn` ()
553 `shouldReturn` ()
554
554
@@ -598,7 +598,7 @@ monadicStateSnapshot
598 , MonadState Int (t (StateT Int IO))
598 , MonadState Int (t (StateT Int IO))
599 )
599 )
600 => (t (StateT Int IO) () -> SerialT (StateT Int IO) ()) -> IO ()
600 => (t (StateT Int IO) () -> SerialT (StateT Int IO) ()) -> IO ()
601−monadicStateSnapshot t = void $ runStateT (S.runStream $ t stateComp) 0
601+monadicStateSnapshot t = void $ runStateT (S.drain $ t stateComp) 0
602
602
603stateCompOp
603stateCompOp
604 :: ( AsyncT (StateT Int IO) ()
604 :: ( AsyncT (StateT Int IO) ()
@@ -621,7 +621,7 @@ monadicStateSnapshotOp
621 -> AsyncT (StateT Int IO) ()
621 -> AsyncT (StateT Int IO) ()
622 )
622 )
623 -> IO ()
623 -> IO ()
624−monadicStateSnapshotOp op = void $ runStateT (S.runStream $ stateCompOp op) 0
624+monadicStateSnapshotOp op = void $ runStateT (S.drain $ stateCompOp op) 0
625
625
626takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
626takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
627 => Int -> (t m Int -> SerialT IO a) -> IO ()
627 => Int -> (t m Int -> SerialT IO a) -> IO ()
@@ -674,7 +674,7 @@ checkScanxStrictness :: IO ()
674checkScanxStrictness = do
674checkScanxStrictness = do
675 let s = return (1 :: Int) `S.consM` error "failure"
675 let s = return (1 :: Int) `S.consM` error "failure"
676 catch
676 catch
677− (S.runStream (
677+ (S.drain (
678 S.scanx (\_ a ->
678 S.scanx (\_ a ->
679 if a == 1
679 if a == 1
680 then error "success"
680 then error "success"
@@ -693,7 +693,7 @@ checkScanl'Strictness :: IO ()
693checkScanl'Strictness = do
693checkScanl'Strictness = do
694 let s = return (1 :: Int) `S.consM` error "failure"
694 let s = return (1 :: Int) `S.consM` error "failure"
695 catch
695 catch
696− (S.runStream
696+ (S.drain
697 (S.scanl'
697 (S.scanl'
698 (\_ a ->
698 (\_ a ->
699 if a == 1
699 if a == 1
@@ -730,13 +730,13 @@ checkScanlMStrictness :: (IORef Int -> SerialT IO Int -> SerialT IO ()) -> IO ()
730checkScanlMStrictness f = do
730checkScanlMStrictness f = do
731 ref <- newIORef 0
731 ref <- newIORef 0
732 let s = return 1 `S.consM` error "x"
732 let s = return 1 `S.consM` error "x"
733− catch (S.runStream $ f ref s) (\(_ :: ErrorCall) -> return ())
733+ catch (S.drain $ f ref s) (\(_ :: ErrorCall) -> return ())
734 readIORef ref `shouldReturn` 1
734 readIORef ref `shouldReturn` 1
735
735
736takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
736takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
737takeInfinite t =
737takeInfinite t =
738 it "take 1" $
738 it "take 1" $
739− S.runStream (t $ S.take 1 $ S.repeatM (print "hello" >> return (1::Int)))
739+ S.drain (t $ S.take 1 $ S.repeatM (print "hello" >> return (1::Int)))
740 `shouldReturn` ()
740 `shouldReturn` ()
741
741
742-- XXX need to test that we have promptly cleaned up everything after the error
742-- XXX need to test that we have promptly cleaned up everything after the error
@@ -751,16 +751,16 @@ simpleMonadError :: Spec
751simpleMonadError = do
751simpleMonadError = do
752{-
752{-
753 it "simple runExceptT" $ do
753 it "simple runExceptT" $ do
754− (runExceptT $ S.runStream $ return ())
754+ (runExceptT $ S.drain $ return ())
755 `shouldReturn` (Right () :: Either String ())
755 `shouldReturn` (Right () :: Either String ())
756 it "simple runExceptT with error" $ do
756 it "simple runExceptT with error" $ do
757− (runExceptT $ S.runStream $ throwError "E") `shouldReturn` Left "E"
757+ (runExceptT $ S.drain $ throwError "E") `shouldReturn` Left "E"
758 -}
758 -}
759 it "simple try" $
759 it "simple try" $
760− try (S.runStream $ return ())
760+ try (S.drain $ return ())
761 `shouldReturn` (Right () :: Either ExampleException ())
761 `shouldReturn` (Right () :: Either ExampleException ())
762 it "simple try with throw error" $
762 it "simple try with throw error" $
763− try (S.runStream $ throwM $ ExampleException "E")
763+ try (S.drain $ throwM $ ExampleException "E")
764 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
764 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
765
765
766composeWithMonadThrow
766composeWithMonadThrow
test/Prop.hs+69−4
@@ -1,4 +1,6 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE OverloadedLists #-}
3+{-# LANGUAGE TypeFamilies #-}
2
4
3module Main (main) where
5module Main (main) where
4
6
@@ -15,13 +17,14 @@ import Data.List
15 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
17 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
16 elemIndex, find, insertBy, intersperse, foldl1', (\\),
18 elemIndex, find, insertBy, intersperse, foldl1', (\\),
17 maximumBy, minimumBy, deleteBy, isPrefixOf, isSubsequenceOf,
19 maximumBy, minimumBy, deleteBy, isPrefixOf, isSubsequenceOf,
18− stripPrefix)
20+ stripPrefix, intercalate)
19import Data.Maybe (mapMaybe)
21import Data.Maybe (mapMaybe)
20import GHC.Word (Word8)
22import GHC.Word (Word8)
21
23
22import Test.Hspec.QuickCheck
24import Test.Hspec.QuickCheck
23import Test.QuickCheck
25import Test.QuickCheck
24− (counterexample, Property, withMaxSuccess, forAll, choose)
26+ (counterexample, Property, withMaxSuccess, forAll, choose, Gen,
27+ arbitrary, elements, frequency, listOf, listOf1, vectorOf, suchThat)
25import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
28import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
26
29
27import Test.Hspec as H
30import Test.Hspec as H
@@ -29,6 +32,7 @@ import Test.Hspec as H
29import Streamly
32import Streamly
30import Streamly.Prelude ((.:), nil)
33import Streamly.Prelude ((.:), nil)
31import qualified Streamly.Prelude as S
34import qualified Streamly.Prelude as S
35+import qualified Streamly.Fold as FL
32
36
33-- Coverage build takes too long with default number of tests
37-- Coverage build takes too long with default number of tests
34maxTestCount :: Int
38maxTestCount :: Int
@@ -463,6 +467,62 @@ transformCombineOpsCommon constr desc eq t = do
463 transform (concatMap (const [1..n]))
467 transform (concatMap (const [1..n]))
464 t (S.concatMap (const (S.fromList [1..n])))
468 t (S.concatMap (const (S.fromList [1..n])))
465
469
470+groupSplitOps :: String -> Spec
471+groupSplitOps desc = do
472+ -- splitting
473+ -- XXX add tests with multichar separators too
474+
475+ prop (desc <> " intercalate . splitOn == id (nil separator)") $
476+ forAll listWithZeroes $ \xs -> do
477+ withMaxSuccess maxTestCount $
478+ monadicIO $ do
479+ ys <- S.toList $ FL.splitOn [] FL.toList (S.fromList xs)
480+ listEquals (==) (intercalate [] ys) xs
481+
482+ prop (desc <> " intercalate . splitOn == id (single element separator)") $
483+ forAll listWithZeroes $ \xs -> do
484+ withMaxSuccess maxTestCount $
485+ monadicIO $ do
486+ ys <- S.toList $ FL.splitOn [0] FL.toList (S.fromList xs)
487+ listEquals (==) (intercalate [0] ys) xs
488+
489+ prop (desc <> " concat . splitOn . intercalate == concat (nil separator/possibly empty list)") $
490+ forAll listsWithoutZeroes $ \xss -> do
491+ withMaxSuccess maxTestCount $
492+ monadicIO $ do
493+ let xs = intercalate [] xss
494+ ys <- S.toList $ FL.splitOn [0] FL.toList (S.fromList xs)
495+ listEquals (==) (concat ys) (concat xss)
496+
497+ prop (desc <> " concat . splitOn . intercalate == concat (non-nil separator/possibly empty list)") $
498+ forAll listsWithoutZeroes $ \xss -> do
499+ withMaxSuccess maxTestCount $
500+ monadicIO $ do
501+ let xs = intercalate [0] xss
502+ ys <- S.toList $ FL.splitOn [0] FL.toList (S.fromList xs)
503+ listEquals (==) (concat ys) (concat xss)
504+
505+ prop (desc <> " splitOn . intercalate == id (exclusive separator/non-empty list)") $
506+ forAll listsWithoutZeroes1 $ \xss -> do
507+ withMaxSuccess maxTestCount $
508+ monadicIO $ do
509+ let xs = intercalate [0] xss
510+ ys <- S.toList $ FL.splitOn [0] FL.toList (S.fromList xs)
511+ listEquals (==) ys xss
512+
513+ where
514+
515+ listWithZeroes :: Gen [Int]
516+ listWithZeroes = listOf $ frequency [(3, arbitrary), (1, elements [0])]
517+
518+ listWithoutZeroes = vectorOf 4 $ suchThat arbitrary (/= 0)
519+
520+ listsWithoutZeroes :: Gen [[Int]]
521+ listsWithoutZeroes = listOf listWithoutZeroes
522+
523+ listsWithoutZeroes1 :: Gen [[Int]]
524+ listsWithoutZeroes1 = listOf1 listWithoutZeroes
525+
466-- transformation tests that can only work reliably for ordered streams i.e.
526-- transformation tests that can only work reliably for ordered streams i.e.
467-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
527-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
468-- might yield a different result every time.
528-- might yield a different result every time.
@@ -832,7 +892,8 @@ main = hspec
832 _ -> S.foldMapWith (<>) return xs
892 _ -> S.foldMapWith (<>) return xs
833 )
893 )
834
894
835− let makeOps t =
895+ let makeOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
896+ makeOps t =
836 [ ("default", t)
897 [ ("default", t)
837#ifndef COVERAGE_BUILD
898#ifndef COVERAGE_BUILD
838 , ("rate AvgRate 10000", t . avgRate 10000)
899 , ("rate AvgRate 10000", t . avgRate 10000)
@@ -1013,7 +1074,8 @@ main = hspec
1013
1074
1014 -- These tests won't work with maxBuffer or maxThreads set to 1, so we
1075 -- These tests won't work with maxBuffer or maxThreads set to 1, so we
1015 -- exclude those cases from these.
1076 -- exclude those cases from these.
1016− let mkOps t =
1077+ let mkOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
1078+ mkOps t =
1017 [ ("default", t)
1079 [ ("default", t)
1018#ifndef COVERAGE_BUILD
1080#ifndef COVERAGE_BUILD
1019 , ("rate Nothing", t . rate Nothing)
1081 , ("rate Nothing", t . rate Nothing)
@@ -1075,6 +1137,9 @@ main = hspec
1075 zipSerialOps $ transformCombineOpsOrdered folded "zipSerially" (==)
1137 zipSerialOps $ transformCombineOpsOrdered folded "zipSerially" (==)
1076 zipAsyncOps $ transformCombineOpsOrdered folded "zipAsyncly" (==)
1138 zipAsyncOps $ transformCombineOpsOrdered folded "zipAsyncly" (==)
1077
1139
1140+ describe "Stream group and split operations" $ do
1141+ groupSplitOps "serially"
1142+
1078 describe "Stream elimination operations" $ do
1143 describe "Stream elimination operations" $ do
1079 serialOps $ eliminationOps S.fromFoldable "serially"
1144 serialOps $ eliminationOps S.fromFoldable "serially"
1080 wSerialOps $ eliminationOps S.fromFoldable "wSerially"
1145 wSerialOps $ eliminationOps S.fromFoldable "wSerially"
test/loops.hs+8−8
@@ -1,38 +1,38 @@
1−import Streamly hiding (runStream)
1+import Streamly
2import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
2import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
3−import Streamly.Prelude (nil, yieldM, runStream)
3+import Streamly.Prelude (nil, yieldM, drain)
4
4
5main :: IO ()
5main :: IO ()
6main = do
6main = do
7 hSetBuffering stdout LineBuffering
7 hSetBuffering stdout LineBuffering
8
8
9 putStrLn "\nloopTail:\n"
9 putStrLn "\nloopTail:\n"
10− runStream $ do
10+ drain $ do
11 x <- loopTail 0
11 x <- loopTail 0
12 yieldM $ print (x :: Int)
12 yieldM $ print (x :: Int)
13
13
14 putStrLn "\nloopHead:\n"
14 putStrLn "\nloopHead:\n"
15− runStream $ do
15+ drain $ do
16 x <- loopHead 0
16 x <- loopHead 0
17 yieldM $ print (x :: Int)
17 yieldM $ print (x :: Int)
18
18
19 putStrLn "\nloopTailA:\n"
19 putStrLn "\nloopTailA:\n"
20− runStream $ do
20+ drain $ do
21 x <- loopTailA 0
21 x <- loopTailA 0
22 yieldM $ print (x :: Int)
22 yieldM $ print (x :: Int)
23
23
24 putStrLn "\nloopHeadA:\n"
24 putStrLn "\nloopHeadA:\n"
25− runStream $ do
25+ drain $ do
26 x <- loopHeadA 0
26 x <- loopHeadA 0
27 yieldM $ print (x :: Int)
27 yieldM $ print (x :: Int)
28
28
29 putStrLn "\nwSerial:\n"
29 putStrLn "\nwSerial:\n"
30− runStream $ do
30+ drain $ do
31 x <- (return 0 <> return 1) `wSerial` (return 100 <> return 101)
31 x <- (return 0 <> return 1) `wSerial` (return 100 <> return 101)
32 yieldM $ print (x :: Int)
32 yieldM $ print (x :: Int)
33
33
34 putStrLn "\nParallel interleave:\n"
34 putStrLn "\nParallel interleave:\n"
35− runStream $ do
35+ drain $ do
36 x <- (return 0 <> return 1) `wAsync` (return 100 <> return 101)
36 x <- (return 0 <> return 1) `wAsync` (return 100 <> return 101)
37 yieldM $ print (x :: Int)
37 yieldM $ print (x :: Int)
38
38
test/nested-loops.hs+3−3
@@ -1,11 +1,11 @@
1import Control.Concurrent (myThreadId)
1import Control.Concurrent (myThreadId)
2import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
2import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
3import System.Random (randomIO)
3import System.Random (randomIO)
4−import Streamly hiding (runStream)
4+import Streamly
5−import Streamly.Prelude (runStream, nil, yieldM)
5+import Streamly.Prelude (drain, nil, yieldM)
6
6
7main :: IO ()
7main :: IO ()
8−main = runStream $ do
8+main = drain $ do
9 yieldM $ hSetBuffering stdout LineBuffering
9 yieldM $ hSetBuffering stdout LineBuffering
10 x <- loop "A " 2
10 x <- loop "A " 2
11 y <- loop "B " 2
11 y <- loop "B " 2
test/parallel-loops.hs+1−1
@@ -7,7 +7,7 @@ import qualified Streamly.Prelude as S
7main :: IO ()
7main :: IO ()
8main = do
8main = do
9 hSetBuffering stdout LineBuffering
9 hSetBuffering stdout LineBuffering
10− S.runStream $ do
10+ S.drain $ do
11 x <- S.take 10 $ loop "A" `parallel` loop "B"
11 x <- S.take 10 $ loop "A" `parallel` loop "B"
12 S.yieldM $ myThreadId >>= putStr . show
12 S.yieldM $ myThreadId >>= putStr . show
13 >> putStr " got "
13 >> putStr " got "