on master help sign in
Add "Streamly.Fold" module
commitfd7bd713ec7d20f5e32ea87f1ef3e6b614a133f6 authorHarendra Kumar <harendra.kumar@gmail.com> dateWed 8 May 2019, 02:18 (7 years ago) parent36c4c4c
39 files changed+4182−459
Changelog.md+10−4 README.md+16−15 benchmark/Adaptive.hs+2−2 benchmark/Array.hs+16−7 benchmark/ArrayOps.hs+12−2 benchmark/Linear.hs+51−2 benchmark/LinearOps.hs+7−3 benchmark/NestedOps.hs+1−1 examples/AcidRain.hs+1−1 examples/CirclingSquare.hs+1−1 examples/ControlFlow.hs+8−9 examples/ListDir.hs+3−2 examples/SearchQuery.hs+4−4 src/Streamly.hs+138−60 src/Streamly/Array.hs+87−38 src/Streamly/Array/Types.hs+157−7 src/Streamly/Fold.hs+1781 src/Streamly/Fold/Types.hs+172 src/Streamly/Prelude.hs+71−42 src/Streamly/RingBuffer.hs+183 src/Streamly/Sink.hs+286 src/Streamly/Sink/Types.hs+24 src/Streamly/Streams/Ahead.hs+1−1 src/Streamly/Streams/Async.hs+2−2 src/Streamly/Streams/Parallel.hs+3−3 src/Streamly/Streams/Prelude.hs+6−24 src/Streamly/Streams/Serial.hs+3−3 src/Streamly/Streams/StreamD.hs+760−114 src/Streamly/Streams/StreamD/Type.hs+127 src/Streamly/Streams/StreamK.hs+6−6 src/Streamly/Streams/StreamK/Type.hs+2−2 src/Streamly/Strict.hs+57 src/Streamly/Tutorial.hs+80−73 streamly.cabal+8 test/Main.hs+15−15 test/Prop.hs+69−4 test/loops.hs+8−8 test/nested-loops.hs+3−3 test/parallel-loops.hs+1−1
Changelog.md+10−4
@@ -7,8 +7,10 @@
77
88### Enhancements
99
10−* Add Streamly.Array module to support efficient fixed size immutable arrays
11− for buffering and IO.
10+* MAJOR: Add Streamly.Array module to support efficient fixed size immutable
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.
1214* Add the following to "Streamly.Prelude":
1315 * `foldrS` to fold a stream to a stream
1416 * `foldrT` to fold a stream to an arbitrary transformer monad
@@ -16,10 +18,14 @@
1618
1719### Deprecations
1820
19−* `runStream`, `foldWith`, `foldMapWith`, `forEachWith` have been moved from
21+* `runStream` has been replaced by `Streamly.Prelude.drain`
22+* `runN` has been replaced by `drainN`
23+* `runWhile` has been replaced by `drainWhile`
24+
25+* `foldWith`, `foldMapWith`, `forEachWith` have been moved from
2026 "Streamly" module to "Streamly.Prelude" module.
2127
22−* "Streamly.Prelude":
28+* In "Streamly.Prelude":
2329 * Deprecate `scanx`, `foldx`, `foldxM`, `foldr1`
2430 * Remove deprecated APIs `scan`, `foldl`, `foldlM`
2531
README.md+16−15
@@ -93,10 +93,11 @@ lookahead style concurrency, the only difference is that this time multiple
9393directories are read concurrently:
9494
9595``` haskell
96−import Streamly (runStream, aheadly)
96+import Streamly (aheadly)
97+import qualified Streamly.Prelude as S
9798
9899main :: IO ()
99−main = runStream $ aheadly $ listDirRecursive
100+main = S.drain $ aheadly $ listDirRecursive
100101```
101102
102103Isn't that magical? What's going on here? Streamly does not introduce any new
@@ -134,7 +135,7 @@ import Streamly
134135import qualified Streamly.Prelude as S
135136import Data.Function ((&))
136137
137−main = runStream $
138+main = S.drain $
138139 S.repeatM getLine
139140 & fmap read
140141 & S.filter even
@@ -172,7 +173,7 @@ The following code finishes in 3 seconds (6 seconds when serial):
172173The following finishes in 10 seconds (100 seconds when serial):
173174
174175``` haskell
175−runStream $ asyncly $ S.replicateM 10 $ p 10
176+S.drain $ asyncly $ S.replicateM 10 $ p 10
176177```
177178
178179## Concurrent Streaming Pipelines
@@ -183,7 +184,7 @@ following example prints a "hello" every second; if you use `&` instead of
183184application.
184185
185186``` haskell
186−main = runStream $
187+main = S.drain $
187188 S.repeatM (threadDelay 1000000 >> return "hello")
188189 |& S.mapM (\x -> threadDelay 1000000 >> putStrLn x)
189190```
@@ -194,7 +195,7 @@ We can use `mapM` or `sequence` functions concurrently on a stream.
194195
195196``` haskell
196197> 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))
198199```
199200
200201## Serial and Concurrent Merging
@@ -230,7 +231,7 @@ delay n = S.yieldM $ do
230231### Serial
231232
232233``` haskell
233−main = runStream $ delay 3 <> delay 2 <> delay 1
234+main = S.drain $ delay 3 <> delay 2 <> delay 1
234235```
235236```
236237ThreadId 36: Delay 3
@@ -241,7 +242,7 @@ ThreadId 36: Delay 1
241242### Parallel
242243
243244``` haskell
244−main = runStream . parallely $ delay 3 <> delay 2 <> delay 1
245+main = S.drain . parallely $ delay 3 <> delay 2 <> delay 1
245246```
246247```
247248ThreadId 42: Delay 1
@@ -262,7 +263,7 @@ loops = do
262263 y <- S.fromFoldable [3,4]
263264 S.yieldM $ putStrLn $ show (x, y)
264265
265−main = runStream loops
266+main = S.drain loops
266267```
267268```
268269(1,3)
@@ -278,21 +279,21 @@ the loop can run run concurrently by but the results are presented in the same
278279order as serial execution:
279280
280281``` haskell
281−main = runStream $ aheadly $ loops
282+main = S.drain $ aheadly $ loops
282283```
283284
284285To run it with depth first concurrency yielding results asynchronously in the
285286same order as they become available (deep async composition):
286287
287288``` haskell
288−main = runStream $ asyncly $ loops
289+main = S.drain $ asyncly $ loops
289290```
290291
291292To run it with breadth first concurrency and yeilding results asynchronously
292293(wide async composition):
293294
294295``` haskell
295−main = runStream $ wAsyncly $ loops
296+main = S.drain $ wAsyncly $ loops
296297```
297298
298299The above streams provide lazy/demand-driven concurrency which is automatically
@@ -301,14 +302,14 @@ infinite streams. The following combinator provides strict, unbounded
301302concurrency irrespective of demand:
302303
303304``` haskell
304−main = runStream $ parallely $ loops
305+main = S.drain $ parallely $ loops
305306```
306307
307308To run it serially but interleaving the outer and inner loop iterations
308309(breadth first serial):
309310
310311``` haskell
311−main = runStream $ wSerially $ loops
312+main = S.drain $ wSerially $ loops
312313```
313314
314315## Magical Concurrency
@@ -346,7 +347,7 @@ example, to print hello once every second you can simply write this:
346347import Streamly
347348import Streamly.Prelude as S
348349
349−main = runStream $ asyncly $ avgRate 1 $ S.repeatM $ putStrLn "hello"
350+main = S.drain $ asyncly $ avgRate 1 $ S.repeatM $ putStrLn "hello"
350351```
351352
352353For some practical uses of rate control, see
benchmark/Adaptive.hs+2−2
@@ -9,7 +9,7 @@ import Control.Concurrent (threadDelay)
99import Control.Monad (when)
1010import Control.Monad.IO.Class (liftIO)
1111import Gauge
12−import Streamly hiding (runStream)
12+import Streamly
1313import Streamly.Prelude as S
1414import System.Random (randomRIO)
1515
@@ -32,7 +32,7 @@ source range = S.replicateM value $ do
3232
3333{-# INLINE run #-}
3434run :: 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
3636 n <- t $ source srange
3737 d <- liftIO (randomRIO crange)
3838 when (d /= 0) $ liftIO $ threadDelay d
benchmark/Array.hs+16−7
@@ -7,12 +7,15 @@
77-- Maintainer : harendra.kumar@gmail.com
88
99import Control.DeepSeq (NFData(..), deepseq)
10+import Data.Functor.Identity (Identity)
1011import Foreign.Storable (Storable(..))
1112import System.Random (randomRIO)
1213
14+import Streamly
1315import qualified GHC.Exts as GHC
1416
1517import qualified ArrayOps as Ops
18+import qualified Streamly.Array as A
1619
1720import Gauge
1821
@@ -42,13 +45,15 @@ main =
4245 defaultMain
4346 [ bgroup "array"
4447 [ bgroup "generation"
45− [ benchPureSrc "intFromTo" Ops.sourceIntFromTo
46− , benchPureSrc "unfoldr" Ops.sourceUnfoldr
47− , benchPureSrc "fromList" Ops.sourceFromList
48− , benchPureSrc "IsList.fromList" Ops.sourceIsList
49− , benchPureSrc "IsString.fromString" Ops.sourceIsString
50− , mkString `deepseq` (bench "readsPrec" $ nf Ops.readInstance mkString)
51− , benchPureSink "showsPrec pure streams" Ops.showInstance
48+ [ benchPureSrc "fromStreamN . intFromTo" Ops.sourceIntFromTo
49+ , benchPureSrc "fromStream . intFromTo" Ops.sourceIntFromToFromStream
50+ , benchPureSrc "fromList . intFromTo" Ops.sourceIntFromToFromList
51+ , benchPureSrc "fromStreamN . unfoldr" Ops.sourceUnfoldr
52+ , benchPureSrc "fromStreamN . fromList" Ops.sourceFromList
53+ , benchPureSrc "fromStreamN . IsList.fromList" Ops.sourceIsList
54+ , benchPureSrc "fromStreamN . IsString.fromString" Ops.sourceIsString
55+ , mkString `deepseq` (bench "read" $ nf Ops.readInstance mkString)
56+ , benchPureSink "show" Ops.showInstance
5257 ]
5358 , bgroup "elimination"
5459 [ benchPureSink "id" id
@@ -62,6 +67,10 @@ main =
6267 , benchPureSink "min" Ops.ordInstanceMin
6368 , benchPureSink "IsList.toList" GHC.toList
6469 , 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)
6574#ifdef DEVBUILD
6675 , benchPureSink "foldable/foldl'" Ops.foldableFoldl'
6776 , benchPureSink "foldable/sum" Ops.foldableSum
benchmark/ArrayOps.hs+12−2
@@ -58,7 +58,16 @@ sourceUnfoldr n = runIdentity $ A.fromStreamN value $ S.unfoldr step n
5858
5959{-# INLINE sourceIntFromTo #-}
6060sourceIntFromTo :: Int -> Stream Int
61−sourceIntFromTo n = runIdentity $ A.fromStreamN value $ S.enumerateFromTo n (n + value)
61+sourceIntFromTo n = runIdentity $
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]
6271
6372{-# INLINE sourceFromList #-}
6473sourceFromList :: Int -> Stream Int
@@ -70,7 +79,8 @@ sourceIsList n = runIdentity $ A.fromStreamN value $ GHC.fromList [n..n+value]
7079
7180{-# INLINE sourceIsString #-}
7281sourceIsString :: Int -> Stream P.Char
73−sourceIsString n = runIdentity $ A.fromStreamN value $ GHC.fromString (P.replicate (n + value) 'a')
82+sourceIsString n = runIdentity $
83+ A.fromStreamN value $ GHC.fromString (P.replicate (n + value) 'a')
7484
7585{-
7686-------------------------------------------------------------------------------
benchmark/Linear.hs+51−2
@@ -9,12 +9,16 @@
99import Control.DeepSeq (NFData(..), deepseq)
1010import Data.Functor.Identity (Identity, runIdentity)
1111import System.Random (randomRIO)
12+import Data.Monoid (Last(..))
1213
1314import qualified GHC.Exts as GHC
1415import qualified LinearOps as Ops
1516
1617import Streamly
18+import qualified Streamly.Array as A
19+import qualified Streamly.Fold as FL
1720import qualified Streamly.Prelude as S
21+-- import qualified Streamly.Sink as Sink
1822import Gauge
1923
2024-------------------------------------------------------------------------------
@@ -66,7 +70,7 @@ benchPureSinkIO name f =
6670
6771{-# INLINE benchPureSrc #-}
6872benchPureSrc :: 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)
7074
7175mkString :: String
7276mkString = "fromList [1" ++ concat (replicate Ops.value ",1") ++ "]"
@@ -186,7 +190,51 @@ main =
186190 , benchIOSink "minimum" Ops.minimum
187191
188192 , 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))
190238 ]
191239 , bgroup "transformation"
192240 [ benchIOSink "scanl" (Ops.scan 1)
@@ -201,6 +249,7 @@ main =
201249 , benchIOSink "findIndices" (Ops.findIndices 1)
202250 , benchIOSink "elemIndices" (Ops.elemIndices 1)
203251 , benchIOSink "reverse" (Ops.reverse 1)
252+ , benchIOSink "reverse'" (Ops.reverse' 1)
204253 , benchIOSink "foldrS" (Ops.foldrS 1)
205254 , benchIOSink "foldrSMap" (Ops.foldrSMap 1)
206255 , benchIOSink "foldrT" (Ops.foldrT 1)
benchmark/LinearOps.hs+7−3
@@ -160,7 +160,7 @@ sourceIsString n = GHC.fromString (P.replicate (n + value) 'a')
160160
161161{-# INLINE runStream #-}
162162runStream :: Monad m => Stream m a -> m ()
163−runStream = S.runStream
163+runStream = S.drain
164164
165165{-# INLINE toList #-}
166166toList :: Monad m => Stream m Int -> m [Int]
@@ -221,7 +221,7 @@ uncons s = do
221221
222222{-# INLINE init #-}
223223init :: 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
225225
226226{-# INLINE tail #-}
227227tail :: Monad m => Stream m a -> m ()
@@ -241,9 +241,11 @@ mapM_ = S.mapM_ (\_ -> return ())
241241
242242toList = S.toList
243243
244+{-
244245{-# INLINE toRevList #-}
245246toRevList :: Monad m => Stream m Int -> m [Int]
246247toRevList = S.toRevList
248+-}
247249
248250foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
249251foldl'Build = S.foldl' (flip (:)) []
@@ -330,6 +332,7 @@ composeN' n f =
330332{-# INLINE insertBy #-}
331333{-# INLINE deleteBy #-}
332334{-# INLINE reverse #-}
335+{-# INLINE reverse' #-}
333336{-# INLINE foldrS #-}
334337{-# INLINE foldrSMap #-}
335338{-# INLINE foldrT #-}
@@ -337,7 +340,7 @@ composeN' n f =
337340scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
338341 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
339342 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse,
340− findIndices, elemIndices, insertBy, deleteBy, reverse,
343+ findIndices, elemIndices, insertBy, deleteBy, reverse, reverse',
341344 foldrS, foldrSMap, foldrT, foldrTMap
342345 :: Monad m
343346 => Int -> Stream m Int -> m ()
@@ -387,6 +390,7 @@ elemIndices n = composeN n $ S.elemIndices maxValue
387390insertBy n = composeN n $ S.insertBy compare maxValue
388391deleteBy n = composeN n $ S.deleteBy (>=) maxValue
389392reverse n = composeN n $ S.reverse
393+reverse' n = composeN n $ S.reverse'
390394foldrS n = composeN n $ S.foldrS S.cons S.nil
391395foldrSMap n = composeN n $ S.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
392396foldrT 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
5252
5353{-# INLINE runStream #-}
5454runStream :: Monad m => Stream m a -> m ()
55−runStream = S.runStream
55+runStream = S.drain
5656
5757{-# INLINE runToList #-}
5858runToList :: Monad m => Stream m a -> m [a]
examples/AcidRain.hs+1−1
@@ -54,5 +54,5 @@ main :: IO ()
5454main = do
5555 putStrLn "Your health is deteriorating due to acid rain,\
5656 \ type \"potion\" or \"quit\""
57− let runGame = S.runWhile (== Alive) $ S.mapM getStatus runEvents
57+ let runGame = S.drainWhile (== Alive) $ S.mapM getStatus runEvents
5858 void $ runStateT runGame 60
examples/CirclingSquare.hs+1−1
@@ -78,6 +78,6 @@ main :: IO ()
7878main = do
7979 sdlInit
8080 cref <- newIORef (0,0)
81− S.runStream $ asyncly $ constRate 40
81+ S.drain $ asyncly $ constRate 40
8282 $ S.repeatM (updateController cref)
8383 `parallel` S.repeatM (updateDisplay cref)
examples/ControlFlow.hs+8−9
@@ -26,7 +26,6 @@ import Control.Monad.Trans.Maybe
2626import Control.Monad.Trans.Except
2727import Control.Monad.Trans.Cont
2828import Streamly
29−import Streamly.Prelude ((|:))
3029import qualified Streamly.Prelude as S
3130
3231-------------------------------------------------------------------------------
@@ -58,7 +57,7 @@ getSequenceMaybeBelow = do
5857
5958mainMaybeBelow :: IO ()
6059mainMaybeBelow = do
61− r <- runMaybeT (runStream getSequenceMaybeBelow)
60+ r <- runMaybeT (S.drain getSequenceMaybeBelow)
6261 case r of
6362 Just _ -> putStrLn "Bingo"
6463 Nothing -> putStrLn "Wrong"
@@ -122,7 +121,7 @@ getSequenceEitherBelow = do
122121mainEitherBelow :: IO ()
123122mainEitherBelow = do
124123 -- XXX Cannot lift catchE
125− r <- runExceptT (runStream getSequenceEitherBelow)
124+ r <- runExceptT (S.drain getSequenceEitherBelow)
126125 case r of
127126 Right _ -> liftIO $ putStrLn "Bingo"
128127 Left s -> liftIO $ putStrLn s
@@ -155,7 +154,7 @@ getSequenceEitherAsyncBelow = do
155154
156155mainEitherAsyncBelow :: IO ()
157156mainEitherAsyncBelow = do
158− r <- runExceptT (runStream $ asyncly getSequenceEitherAsyncBelow)
157+ r <- runExceptT (S.drain $ asyncly getSequenceEitherAsyncBelow)
159158 case r of
160159 Right _ -> liftIO $ putStrLn "Bingo"
161160 Left s -> liftIO $ putStrLn s
@@ -219,7 +218,7 @@ getSequenceMonadThrow = do
219218
220219mainMonadThrow :: IO ()
221220mainMonadThrow =
222− catch (runStream getSequenceMonadThrow >> liftIO (putStrLn "Bingo"))
221+ catch (S.drain getSequenceMonadThrow >> liftIO (putStrLn "Bingo"))
223222 (\(e :: SomeException) -> liftIO $ print e)
224223
225224-------------------------------------------------------------------------------
@@ -300,10 +299,10 @@ mainContAbove = do
300299main :: IO ()
301300main = do
302301 mainMaybeBelow
303− runStream $ runMaybeT mainMaybeAbove
304− runContT (runStream mainContBelow) return
305− runStream (runContT mainContAbove return)
302+ S.drain $ runMaybeT mainMaybeAbove
303+ runContT (S.drain mainContBelow) return
304+ S.drain (runContT mainContAbove return)
306305 mainEitherBelow
307− runStream (runExceptT mainEitherAbove)
306+ S.drain (runExceptT mainEitherAbove)
308307 mainMonadThrow
309308 mainEitherAsyncBelow
examples/ListDir.hs+3−2
@@ -1,7 +1,8 @@
11import Control.Monad.IO.Class (liftIO)
22import Path.IO (listDir, getCurrentDir)
33import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
4−import Streamly (runStream, aheadly, (<>))
4+import Streamly (aheadly, (<>))
5+import qualified Streamly.Prelude as S
56
67-- | List the current directory recursively using concurrent processing
78--
@@ -11,7 +12,7 @@ import Streamly (runStream, aheadly, (<>))
1112main :: IO ()
1213main = do
1314 hSetBuffering stdout LineBuffering
14− runStream . aheadly $ getCurrentDir >>= readdir
15+ S.drain . aheadly $ getCurrentDir >>= readdir
1516 where readdir d = do
1617 (ds, fs) <- listDir d
1718 liftIO $ mapM_ putStrLn $ fmap show fs <> fmap show ds
examples/SearchQuery.hs+4−4
@@ -1,5 +1,5 @@
11import Streamly
2−import Streamly.Prelude (nil, yieldM, (|:))
2+import Streamly.Prelude (drain, nil, yieldM, (|:))
33import Network.HTTP.Simple
44
55-- | Runs three search engine queries in parallel and prints the search engine
@@ -10,13 +10,13 @@ import Network.HTTP.Simple
1010main :: IO ()
1111main = do
1212 putStrLn "Using parallel stream construction"
13− runStream . parallely $ google |: bing |: duckduckgo |: nil
13+ drain . parallely $ google |: bing |: duckduckgo |: nil
1414
1515 putStrLn "\nUsing parallel semigroup composition"
16− runStream . parallely $ yieldM google <> yieldM bing <> yieldM duckduckgo
16+ drain . parallely $ yieldM google <> yieldM bing <> yieldM duckduckgo
1717
1818 putStrLn "\nUsing parallel applicative zip"
19− runStream . zipAsyncly $
19+ drain . zipAsyncly $
2020 (,,) <$> yieldM google <*> yieldM bing <*> yieldM duckduckgo
2121
2222 where
src/Streamly.hs+138−60
@@ -7,55 +7,25 @@
77-- Stability : experimental
88-- Portability : GHC
99--
10−-- The way a list represents a sequence of pure values, a stream represents a
11−-- sequence of monadic actions. The monadic stream API offered by Streamly is
12−-- very close to the Haskell "Prelude" pure lists' API, it can be considered as
13−-- a natural extension of lists to monadic actions. Streamly streams provide
14−-- concurrent composition and merging of streams. It can be considered as a
15−-- concurrent list transformer. In contrast to the "Prelude" lists, merging or
16−-- appending streams of arbitrary length is scalable and inexpensive.
17−--
18−-- The basic stream type is 'Serial', it represents a sequence of IO actions,
19−-- and is a 'Monad'. The type 'SerialT' is a monad transformer that can
20−-- represent a sequence of actions in an arbitrary monad. The type 'Serial' is
21−-- in fact a synonym for @SerialT IO@. There are a few more types similar to
22−-- 'SerialT', all of them represent a stream and differ only in the
23−-- 'Semigroup', 'Applicative' and 'Monad' compositions of the stream. 'Serial'
24−-- and 'WSerial' types compose serially whereas 'Async' and 'WAsync'
25−-- types compose concurrently. All these types can be freely inter-converted
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−-- @
10+-- Haskell lists provide a simple and powerful API but they are limited to
11+-- holding only pure values. Streamly streams are a logical extension of
12+-- Haskell lists to support monadic sequences and powerful declarative
13+-- concurrency. They provide almost the same API as lists (See
14+-- "Streamly.Prelude"). In fact, Haskell lists can be expressed as pure
15+-- streams, a special case of monadic streams, with similar or better
16+-- performance and almost drop-in replacement.
17+--
18+-- Streams are designed for high performance applications and do not exhibit
19+-- issues that are usually associated with lists. For example, streams
20+-- express strings as streams of 'Char' and bytestrings as streams of 'Word8'
21+-- without any issues, obviating the need for special purpose libraries like
22+-- bytestring and text, and various lazy and strict falvors of those. Streamly
23+-- arrays (See "Streamly.Array") complement streams for storing or buffering
24+-- data efficiently and facilitating efficient interfacing of streams with IO
25+-- systems.
5726
5827{-# LANGUAGE CPP #-}
28+{-# LANGUAGE MultiParamTypeClasses #-}
5929
6030#if __GLASGOW_HASKELL__ >= 800
6131{-# OPTIONS_GHC -Wno-orphans #-}
@@ -65,6 +35,13 @@
6535
6636module Streamly
6737 (
38+ -- * Streams and Folds
39+ -- $overview
40+
41+ -- * Streams Overview
42+ -- $streams
43+
44+ -- * Type Synonyms
6845 MonadAsync
6946
7047 -- * Stream transformers
@@ -73,11 +50,11 @@ module Streamly
7350 , SerialT
7451 , WSerialT
7552
76− -- ** Concurrent Lookahead Streams
53+ -- ** Speculative Streams
7754 -- $lookahead
7855 , AheadT
7956
80− -- ** Concurrent Asynchronous Streams
57+ -- ** Asynchronous Streams
8158 -- $async
8259 , AsyncT
8360 , WAsyncT
@@ -147,6 +124,7 @@ module Streamly
147124
148125 -- * Deprecated
149126 , Streaming
127+ , runStream
150128 , runStreaming
151129 , runStreamT
152130 , runInterleavedT
@@ -165,7 +143,6 @@ module Streamly
165143
166144 -- * Moved
167145 -- | These APIs have been moved to other modules
168− , runStream
169146 , foldWith
170147 , foldMapWith
171148 , forEachWith
@@ -179,12 +156,113 @@ import Streamly.Streams.Async
179156import Streamly.Streams.Combinators
180157import Streamly.Streams.Parallel
181158import Streamly.Streams.Serial
182−import Streamly.Streams.StreamK hiding (runStream, serial)
159+import Streamly.Streams.StreamK hiding (serial)
183160import Streamly.Streams.Zip
184161
185162import qualified Streamly.Prelude as P
186163import qualified Streamly.Streams.StreamK as K
187164
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+
188266------------------------------------------------------------------------------
189267-- Eliminating a stream
190268------------------------------------------------------------------------------
@@ -194,55 +272,55 @@ import qualified Streamly.Streams.StreamK as K
194272-- @since 0.1.0
195273{-# DEPRECATED runStreaming "Please use runStream instead." #-}
196274runStreaming :: (Monad m, IsStream t) => t m a -> m ()
197−runStreaming = P.runStream . K.adapt
275+runStreaming = P.drain . K.adapt
198276
199277-- | Same as @runStream@.
200278--
201279-- @since 0.1.0
202280{-# DEPRECATED runStreamT "Please use runStream instead." #-}
203281runStreamT :: Monad m => SerialT m a -> m ()
204−runStreamT = P.runStream
282+runStreamT = P.drain
205283
206284-- | Same as "Streamly.Prelude.runStream".
207285--
208−{-# DEPRECATED runStream "Please use Streamly.Prelude.runStream instead." #-}
286+{-# DEPRECATED runStream "Please use Streamly.Prelude.drain instead." #-}
209287runStream :: Monad m => SerialT m a -> m ()
210−runStream = P.runStream
288+runStream = P.drain
211289
212290-- | Same as @runStream . wSerially@.
213291--
214292-- @since 0.1.0
215293{-# DEPRECATED runInterleavedT "Please use 'runStream . interleaving' instead." #-}
216294runInterleavedT :: Monad m => WSerialT m a -> m ()
217−runInterleavedT = P.runStream . K.adapt
295+runInterleavedT = P.drain . K.adapt
218296
219297-- | Same as @runStream . parallely@.
220298--
221299-- @since 0.1.0
222300{-# DEPRECATED runParallelT "Please use 'runStream . parallely' instead." #-}
223301runParallelT :: Monad m => ParallelT m a -> m ()
224−runParallelT = P.runStream . K.adapt
302+runParallelT = P.drain . K.adapt
225303
226304-- | Same as @runStream . asyncly@.
227305--
228306-- @since 0.1.0
229307{-# DEPRECATED runAsyncT "Please use 'runStream . asyncly' instead." #-}
230308runAsyncT :: Monad m => AsyncT m a -> m ()
231−runAsyncT = P.runStream . K.adapt
309+runAsyncT = P.drain . K.adapt
232310
233311-- | Same as @runStream . zipping@.
234312--
235313-- @since 0.1.0
236314{-# DEPRECATED runZipStream "Please use 'runStream . zipSerially instead." #-}
237315runZipStream :: Monad m => ZipSerialM m a -> m ()
238−runZipStream = P.runStream . K.adapt
316+runZipStream = P.drain . K.adapt
239317
240318-- | Same as @runStream . zippingAsync@.
241319--
242320-- @since 0.1.0
243321{-# DEPRECATED runZipAsync "Please use 'runStream . zipAsyncly instead." #-}
244322runZipAsync :: Monad m => ZipAsyncM m a -> m ()
245−runZipAsync = P.runStream . K.adapt
323+runZipAsync = P.drain . K.adapt
246324
247325-- | Same as "Streamly.Prelude.foldWith".
248326--
src/Streamly/Array.hs+87−38
@@ -55,6 +55,17 @@
5555-- * provide /O(1)/ access to elements
5656-- * store only data and not functions
5757-- * 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
5869
5970-- Each array is one pointer visible to the GC. Too many small arrays (e.g.
6071-- single byte) are only as good as holding those elements in a Haskell list.
@@ -69,26 +80,44 @@ module Streamly.Array
6980 (
7081 Array
7182
83+ -- , defaultChunkSize
84+
7285 -- * 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+
7493 , fromStreamN
75− -- , fromList
76− -- , fromListN
94+ , fromStream
95+ , fromListN
96+ , fromList
97+
98+ -- Folds
99+ , toArrayN
100+ -- , toArrays
101+ -- , toArray
102+
103+ -- Streams
104+ , arraysOf
77105
78106 -- * Elimination
79− -- , toList
107+ -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
108+ -- list.
109+
80110 , toStream
111+ , toStreamRev
112+ , toList
113+ , flattenArrays
114+ -- , flattenArraysRev
115+ , spliceArrays
81116
82117 -- * Random Access
83118 , length
84119 , (!!)
85120
86− -- * Streams of Arrays
87− -- , defaultChunkSize
88− -- , fromStreamArraysOf
89− , flattenArrays
90− , spliceArrays
91−
92121 -- * IO: Single Array
93122 -- , fromHandleUpto
94123 -- , fromHandleN
@@ -117,10 +146,10 @@ import GHC.IO (unsafeDupablePerformIO)
117146
118147import Streamly.Streams.Serial (SerialT)
119148import 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
121151
122152import qualified Streamly.Streams.StreamD as D
123−import qualified Streamly.Streams.Prelude as P
124153import qualified Streamly.Prelude as S
125154
126155-------------------------------------------------------------------------------
@@ -135,7 +164,6 @@ import qualified Streamly.Prelude as S
135164{-# INLINE fromStreamN #-}
136165fromStreamN :: (Monad m, Storable a) => Int -> SerialT m a -> m (Array a)
137166fromStreamN n m = fromStreamDN n $ D.toStreamD m
138−-- fromStreamN n = FL.foldl (FL.toArrayN n)
139167
140168-------------------------------------------------------------------------------
141169-- Elimination
@@ -144,9 +172,22 @@ fromStreamN n m = fromStreamDN n $ D.toStreamD m
144172-- | Convert an 'Array' into a stream.
145173--
146174-- @since 0.7.0
147−{-# INLINABLE toStream #-}
175+{-# INLINE_EARLY toStream #-}
148176toStream :: (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 #-}
150191
151192{-# INLINE null #-}
152193null :: Storable a => Array a -> Bool
@@ -174,22 +215,35 @@ arr !! i =
174215-- Streams of Arrays
175216-------------------------------------------------------------------------------
176217
177−-- | Convert a stream of Arrays into a stream of their elements.
218+-- | Convert a stream of arrays into a stream of their elements.
178219--
179220-- @since 0.7.0
180221{-# INLINE flattenArrays #-}
181222flattenArrays :: (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)
183224
184−{-
185−-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
186−-- arrays of size n.
187−{-# INLINE fromStreamArraysOf #-}
188−fromStreamArraysOf :: (IsStream t, Monad m, Storable a)
225+-- XXX should we have a reverseArrays API to reverse the stream of arrays
226+-- instead?
227+--
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 #-}
243+arraysOf :: (IsStream t, Monad m, Storable a)
189244 => Int -> t m a -> t m (Array a)
190−fromStreamArraysOf n str =
191− D.fromStreamD $ D.groupsOf n (fromStreamN n) (D.toStreamD str)
192− -}
245+arraysOf n str =
246+ D.fromStreamD $ fromStreamDArraysOf n (D.toStreamD str)
193247
194248-- | Given a stream of arrays, splice them all together to generate a single
195249-- array. The stream must be /finite/.
@@ -214,23 +268,17 @@ spliceArrays s = do
214268 return $ dst `plusPtr` len
215269 in dst'
216270
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−--
222271-- | 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
224−-- when the size is already known.
272+-- single array from a stream of unknown size. 'fromStreamN' is at least twice
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.
225278--
226279{-# INLINE fromStream #-}
227280fromStream :: (Monad m, Storable a) => SerialT m a -> m (Array a)
228−fromStream m = do
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−-}
281+fromStream m = A.fromStreamD $ D.toStreamD m
234282
235283{-
236284-- Move this to FileIO
@@ -253,6 +301,7 @@ toHandleArrays h m = S.mapM_ (liftIO . toHandle h) m
253301fromHandleUpto :: Int -> Handle -> IO (Array Word8)
254302fromHandleUpto size h = do
255303 ptr <- mallocPlainForeignPtrBytes size
304+ -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
256305 withForeignPtr ptr $ \p -> do
257306 n <- hGetBufSome h p size
258307 let v = Array
src/Streamly/Array/Types.hs+157−7
@@ -29,11 +29,15 @@ module Streamly.Array.Types
2929 , unsafeAppend
3030 , shrinkToFit
3131 , memcpy
32+ , memcmp
3233
33− , fromCStringAddrUnsafe
3434 , fromList
3535 , fromListN
3636 , fromStreamDN
37+ , fromStreamD
38+ , fromStreamDArraysOf
39+ , flattenArrays
40+ , flattenArraysRev
3741
3842 -- * Elimination
3943 , unsafeIndex
@@ -42,10 +46,13 @@ module Streamly.Array.Types
4246 , foldr
4347
4448 , toStreamD
49+ , toStreamDRev
4550 , toList
51+ , toArrayN
4652
4753 -- * Utilities
4854 , defaultChunkSize
55+ , mkChunkSizeKB
4956 )
5057where
5158
@@ -70,8 +77,11 @@ import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes, newForeignPtr_)
7077import GHC.IO (IO(IO), unsafeDupablePerformIO)
7178import GHC.Ptr (Ptr(..))
7279
73−import qualified Prelude
80+import Streamly.Fold.Types (Fold(..))
81+import Streamly.SVar (adaptState)
82+
7483import qualified Streamly.Streams.StreamD.Type as D
84+import qualified Streamly.Streams.StreamK as K
7585import qualified GHC.Exts as Exts
7686
7787-------------------------------------------------------------------------------
@@ -118,7 +128,8 @@ import qualified GHC.Exts as Exts
118128-- is to store the elemSize in the array at construction and use that instead
119129-- of using sizeOf. Need to charaterize perf cost of this.
120130--
121−-- XXX add reverse flag to reverse the contents without physically reversing.
131+-- XXX rename the fields to "start, next, end".
132+--
122133data Array a =
123134#ifdef DEVBUILD
124135 Storable a =>
@@ -222,13 +233,18 @@ shrinkToFit arr@Array{..} = do
222233 }
223234 else return arr
224235
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+--
225241-- Note that the address must be a read-only address (meant to be used for
226242-- read-only string literals) because we are sharing it, any modification to
227243-- the original address would change our array. That's why this function is
228244-- unsafe.
229−{-# INLINE fromCStringAddrUnsafe #-}
230−fromCStringAddrUnsafe :: Addr# -> IO (Array Word8)
231−fromCStringAddrUnsafe addr# = do
245+{-# INLINE _fromCStringAddrUnsafe #-}
246+_fromCStringAddrUnsafe :: Addr# -> IO (Array Word8)
247+_fromCStringAddrUnsafe addr# = do
232248 ptr <- newForeignPtr_ (castPtr cstr)
233249 len <- c_strlen cstr
234250 let n = fromIntegral len
@@ -287,6 +303,23 @@ toStreamD Array{..} =
287303 return r
288304 return $ D.Yield x (p `plusPtr` (sizeOf (undefined :: a)))
289305
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+
290323{-# INLINE_NORMAL foldl' #-}
291324foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
292325foldl' 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
299332-- Instances
300333-------------------------------------------------------------------------------
301334
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+
302368{-# INLINE_NORMAL fromStreamDN #-}
303369fromStreamDN :: forall m a. (Monad m, Storable a)
304370 => Int -> D.Stream m a -> m (Array a)
@@ -313,6 +379,86 @@ fromStreamDN limit str = do
313379 let !_ = unsafeInlineIO $ poke ptr x
314380 in ptr `plusPtr` sizeOf (undefined :: a)
315381
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+
316462-- | Convert an 'Array' into a list.
317463--
318464-- @since 0.7.0
@@ -338,7 +484,7 @@ fromListN n xs = runIdentity $ fromStreamDN n $ D.fromList xs
338484-- @since 0.7.0
339485{-# INLINABLE fromList #-}
340486fromList :: Storable a => [a] -> Array a
341−fromList xs = fromListN (Prelude.length xs) xs
487+fromList xs = runIdentity $ fromStreamD $ D.fromList xs
342488
343489instance (Storable a, Read a, Show a) => Read (Array a) where
344490 {-# INLINE readPrec #-}
@@ -468,3 +614,7 @@ allocOverhead = 2 * sizeOf (undefined :: Int)
468614defaultChunkSize :: Int
469615defaultChunkSize = 32 * k - allocOverhead
470616 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
102102 -- these can be expressed in terms of primitives.
103103 , P.fromList
104104 , fromListM
105− -- , P.fromArray
106105 , K.fromFoldable
107106 , fromFoldableM
108107
@@ -183,16 +182,17 @@ module Streamly.Prelude
183182 , foldl1'
184183 , foldlM'
185184
186− -- ** Strict Full Folds
185+ -- ** Full Folds
187186 -- | Folds that are guaranteed to evaluate the whole stream.
188187
189188 -- -- ** To Summary (Full Folds)
190189 -- -- | Folds that summarize the stream to a single value.
191− , runStream
190+ , drain
192191 , last
193192 , length
194193 , sum
195194 , product
195+ --, mconcat
196196
197197 -- -- ** To Summary (Maybe) (Full Folds)
198198 -- -- | Folds that summarize a non-empty stream to a 'Just' value and return
@@ -202,7 +202,7 @@ module Streamly.Prelude
202202 , minimumBy
203203 , minimum
204204 , the
205− , toRevList
205+ -- , toListRev -- experimental
206206
207207 -- ** Lazy Folds
208208 --
@@ -213,7 +213,6 @@ module Streamly.Prelude
213213 -- -- | Convert or divert a stream into an output structure, container or
214214 -- sink.
215215 , toList
216− -- , toArrayN
217216 , toHandle
218217
219218 -- ** Partial Folds
@@ -221,8 +220,8 @@ module Streamly.Prelude
221220 -- folds strictly evaluate the stream until the result is determined.
222221
223222 -- -- ** To Elements (Partial Folds)
224− , runN
225− , runWhile
223+ , drainN
224+ , drainWhile
226225
227226 -- -- | Folds that extract selected elements of a stream or their properties.
228227 , (!!)
@@ -372,6 +371,7 @@ module Streamly.Prelude
372371
373372 -- ** Reordering
374373 , reverse
374+ , reverse'
375375
376376 -- * Multi-Stream Operations
377377 -- | New streams can be constructed by appending, merging or zipping
@@ -462,6 +462,9 @@ module Streamly.Prelude
462462 , foldx
463463 , foldxM
464464 , foldr1
465+ , runStream
466+ , runN
467+ , runWhile
465468 )
466469where
467470
@@ -493,7 +496,6 @@ import qualified Streamly.Streams.Prelude as P
493496import qualified Streamly.Streams.StreamK as K
494497import qualified Streamly.Streams.StreamD as D
495498import qualified Streamly.Streams.Zip as Z
496−import qualified Streamly.Array.Types as A
497499
498500#ifdef USE_STREAMK_ONLY
499501import qualified Streamly.Streams.StreamK as S
@@ -568,7 +570,7 @@ unfoldr step seed = fromStreamS (S.unfoldr step seed)
568570-- if b > 3
569571-- then return Nothing
570572-- else print b >> return (Just (b, b + 1))
571−-- in runStream $ unfoldrM f 0
573+-- in drain $ unfoldrM f 0
572574-- @
573575-- @
574576-- 0
@@ -687,8 +689,8 @@ fromIndicesM = fromStreamD . D.fromIndicesM
687689-- Generate a stream by performing a monadic action @n@ times. Same as:
688690--
689691-- @
690−-- runStream $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
691−-- runStream $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
692+-- drain $ serially $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
693+-- drain $ asyncly $ S.replicateM 10 $ (threadDelay 1000000 >> print 1)
692694-- @
693695--
694696-- /Concurrent/
@@ -723,8 +725,8 @@ replicate n = fromStreamS . S.replicate n
723725-- Generate a stream by repeatedly executing a monadic action forever.
724726--
725727-- @
726−-- runStream $ serially $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
727−-- runStream $ asyncly $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
728+-- drain $ serially $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
729+-- drain $ asyncly $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
728730-- @
729731--
730732-- /Concurrent, infinite (do not use with 'parallely')/
@@ -769,10 +771,10 @@ iterate step = fromStream . go
769771-- previous iteration.
770772--
771773-- @
772−-- runStream $ serially $ S.take 10 $ S.iterateM
774+-- drain $ serially $ S.take 10 $ S.iterateM
773775-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
774776--
775−-- runStream $ asyncly $ S.take 10 $ S.iterateM
777+-- drain $ asyncly $ S.take 10 $ S.iterateM
776778-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
777779-- @
778780--
@@ -813,8 +815,8 @@ fromListM = fromStreamD . D.fromListM
813815-- Construct a stream from a 'Foldable' containing monadic actions.
814816--
815817-- @
816−-- runStream $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
817−-- runStream $ asyncly $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
818+-- drain $ serially $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
819+-- drain $ asyncly $ S.fromFoldableM $ replicateM 10 (threadDelay 1000000 >> print 1)
818820-- @
819821--
820822-- /Concurrent (do not use with 'parallely' on infinite containers)/
@@ -1165,36 +1167,51 @@ foldlM' step begin m = S.foldlM' step begin $ toStreamS m
11651167------------------------------------------------------------------------------
11661168
11671169-- |
1168−-- > runStream = mapM_ (\_ -> return ())
1170+-- > drain = mapM_ (\_ -> return ())
11691171--
11701172-- Run a stream, discarding the results. By default it interprets the stream
11711173-- as 'SerialT', to run other types of streams use the type adapting
1172−-- combinators for example @runStream . 'asyncly'@.
1174+-- combinators for example @drain . 'asyncly'@.
11731175--
11741176-- @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" #-}
11751182{-# INLINE runStream #-}
11761183runStream :: Monad m => SerialT m a -> m ()
1177−runStream = P.runStream
1184+runStream = drain
11781185
11791186-- |
1180−-- > runN n = runStream . take n
1187+-- > drainN n = drain . take n
11811188--
11821189-- Run maximum up to @n@ iterations of a stream.
11831190--
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" #-}
11851197{-# INLINE runN #-}
11861198runN :: Monad m => Int -> SerialT m a -> m ()
1187−runN n = runStream . take n
1199+runN = drainN
11881200
11891201-- |
1190−-- > runWhile p = runStream . takeWhile p
1202+-- > drainWhile p = drain . takeWhile p
11911203--
11921204-- Run a stream as long as the predicate holds true.
11931205--
11941206-- @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" #-}
11951212{-# INLINE runWhile #-}
11961213runWhile :: Monad m => (a -> Bool) -> SerialT m a -> m ()
1197−runWhile p = runStream . takeWhile p
1214+runWhile = drainWhile
11981215
11991216-- | Determine whether the stream is empty.
12001217--
@@ -1456,9 +1473,9 @@ stripPrefix m1 m2 = fmap fromStreamD <$>
14561473-- Map and Fold
14571474------------------------------------------------------------------------------
14581475
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
14601477-- |
1461−-- > mapM_ = runStream . mapM
1478+-- > mapM_ = drain . mapM
14621479--
14631480-- Apply a monadic action to each element of the stream and discard the output
14641481-- 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
14801497--
14811498-- Convert a stream into a list in the underlying monad. The list can be
14821499-- 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.
14841504--
14851505-- @since 0.1.0
14861506{-# INLINE toList #-}
14871507toList :: Monad m => SerialT m a -> m [a]
14881508toList = P.toList
14891509
1490−{-# INLINE _toArrayN #-}
1491−_toArrayN :: (Monad m, Storable a) => Int -> SerialT m a -> m (A.Array a)
1492−_toArrayN = P.toArrayN
1493−
14941510-- |
14951511-- @
1496−-- toRevList = S.foldl' (flip (:)) []
1512+-- toListRev = S.foldl' (flip (:)) []
14971513-- @
14981514--
14991515-- Convert a stream into a list in reverse order in the underlying monad.
15001516--
1517+-- /Warning!/ working on large lists accumulated as buffers in memory could be
1518+-- very inefficient, consider using "Streamly.Array" instead.
1519+--
15011520-- @since 0.7.0
1502−{-# INLINE toRevList #-}
1503−toRevList :: Monad m => SerialT m a -> m [a]
1504−toRevList = D.toRevList . toStreamD
1521+{-# INLINE _toListRev #-}
1522+_toListRev :: Monad m => SerialT m a -> m [a]
1523+_toListRev = D.toListRev . toStreamD
15051524
15061525-- |
15071526-- @
@@ -1756,13 +1775,13 @@ dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
17561775-- the output of the resulting action.
17571776--
17581777-- @
1759−-- > runStream $ S.mapM putStr $ S.fromList ["a", "b", "c"]
1778+-- > drain $ S.mapM putStr $ S.fromList ["a", "b", "c"]
17601779-- abc
17611780--
1762−-- runStream $ S.replicateM 10 (return 1)
1781+-- drain $ S.replicateM 10 (return 1)
17631782-- & (serially . S.mapM (\\x -> threadDelay 1000000 >> print x))
17641783--
1765−-- runStream $ S.replicateM 10 (return 1)
1784+-- drain $ S.replicateM 10 (return 1)
17661785-- & (asyncly . S.mapM (\\x -> threadDelay 1000000 >> print x))
17671786-- @
17681787--
@@ -1787,13 +1806,13 @@ mapMSerial = Serial.mapM
17871806-- those actions.
17881807--
17891808-- @
1790−-- > runStream $ S.sequence $ S.fromList [putStr "a", putStr "b", putStrLn "c"]
1809+-- > drain $ S.sequence $ S.fromList [putStr "a", putStr "b", putStrLn "c"]
17911810-- abc
17921811--
1793−-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1812+-- drain $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
17941813-- & (serially . S.sequence)
17951814--
1796−-- runStream $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
1815+-- drain $ S.replicateM 10 (return $ threadDelay 1000000 >> print 1)
17971816-- & (asyncly . S.sequence)
17981817-- @
17991818--
@@ -1844,11 +1863,21 @@ mapMaybeMSerial f m = fromStreamD $ D.mapMaybeM f $ toStreamD m
18441863-- Returns the elements of the stream in reverse order. The stream must be
18451864-- finite.
18461865--
1866+-- /Note:/ 'reverse'' is much faster than this, use that when performance
1867+-- matters.
1868+--
18471869-- @since 0.1.1
18481870{-# INLINE reverse #-}
18491871reverse :: (IsStream t, Monad m) => t m a -> t m a
18501872reverse s = fromStreamS $ S.reverse $ toStreamS s
18511873
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+
18521881------------------------------------------------------------------------------
18531882-- Transformation by Inserting
18541883------------------------------------------------------------------------------
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)
613613-- import qualified "Streamly.Prelude" as S
614614-- import Control.Concurrent
615615--
616−-- main = 'runStream' . 'aheadly' $ do
616+-- main = 'drain' . 'aheadly' $ do
617617-- n <- return 3 \<\> return 2 \<\> return 1
618618-- S.yieldM $ do
619619-- threadDelay (n * 1000000)
src/Streamly/Streams/Async.hs+2−2
@@ -618,7 +618,7 @@ consMAsync m r = fromStream $ K.yieldM m `async` (toStream r)
618618-- import qualified "Streamly.Prelude" as S
619619-- import Control.Concurrent
620620--
621−-- main = 'runStream' . 'asyncly' $ do
621+-- main = 'drain' . 'asyncly' $ do
622622-- n <- return 3 \<\> return 2 \<\> return 1
623623-- S.yieldM $ do
624624-- threadDelay (n * 1000000)
@@ -754,7 +754,7 @@ wAsync = joinStreamVarAsync WAsyncVar
754754-- import qualified "Streamly.Prelude" as S
755755-- import Control.Concurrent
756756--
757−-- main = 'runStream' . 'wAsyncly' $ do
757+-- main = 'drain' . 'wAsyncly' $ do
758758-- n <- return 3 \<\> return 2 \<\> return 1
759759-- S.yieldM $ do
760760-- threadDelay (n * 1000000)
src/Streamly/Streams/Parallel.hs+3−3
@@ -169,7 +169,7 @@ infixl 1 |&.
169169--
170170--
171171-- @
172−-- runStream $
172+-- drain $
173173-- S.mapM (\\x -> threadDelay 1000000 >> print x)
174174-- |$ S.repeatM (threadDelay 1000000 >> return 1)
175175-- @
@@ -186,7 +186,7 @@ f |$ x = applyWith f x
186186-- concurrent.
187187--
188188-- @
189−-- runStream $
189+-- drain $
190190-- S.repeatM (threadDelay 1000000 >> return 1)
191191-- |& S.mapM (\\x -> threadDelay 1000000 >> print x)
192192-- @
@@ -304,7 +304,7 @@ x |&. f = f |$. x
304304-- import qualified "Streamly.Prelude" as S
305305-- import Control.Concurrent
306306--
307−-- main = 'runStream' . 'parallely' $ do
307+-- main = 'drain' . 'parallely' $ do
308308-- n <- return 3 \<\> return 2 \<\> return 1
309309-- S.yieldM $ do
310310-- threadDelay (n * 1000000)
src/Streamly/Streams/Prelude.hs+6−24
@@ -23,13 +23,11 @@ module Streamly.Streams.Prelude
2323 , toStreamS
2424
2525 -- * Running Effects
26− , runStream
26+ , drain
2727
2828 -- * Conversion operations
2929 , fromList
30− , fromArray
3130 , toList
32− , toArrayN
3331
3432 -- * Fold operations
3533 , foldrM
@@ -61,7 +59,6 @@ module Streamly.Streams.Prelude
6159where
6260
6361import Control.Monad.Trans (MonadTrans(..))
64−import Foreign.Storable (Storable)
6562import Prelude hiding (foldr)
6663import qualified Prelude
6764
@@ -74,7 +71,6 @@ import qualified Streamly.Streams.StreamD as S
7471import Streamly.Streams.StreamK (IsStream(..))
7572import qualified Streamly.Streams.StreamK as K
7673import qualified Streamly.Streams.StreamD as D
77−import qualified Streamly.Array.Types as A
7874
7975------------------------------------------------------------------------------
8076-- Conversion to and from direct style stream
@@ -93,11 +89,11 @@ toStreamS = S.fromStreamK . toStream
9389-- Conversions
9490------------------------------------------------------------------------------
9591
96−{-# INLINE_EARLY runStream #-}
97−runStream :: (IsStream t, Monad m) => t m a -> m ()
98−runStream m = D.runStream $ D.fromStreamK (toStream m)
99−{-# RULES "runStream fallback to CPS" [1]
100− forall a. D.runStream (D.fromStreamK a) = K.runStream a #-}
92+{-# INLINE_EARLY drain #-}
93+drain :: (IsStream t, Monad m) => t m a -> m ()
94+drain m = D.drain $ D.fromStreamK (toStream m)
95+{-# RULES "drain fallback to CPS" [1]
96+ forall a. D.drain (D.fromStreamK a) = K.drain a #-}
10197
10298------------------------------------------------------------------------------
10399-- Conversions
@@ -118,16 +114,6 @@ fromList = fromStreamS . S.fromList
118114{-# RULES "fromList fallback to StreamK" [1]
119115 forall a. S.toStreamK (S.fromList a) = K.fromFoldable a #-}
120116
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−
131117-- | Convert a stream into a list in the underlying monad.
132118--
133119-- @since 0.1.0
@@ -135,10 +121,6 @@ fromArray = D.fromStreamD . D.fromArray
135121toList :: (Monad m, IsStream t) => t m a -> m [a]
136122toList m = S.toList $ toStreamS m
137123
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−
142124------------------------------------------------------------------------------
143125-- Folds
144126------------------------------------------------------------------------------
src/Streamly/Streams/Serial.hs+3−3
@@ -92,7 +92,7 @@ import qualified Streamly.Streams.StreamD as D
9292-- element of the stream, serially.
9393--
9494-- @
95−-- main = 'runStream' . 'serially' $ do
95+-- main = 'drain' . 'serially' $ do
9696-- x <- return 1 \<\> return 2
9797-- S.yieldM $ print x
9898-- @
@@ -104,7 +104,7 @@ import qualified Streamly.Streams.StreamD as D
104104-- 'SerialT' nests streams serially in a depth first manner.
105105--
106106-- @
107−-- main = 'runStream' . 'serially' $ do
107+-- main = 'drain' . 'serially' $ do
108108-- x <- return 1 \<\> return 2
109109-- y <- return 3 \<\> return 4
110110-- S.yieldM $ print (x, y)
@@ -223,7 +223,7 @@ TRAVERSABLE_INSTANCE(SerialT)
223223--
224224--
225225-- @
226−-- main = 'runStream' . 'wSerially' $ do
226+-- main = 'drain' . 'wSerially' $ do
227227-- x <- return 1 \<\> return 2
228228-- y <- return 3 \<\> return 4
229229-- S.yieldM $ print (x, y)
src/Streamly/Streams/StreamD.hs+760−114
@@ -83,7 +83,6 @@ module Streamly.Streams.StreamD
8383 , yieldM
8484 , fromList
8585 , fromListM
86− , fromArray
8786 , fromStreamK
8887 , fromStreamD
8988
@@ -100,12 +99,14 @@ module Streamly.Streams.StreamD
10099 , foldlS
101100 , foldlT
102101 , reverse
102+ , reverse'
103103
104104 , foldlx'
105105 , foldlMx'
106106
107107 -- ** Specialized Folds
108− , runStream
108+ , tap
109+ , drain
109110 , null
110111 , head
111112 , tail
@@ -129,6 +130,20 @@ module Streamly.Streams.StreamD
129130 , concatMap
130131 , flattenArrays
131132
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+
132147 -- ** Substreams
133148 , isPrefixOf
134149 , isSubsequenceOf
@@ -140,10 +155,9 @@ module Streamly.Streams.StreamD
140155 -- ** Conversions
141156 -- | Transform a stream into another type.
142157 , toList
143− , toRevList
158+ , toListRev
144159 , toStreamK
145160 , toStreamD
146− , toArrayN
147161
148162 -- * Transformation
149163 -- ** By folding (scans)
@@ -215,10 +229,10 @@ module Streamly.Streams.StreamD
215229where
216230
217231import Control.Monad.Trans (MonadTrans(lift))
232+import Data.Word (Word32)
233+import Data.Bits (shiftR, shiftL, (.|.), (.&.))
218234import Data.Maybe (fromJust, isJust)
219−import Foreign.ForeignPtr (ForeignPtr, touchForeignPtr)
220−import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
221−import Foreign.Ptr (Ptr, plusPtr)
235+import Foreign.Ptr (Ptr)
222236import Foreign.Storable (Storable(..))
223237import GHC.Types (SPEC(..))
224238import Prelude
@@ -227,14 +241,18 @@ import Prelude
227241 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
228242 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
229243 reverse)
244+import System.IO.Unsafe (unsafeDupablePerformIO)
230245
231246import Streamly.Array.Types
232− (Array(..), Array, unsafeInlineIO)
247+ (Array(..), unsafeInlineIO, unsafeIndex, flattenArrays)
248+import Streamly.Fold.Types (Fold(..))
233249import Streamly.SVar (MonadAsync, defState, adaptState)
250+import Streamly.Sink.Types (Sink(..))
234251
235252import Streamly.Streams.StreamD.Type
236253import qualified Streamly.Streams.StreamK as K
237254import qualified Streamly.Array.Types as A
255+import qualified Streamly.RingBuffer as RB
238256
239257------------------------------------------------------------------------------
240258-- Construction
@@ -530,20 +548,6 @@ fromListM = Stream step
530548 step _ (m:ms) = m >>= \x -> return $ Yield x ms
531549 step _ [] = return Stop
532550
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−
547551{-# INLINE toStreamD #-}
548552toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
549553toStreamD = fromStreamK . K.toStream
@@ -568,27 +572,6 @@ foldr1 f m = do
568572-- Left Folds
569573------------------------------------------------------------------------------
570574
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−
592575{-# INLINE_NORMAL foldlT #-}
593576foldlT :: (Monad m, Monad (s m), MonadTrans s)
594577 => (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))
628611------------------------------------------------------------------------------
629612
630613-- | Run a streaming composition, discard the results.
631−{-# INLINE_LATE runStream #-}
632−runStream :: Monad m => Stream m a -> m ()
633−-- runStream m = foldrM (\x xs -> x `seq` xs) (return ()) m
634−runStream (Stream step state) = go SPEC state
614+{-# INLINE_LATE drain #-}
615+drain :: Monad m => Stream m a -> m ()
616+-- drain m = foldrM (\x xs -> x `seq` xs) (return ()) m
617+drain (Stream step state) = go SPEC state
635618 where
636619 go !_ st = do
637620 r <- step defState st
@@ -827,9 +810,9 @@ findIndices p (Stream step state) = Stream step' (state, 0)
827810 Skip s -> Skip (s, i+1)
828811 Stop -> Stop
829812
830−{-# INLINE toRevList #-}
831−toRevList :: Monad m => Stream m a -> m [a]
832−toRevList = foldl' (flip (:)) []
813+{-# INLINE toListRev #-}
814+toListRev :: Monad m => Stream m a -> m [a]
815+toListRev = foldl' (flip (:)) []
833816
834817-- We can implement reverse as:
835818--
@@ -844,16 +827,50 @@ toRevList = foldl' (flip (:)) []
844827reverse :: Monad m => Stream m a -> Stream m a
845828reverse m = Stream step Nothing
846829 where
847− step _ Nothing = do
848− xs <- toRevList m
849− return $ Skip (Just xs)
850− step _ (Just (x:xs)) = return $ Yield x (Just xs)
851− step _ (Just []) = return Stop
830+ {-# INLINE_LATE step #-}
831+ step _ Nothing = do
832+ xs <- toListRev m
833+ return $ Skip (Just xs)
834+ step _ (Just (x:xs)) = return $ Yield x (Just xs)
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)
852857
853−{-# INLINE_NORMAL toArrayN #-}
854−toArrayN :: forall m a. (Monad m, Storable a)
855− => Int -> Stream m a -> m (Array a)
856−toArrayN = A.fromStreamDN
858+ step _ (Just (start, p)) | p < unsafeForeignPtrToPtr start = return Stop
859+
860+ step _ (Just (start, p)) = do
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
857874
858875------------------------------------------------------------------------------
859876-- concatMap
@@ -877,8 +894,8 @@ concatMapM f (Stream step state) = Stream step' (Left state)
877894 -- can get inner_step to inline and fuse here we can perhaps get the same
878895 -- performance using "concatMap fromArray".
879896 --
880− -- 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
897+ -- XXX using the pattern synonym "Stream" causes a major performance issue
898+ -- here even if the synonym does not include an adaptState call. Need to
882899 -- find out why. Is that something to be fixed in GHC?
883900 step' gst (Right (UnStream inner_step inner_st, st)) = do
884901 r <- inner_step (adaptState gst) inner_st
@@ -889,38 +906,6 @@ concatMapM f (Stream step state) = Stream step' (Left state)
889906 return $ Skip (Right (Stream inner_step inner_s, st))
890907 Stop -> return $ Skip (Left st)
891908
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−
924909{-# INLINE concatMap #-}
925910concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
926911concatMap f = concatMapM (return . f)
@@ -932,8 +917,673 @@ concatMap f = concatMapM (return . f)
932917-- "concatMap fromArray" (without the RULE) become much slower, this means
933918-- "concatMap f" in general would become slower. Need to find a solution to
934919-- 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.
9351129--
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
9371587
9381588------------------------------------------------------------------------------
9391589-- Substreams
@@ -1011,31 +1661,12 @@ stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing)
10111661-- | Execute a monadic action for each element of the 'Stream'
10121662{-# INLINE_NORMAL mapM_ #-}
10131663mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()
1014−mapM_ m = runStream . mapM m
1664+mapM_ m = drain . mapM m
10151665
10161666------------------------------------------------------------------------------
10171667-- Converting folds
10181668------------------------------------------------------------------------------
10191669
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−
10391670{-# INLINE fromStreamD #-}
10401671fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
10411672fromStreamD = K.fromStream . toStreamK
@@ -1238,6 +1869,21 @@ scanl1M' fstep (Stream step state) = Stream step' (state, Nothing)
12381869scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
12391870scanl1' f = scanl1M' (\x y -> return (f x y))
12401871
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+
12411887-------------------------------------------------------------------------------
12421888-- Filtering
12431889-------------------------------------------------------------------------------
src/Streamly/Streams/StreamD/Type.hs+127
@@ -32,6 +32,8 @@ module Streamly.Streams.StreamD.Type
3232 , Stream (UnStream)
3333 , pattern Stream
3434#endif
35+ , fromStreamK
36+ , toStreamK
3537 , map
3638 , mapM
3739
@@ -42,6 +44,8 @@ module Streamly.Streams.StreamD.Type
4244
4345 , foldl'
4446 , foldlM'
47+ , foldlx'
48+ , foldlMx'
4549
4650 , toList
4751 , fromList
@@ -49,6 +53,7 @@ module Streamly.Streams.StreamD.Type
4953 , eqBy
5054 , cmpBy
5155 , take
56+ , groupsOf
5257 )
5358where
5459
@@ -56,7 +61,9 @@ import Control.Applicative (liftA2)
5661import Control.Monad.Trans (lift, MonadTrans)
5762import GHC.Types (SPEC(..))
5863import Prelude hiding (map, mapM, foldr, take)
64+
5965import Streamly.SVar (State(..), adaptState, defState)
66+import Streamly.Fold.Types (Fold(..))
6067
6168import qualified Streamly.Streams.StreamK as K
6269
@@ -93,6 +100,35 @@ pattern Stream step state <- (unShare -> UnStream step state)
93100{-# COMPLETE Stream #-}
94101#endif
95102
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+
96132------------------------------------------------------------------------------
97133-- Instances
98134------------------------------------------------------------------------------
@@ -190,6 +226,27 @@ foldrT f final (Stream step state) = go SPEC state
190226toList :: Monad m => Stream m a -> m [a]
191227toList = foldr (:) []
192228
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+
193250-- XXX implement in terms of foldlMx'?
194251{-# INLINE_NORMAL foldlM' #-}
195252foldlM' :: 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)
290347 Skip s -> Skip (s, i)
291348 Stop -> Stop
292349 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
8787 , foldlMx'
8888
8989 -- ** Specialized Folds
90− , runStream
90+ , drain
9191 , null
9292 , head
9393 , tail
@@ -490,11 +490,11 @@ foldlT step begin m = go begin m
490490------------------------------------------------------------------------------
491491
492492-- |
493−-- > runStream = foldl' (\_ _ -> ()) ()
494−-- > runStream = mapM_ (\_ -> return ())
495−{-# INLINE runStream #-}
496−runStream :: (Monad m, IsStream t) => t m a -> m ()
497−runStream = go
493+-- > drain = foldl' (\_ _ -> ()) ()
494+-- > drain = mapM_ (\_ -> return ())
495+{-# INLINE drain #-}
496+drain :: (Monad m, IsStream t) => t m a -> m ()
497+drain = go
498498 where
499499 go m1 =
500500 let stop = return ()
src/Streamly/Streams/StreamK/Type.hs+2−2
@@ -141,8 +141,8 @@ class IsStream t where
141141 --
142142 -- @
143143 -- let delay = threadDelay 1000000 >> print 1
144− -- runStream $ serially $ delay |: delay |: delay |: nil
145− -- runStream $ parallely $ delay |: delay |: delay |: nil
144+ -- drain $ serially $ delay |: delay |: delay |: nil
145+ -- drain $ parallely $ delay |: delay |: delay |: nil
146146 -- @
147147 --
148148 -- /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 @@
66-- License : BSD3
77-- Maintainer : harendra.kumar@gmail.com
88--
9−-- Streamly is a general computing framework based on streaming IO. The IO
10−-- monad and pure lists are a special case of streamly. On one hand, streamly
11−-- extends the lists of pure values to lists of monadic actions, on the other
12−-- hand it extends the IO monad with concurrent non-determinism. In simple
13−-- imperative terms we can say that streamly extends the IO monad with @for@
14−-- loops and nested @for@ loops with concurrency support. You can understand
15−-- this analogy better once you can go through this tutorial.
9+-- Streamly is a general computing framework based on concurrent data flow
10+-- programming. The IO monad and pure lists are a special case of streamly. On
11+-- one hand, streamly extends the lists of pure values to lists of monadic
12+-- actions, on the other hand it extends the IO monad with concurrent
13+-- non-determinism. In simple imperative terms we can say that streamly extends
14+-- the IO monad with @for@ loops and nested @for@ loops with concurrency
15+-- support. Hopefully, this analogy becomes clearer once you go through this
16+-- tutorial.
1617--
1718-- Streaming in general enables writing modular, composable and scalable
1819-- applications with ease, and concurrency allows you to make them scale and
1920-- perform well. Streamly enables writing scalable concurrent applications
2021-- 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
2223-- controlled based on the demand by the consumer. However, combinators can be
2324-- used to fine tune the concurrency control.
2425--
@@ -29,12 +30,19 @@
2930-- programming in an elegant and simple API that is a natural extension of pure
3031-- lists to monadic streams.
3132--
32−-- In this tutorial we will go over the basic concepts and how to
33−-- use the library. See the last section for further reading resources.
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+--
40+-- Once you finish this tutorial, see the last section for further reading
41+-- resources.
3442
3543module Streamly.Tutorial
3644 (
37− -- * Streams
45+ -- * Stream Types
3846 -- $streams
3947
4048 -- * Concurrent Streams
@@ -75,7 +83,7 @@ module Streamly.Tutorial
7583 -- *** Wide Serial Composition ('WSerial')
7684 -- $interleaved
7785
78− -- *** Deep Ahead Composition ('Ahead')
86+ -- *** Deep Speculative Composition ('Ahead')
7987 -- $ahead
8088
8189 -- *** Deep Asynchronous Composition ('Async')
@@ -106,7 +114,7 @@ module Streamly.Tutorial
106114 -- *** Wide Serial Nesting ('WSerial')
107115 -- $interleavedNesting
108116
109− -- *** Deep Ahead Nesting ('Ahead')
117+ -- *** Deep Speculative Nesting ('Ahead')
110118 -- $aheadNesting
111119
112120 -- *** Deep Asynchronous Nesting ('Async')
@@ -172,22 +180,20 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
172180
173181-- $streams
174182--
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
177−-- very close to the Haskell "Prelude" pure lists' API, it can be considered as a
178−-- natural extension of lists to monadic actions. Streamly streams provide
179−-- concurrent composition and merging of streams. It can be considered as a
180−-- concurrent list transformer. In contrast to the "Prelude" lists, merging or
181−-- appending streams of arbitrary length is scalable and inexpensive.
183+-- The monadic stream API offered by Streamly is very close to the Haskell
184+-- "Prelude" pure lists' API, it can be considered as a natural extension of
185+-- lists to monadic actions. Streamly streams provide concurrent composition
186+-- and merging of streams. It can be considered as a concurrent list
187+-- transformer.
182188--
183189-- The basic stream type is 'Serial', it represents a sequence of IO actions,
184190-- and is a 'Monad'. The 'Serial' monad is almost a drop in replacement for
185191-- the 'IO' monad, IO monad is a special case of the 'Serial' monad; IO monad
186192-- represents a single IO action whereas the 'Serial' monad represents a series
187193-- 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
189195-- 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
191197-- are needed unless you have used some stream specific composition or
192198-- combinators.
193199--
@@ -326,9 +332,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
326332-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
327333--
328334-- @
329−-- > 'runStream' $ liftIO $ putStrLn "Hello world!"
335+-- > 'drain' $ liftIO $ putStrLn "Hello world!"
330336-- Hello world!
331−-- > 'runStream' $ lift $ putStrLn "Hello world!"
337+-- > 'drain' $ lift $ putStrLn "Hello world!"
332338-- Hello world!
333339-- @
334340--
@@ -385,7 +391,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
385391-- right fold using 'consM' and 'nil':
386392--
387393-- @
388−-- > 'runStream' $ 'Prelude.foldr' ('|:') S.'nil' ['putStr' "Hello ", 'putStrLn' "world!"]
394+-- > 'drain' $ 'Prelude.foldr' ('|:') S.'nil' ['putStr' "Hello ", 'putStrLn' "world!"]
389395-- Hello world!
390396-- @
391397--
@@ -412,14 +418,14 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
412418-- The following finishes in 10 seconds (100 seconds when serial):
413419--
414420-- @
415−-- > 'runStream' $ 'asyncly' $ S.'replicateM' 10 $ p 10
421+-- > 'drain' $ 'asyncly' $ S.'replicateM' 10 $ p 10
416422-- @
417423--
418424
419425-- $eliminating
420426--
421−-- We have already seen 'runStream' and 'toList' to eliminate a stream in the
422−-- examples above. 'runStream' runs a stream discarding the results i.e. only
427+-- We have already seen 'drain' and 'toList' to eliminate a stream in the
428+-- examples above. 'drain' runs a stream discarding the results i.e. only
423429-- for effects. 'toList' runs the stream and collects the results in a list.
424430--
425431-- For other ways to eliminate a stream see the @Folding@ section in
@@ -439,7 +445,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
439445-- forever:
440446--
441447-- @
442−-- > 'runStream' $ S.'repeatM' getLine & S.'mapM' putStrLn
448+-- > 'drain' $ S.'repeatM' getLine & S.'mapM' putStrLn
443449-- @
444450--
445451-- The following code snippet reads lines from standard input, filters blank
@@ -452,7 +458,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
452458-- import Data.Char (toUpper)
453459-- import Data.Function ((&))
454460--
455−-- main = 'runStream' $
461+-- main = 'drain' $
456462-- S.'repeatM' getLine
457463-- & S.'filter' (not . null)
458464-- & S.'drop' 1
@@ -473,7 +479,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
473479--
474480-- @
475481-- > 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))
477483-- @
478484--
479485
@@ -488,7 +494,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
488494--
489495-- @
490496-- > 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)
492498-- @
493499
494500-- $semigroup
@@ -544,7 +550,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
544550-- 3, 4:
545551--
546552-- @
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)
548554-- @
549555-- @
550556-- 1
@@ -558,7 +564,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
558564-- same thread and take a combined total of @3 + 2 + 1 = 6@ seconds:
559565--
560566-- @
561−-- main = 'runStream' $ delay 3 <> delay 2 <> delay 1
567+-- main = 'drain' $ delay 3 <> delay 2 <> delay 1
562568-- @
563569-- @
564570-- ThreadId 36: Delay 3
@@ -571,7 +577,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
571577-- irrespective of the type of stream:
572578--
573579-- @
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)
575581-- @
576582
577583-- $interleaved
@@ -588,7 +594,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
588594-- The following example prints the sequence 1, 3, 2, 4
589595--
590596-- @
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)
592598-- @
593599-- @
594600-- 1
@@ -603,7 +609,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
603609-- thread and take a combined total of @3 + 2 + 1 = 6@ seconds:
604610--
605611-- @
606−-- main = 'runStream' . 'wSerially' $ delay 3 <> delay 2 <> delay 1
612+-- main = 'drain' . 'wSerially' $ delay 3 <> delay 2 <> delay 1
607613-- @
608614-- @
609615-- ThreadId 36: Delay 3
@@ -617,7 +623,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
617623-- combinator in the following example:
618624--
619625-- @
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)
621627-- @
622628-- @
623629-- 1
@@ -684,7 +690,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
684690--
685691-- @
686692-- 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)
688694-- print xs
689695-- where p n = threadDelay 1000000 >> return n
690696-- @
@@ -699,7 +705,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
699705--
700706-- @
701707-- 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)
703709-- print xs
704710-- where p n = threadDelay 1000000 >> return n
705711-- @
@@ -713,7 +719,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
713719-- (3, 2, 1) = 3@ seconds:
714720--
715721-- @
716−-- main = 'runStream' . 'asyncly' $ delay 3 '<>' delay 2 '<>' delay 1
722+-- main = 'drain' . 'asyncly' $ delay 3 '<>' delay 2 '<>' delay 1
717723-- @
718724-- @
719725-- ThreadId 42: Delay 1
@@ -732,7 +738,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
732738-- second as all of the actions are concurrent.
733739--
734740-- @
735−-- main = 'runStream' . 'asyncly' $ (delay 1 <> delay 2) <> (delay 3 <> delay 4)
741+-- main = 'drain' . 'asyncly' $ (delay 1 <> delay 2) <> (delay 3 <> delay 4)
736742-- @
737743-- @
738744-- 1
@@ -749,7 +755,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
749755-- even if none of them blocks:
750756--
751757-- @
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)
753759-- where traced m = S.'yieldM' (myThreadId >>= print) >> return m
754760-- @
755761-- @
@@ -767,7 +773,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
767773-- not used the 'asyncly' combinator in the following example:
768774--
769775-- @
770−-- main = 'runStream' $ delay 3 \`async` delay 2 \`async` delay 1
776+-- main = 'drain' $ delay 3 \`async` delay 2 \`async` delay 1
771777-- @
772778-- @
773779-- ThreadId 42: Delay 1
@@ -810,7 +816,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
810816-- first traversal order but this is not guaranteed.
811817--
812818-- @
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)
814820-- @
815821-- @
816822-- 1
@@ -825,7 +831,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
825831-- the 'wAsyncly' combinator in the following example:
826832--
827833-- @
828−-- main = 'runStream' $ delay 3 \`wAsync` delay 2 \`wAsync` delay 1
834+-- main = 'drain' $ delay 3 \`wAsync` delay 2 \`wAsync` delay 1
829835-- @
830836-- @
831837-- ThreadId 42: Delay 1
@@ -867,7 +873,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
867873-- import qualified Streamly.Prelude as S
868874-- import Network.HTTP.Simple
869875--
870−-- main = 'runStream' . 'parallely' $ google \<> bing \<> duckduckgo
876+-- main = 'drain' . 'parallely' $ google \<> bing \<> duckduckgo
871877-- where
872878-- google = get "https://www.google.com/search?q=haskell"
873879-- bing = get "https://www.bing.com/search?q=haskell"
@@ -881,7 +887,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
881887-- 'parallely' combinator in the following example:
882888--
883889-- @
884−-- main = 'runStream' $ delay 3 \`parallel` delay 2 \`wAsync` delay 1
890+-- main = 'drain' $ delay 3 \`parallel` delay 2 \`wAsync` delay 1
885891-- @
886892-- @
887893-- ThreadId 42: Delay 1
@@ -928,10 +934,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
928934-- import Control.Concurrent
929935--
930936-- main = do
931−-- 'runStream' $ 'asyncly' $ foldMap delay [1..10]
932−-- 'runStream' $ 'foldWith' 'async' (map delay [1..10])
933−-- 'runStream' $ 'foldMapWith' 'async' delay [1..10]
934−-- 'runStream' $ 'forEachWith' 'async' [1..10] delay
937+-- 'drain' $ 'asyncly' $ foldMap delay [1..10]
938+-- 'drain' $ 'foldWith' 'async' (map delay [1..10])
939+-- 'drain' $ 'foldMapWith' 'async' delay [1..10]
940+-- 'drain' $ 'forEachWith' 'async' [1..10] delay
935941-- where delay n = S.'yieldM' $ threadDelay (n * 1000000) >> print n
936942-- @
937943
@@ -980,7 +986,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
980986-- import "Streamly"
981987-- import qualified "Streamly.Prelude" as S
982988--
983−-- main = 'runStream' $ do
989+-- main = 'drain' $ do
984990-- x <- S.'fromFoldable' [3,2,1]
985991-- delay x
986992-- @
@@ -1002,7 +1008,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
10021008-- import "Streamly"
10031009-- import qualified "Streamly.Prelude" as S
10041010--
1005−-- main = 'runStream' $ forever $ S.yieldM getLine >>= S.yieldM . putStrLn
1011+-- main = 'drain' $ forever $ S.yieldM getLine >>= S.yieldM . putStrLn
10061012-- @
10071013--
10081014-- When multiple streams are composed using this style they nest in a DFS
@@ -1014,7 +1020,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
10141020-- import "Streamly"
10151021-- import qualified "Streamly.Prelude" as S
10161022--
1017−-- main = 'runStream' $ do
1023+-- main = 'drain' $ do
10181024-- x <- S.'fromFoldable' [1,2]
10191025-- y <- S.'fromFoldable' [3,4]
10201026-- S.'yieldM' $ putStrLn $ show (x, y)
@@ -1084,7 +1090,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
10841090-- import "Streamly"
10851091-- import "Streamly.Prelude"
10861092--
1087−-- main = 'runStream' . 'asyncly' $ do
1093+-- main = 'drain' . 'asyncly' $ do
10881094-- x <- S.'fromFoldable' [3,2,1]
10891095-- delay x
10901096-- @
@@ -1111,7 +1117,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
11111117-- import "Streamly"
11121118-- import qualified "Streamly.Prelude" as S
11131119--
1114−-- main = 'runStream' . 'asyncly' $ do
1120+-- main = 'drain' . 'asyncly' $ do
11151121-- x <- S.'fromFoldable' [1,2]
11161122-- y <- S.'fromFoldable' [3,4]
11171123-- S.'yieldM' $ putStrLn $ show (x, y)
@@ -1136,7 +1142,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
11361142-- import "Streamly"
11371143-- import qualified "Streamly.Prelude" as S
11381144--
1139−-- main = 'runStream' . 'wSerially' $ do
1145+-- main = 'drain' . 'wSerially' $ do
11401146-- x <- S.'fromFoldable' [1,2]
11411147-- y <- S.'fromFoldable' [3,4]
11421148-- S.yieldM $ putStrLn $ show (x, y)
@@ -1164,7 +1170,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
11641170-- import "Streamly"
11651171-- import qualified "Streamly.Prelude" as S
11661172--
1167−-- main = 'runStream' . 'wAsyncly' $ do
1173+-- main = 'drain' . 'wAsyncly' $ do
11681174-- x <- S.'fromFoldable' [1,2]
11691175-- y <- S.'fromFoldable' [3,4]
11701176-- S.'yieldM' $ putStrLn $ show (x, y)
@@ -1190,7 +1196,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
11901196-- import "Streamly"
11911197-- import qualified "Streamly.Prelude" as S
11921198--
1193−-- main = 'runStream' . 'parallely' $ do
1199+-- main = 'drain' . 'parallely' $ do
11941200-- x <- S.'fromFoldable' [3,2,1]
11951201-- delay x
11961202-- @
@@ -1228,11 +1234,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
12281234-- Now we can interpret this in whatever way we want:
12291235--
12301236-- @
1231−-- main = 'runStream' . 'serially' $ composed
1232−-- main = 'runStream' . 'wSerially' $ composed
1233−-- main = 'runStream' . 'asyncly' $ composed
1234−-- main = 'runStream' . 'wAsyncly' $ composed
1235−-- main = 'runStream' . 'parallely' $ composed
1237+-- main = 'drain' . 'serially' $ composed
1238+-- main = 'drain' . 'wSerially' $ composed
1239+-- main = 'drain' . 'asyncly' $ composed
1240+-- main = 'drain' . 'wAsyncly' $ composed
1241+-- main = 'drain' . 'parallely' $ composed
12361242-- @
12371243--
12381244-- 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))
15131519-- main = do
15141520-- putStrLn "Your health is deteriorating due to acid rain,\\
15151521-- \\ type \\"potion\\" or \\"quit\\""
1516−-- let runGame = S.'runWhile' (== Alive) $ S.'mapM' getStatus runEvents
1522+-- let runGame = S.'drainWhile' (== Alive) $ S.'mapM' getStatus runEvents
15171523-- void $ runStateT runGame 60
15181524-- @
15191525--
@@ -1713,13 +1719,14 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
17131719-- that and takes a lazy pull approach versus transient's strict push approach.
17141720--
17151721-- The non-determinism, concurrency and streaming combination make streamly a
1716−-- strong FRP capable library as well. FRP is fundamentally stream of events
1717−-- that can be processed concurrently. The example in this tutorial as well as
1718−-- the "Streamly.Examples.CirclingSquare" example from Yampa demonstrate the
1719−-- basic FRP capability of streamly. In core concepts streamly is strikingly
1720−-- similar to @dunai@. dunai was designed from a FRP perspective and streamly
1721−-- was originally designed from a concurrency perspective. However, both have
1722−-- similarity at the core.
1722+-- strong reactive programming library as well. Reactive programming is
1723+-- fundamentally stream of events that can be processed concurrently. The
1724+-- example in this tutorial as well as the
1725+-- <examples/CirclingSquare.hs CirclingSquare> example from Yampa demonstrate
1726+-- the basic reactive capability of streamly. In core concepts streamly is
1727+-- strikingly similar to @dunai@. dunai was designed from a FRP perspective
1728+-- and streamly was originally designed from a concurrency perspective.
1729+-- However, both have similarity at the core.
17231730
17241731-- $furtherReading
17251732--
streamly.cabal+8
@@ -172,11 +172,18 @@ library
172172 , Streamly.String
173173 , Streamly.Enumeration
174174 , Streamly.Array.Types
175+ , Streamly.RingBuffer
176+ , Streamly.Strict
177+ , Streamly.Fold.Types
178+ , Streamly.Sink.Types
179+ , Streamly.Sink
175180
176181 exposed-modules: Streamly.Prelude
177182 , Streamly.Array
178183 , Streamly.Time
179184 , Streamly
185+ , Streamly.Fold
186+
180187 , Streamly.Tutorial
181188 , Streamly.Internal
182189
@@ -220,6 +227,7 @@ library
220227 , mtl >= 2.2 && < 3
221228 , transformers >= 0.4 && < 0.6
222229 , transformers-base >= 0.4 && < 0.5
230+ , random
223231
224232 if impl(ghc < 8.0)
225233 build-depends:
test/Main.hs+15−15
@@ -153,7 +153,7 @@ checkCleanup :: IsStream t
153153 -> IO ()
154154checkCleanup d t op = do
155155 r <- newIORef (-1 :: Int)
156− S.runStream . serially $ do
156+ S.drain . serially $ do
157157 _ <- t $ op $ delay r 0 S.|: delay r 1 S.|: delay r 2 S.|: S.nil
158158 return ()
159159 performMajorGC
@@ -193,9 +193,9 @@ parallelTests = H.parallel $ do
193193 -- XXX move these to property tests
194194 -- XXX use an IORef to store and check the side effects
195195 it "simple serially" $
196− (S.runStream . serially) (return (0 :: Int)) `shouldReturn` ()
196+ (S.drain . serially) (return (0 :: Int)) `shouldReturn` ()
197197 it "simple serially with IO" $
198− (S.runStream . serially) (S.yieldM $ putStrLn "hello") `shouldReturn` ()
198+ (S.drain . serially) (S.yieldM $ putStrLn "hello") `shouldReturn` ()
199199
200200 describe "Empty" $ -- do
201201 it "Monoid - mempty" $
@@ -543,12 +543,12 @@ parallelTests = H.parallel $ do
543543 ---------------------------------------------------------------------------
544544
545545 it "asyncly crosses thread limit (2000 threads)" $
546− S.runStream (asyncly $ fold $
546+ S.drain (asyncly $ fold $
547547 replicate 2000 $ S.yieldM $ threadDelay 1000000)
548548 `shouldReturn` ()
549549
550550 it "aheadly crosses thread limit (4000 threads)" $
551− S.runStream (aheadly $ fold $
551+ S.drain (aheadly $ fold $
552552 replicate 4000 $ S.yieldM $ threadDelay 1000000)
553553 `shouldReturn` ()
554554
@@ -598,7 +598,7 @@ monadicStateSnapshot
598598 , MonadState Int (t (StateT Int IO))
599599 )
600600 => (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
602602
603603stateCompOp
604604 :: ( AsyncT (StateT Int IO) ()
@@ -621,7 +621,7 @@ monadicStateSnapshotOp
621621 -> AsyncT (StateT Int IO) ()
622622 )
623623 -> IO ()
624−monadicStateSnapshotOp op = void $ runStateT (S.runStream $ stateCompOp op) 0
624+monadicStateSnapshotOp op = void $ runStateT (S.drain $ stateCompOp op) 0
625625
626626takeCombined :: (Monad m, Semigroup (t m Int), Show a, Eq a, IsStream t)
627627 => Int -> (t m Int -> SerialT IO a) -> IO ()
@@ -674,7 +674,7 @@ checkScanxStrictness :: IO ()
674674checkScanxStrictness = do
675675 let s = return (1 :: Int) `S.consM` error "failure"
676676 catch
677− (S.runStream (
677+ (S.drain (
678678 S.scanx (\_ a ->
679679 if a == 1
680680 then error "success"
@@ -693,7 +693,7 @@ checkScanl'Strictness :: IO ()
693693checkScanl'Strictness = do
694694 let s = return (1 :: Int) `S.consM` error "failure"
695695 catch
696− (S.runStream
696+ (S.drain
697697 (S.scanl'
698698 (\_ a ->
699699 if a == 1
@@ -730,13 +730,13 @@ checkScanlMStrictness :: (IORef Int -> SerialT IO Int -> SerialT IO ()) -> IO ()
730730checkScanlMStrictness f = do
731731 ref <- newIORef 0
732732 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 ())
734734 readIORef ref `shouldReturn` 1
735735
736736takeInfinite :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
737737takeInfinite t =
738738 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)))
740740 `shouldReturn` ()
741741
742742-- XXX need to test that we have promptly cleaned up everything after the error
@@ -751,16 +751,16 @@ simpleMonadError :: Spec
751751simpleMonadError = do
752752{-
753753 it "simple runExceptT" $ do
754− (runExceptT $ S.runStream $ return ())
754+ (runExceptT $ S.drain $ return ())
755755 `shouldReturn` (Right () :: Either String ())
756756 it "simple runExceptT with error" $ do
757− (runExceptT $ S.runStream $ throwError "E") `shouldReturn` Left "E"
757+ (runExceptT $ S.drain $ throwError "E") `shouldReturn` Left "E"
758758 -}
759759 it "simple try" $
760− try (S.runStream $ return ())
760+ try (S.drain $ return ())
761761 `shouldReturn` (Right () :: Either ExampleException ())
762762 it "simple try with throw error" $
763− try (S.runStream $ throwM $ ExampleException "E")
763+ try (S.drain $ throwM $ ExampleException "E")
764764 `shouldReturn` (Left (ExampleException "E") :: Either ExampleException ())
765765
766766composeWithMonadThrow
test/Prop.hs+69−4
@@ -1,4 +1,6 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE OverloadedLists #-}
3+{-# LANGUAGE TypeFamilies #-}
24
35module Main (main) where
46
@@ -15,13 +17,14 @@ import Data.List
1517 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
1618 elemIndex, find, insertBy, intersperse, foldl1', (\\),
1719 maximumBy, minimumBy, deleteBy, isPrefixOf, isSubsequenceOf,
18− stripPrefix)
20+ stripPrefix, intercalate)
1921import Data.Maybe (mapMaybe)
2022import GHC.Word (Word8)
2123
2224import Test.Hspec.QuickCheck
2325import Test.QuickCheck
24− (counterexample, Property, withMaxSuccess, forAll, choose)
26+ (counterexample, Property, withMaxSuccess, forAll, choose, Gen,
27+ arbitrary, elements, frequency, listOf, listOf1, vectorOf, suchThat)
2528import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
2629
2730import Test.Hspec as H
@@ -29,6 +32,7 @@ import Test.Hspec as H
2932import Streamly
3033import Streamly.Prelude ((.:), nil)
3134import qualified Streamly.Prelude as S
35+import qualified Streamly.Fold as FL
3236
3337-- Coverage build takes too long with default number of tests
3438maxTestCount :: Int
@@ -463,6 +467,62 @@ transformCombineOpsCommon constr desc eq t = do
463467 transform (concatMap (const [1..n]))
464468 t (S.concatMap (const (S.fromList [1..n])))
465469
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+
466526-- transformation tests that can only work reliably for ordered streams i.e.
467527-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
468528-- might yield a different result every time.
@@ -832,7 +892,8 @@ main = hspec
832892 _ -> S.foldMapWith (<>) return xs
833893 )
834894
835− let makeOps t =
895+ let makeOps :: IsStream t => (t m a -> c) -> [(String, t m a -> c)]
896+ makeOps t =
836897 [ ("default", t)
837898#ifndef COVERAGE_BUILD
838899 , ("rate AvgRate 10000", t . avgRate 10000)
@@ -1013,7 +1074,8 @@ main = hspec
10131074
10141075 -- These tests won't work with maxBuffer or maxThreads set to 1, so we
10151076 -- 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 =
10171079 [ ("default", t)
10181080#ifndef COVERAGE_BUILD
10191081 , ("rate Nothing", t . rate Nothing)
@@ -1075,6 +1137,9 @@ main = hspec
10751137 zipSerialOps $ transformCombineOpsOrdered folded "zipSerially" (==)
10761138 zipAsyncOps $ transformCombineOpsOrdered folded "zipAsyncly" (==)
10771139
1140+ describe "Stream group and split operations" $ do
1141+ groupSplitOps "serially"
1142+
10781143 describe "Stream elimination operations" $ do
10791144 serialOps $ eliminationOps S.fromFoldable "serially"
10801145 wSerialOps $ eliminationOps S.fromFoldable "wSerially"
test/loops.hs+8−8
@@ -1,38 +1,38 @@
1−import Streamly hiding (runStream)
1+import Streamly
22import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
3−import Streamly.Prelude (nil, yieldM, runStream)
3+import Streamly.Prelude (nil, yieldM, drain)
44
55main :: IO ()
66main = do
77 hSetBuffering stdout LineBuffering
88
99 putStrLn "\nloopTail:\n"
10− runStream $ do
10+ drain $ do
1111 x <- loopTail 0
1212 yieldM $ print (x :: Int)
1313
1414 putStrLn "\nloopHead:\n"
15− runStream $ do
15+ drain $ do
1616 x <- loopHead 0
1717 yieldM $ print (x :: Int)
1818
1919 putStrLn "\nloopTailA:\n"
20− runStream $ do
20+ drain $ do
2121 x <- loopTailA 0
2222 yieldM $ print (x :: Int)
2323
2424 putStrLn "\nloopHeadA:\n"
25− runStream $ do
25+ drain $ do
2626 x <- loopHeadA 0
2727 yieldM $ print (x :: Int)
2828
2929 putStrLn "\nwSerial:\n"
30− runStream $ do
30+ drain $ do
3131 x <- (return 0 <> return 1) `wSerial` (return 100 <> return 101)
3232 yieldM $ print (x :: Int)
3333
3434 putStrLn "\nParallel interleave:\n"
35− runStream $ do
35+ drain $ do
3636 x <- (return 0 <> return 1) `wAsync` (return 100 <> return 101)
3737 yieldM $ print (x :: Int)
3838
test/nested-loops.hs+3−3
@@ -1,11 +1,11 @@
11import Control.Concurrent (myThreadId)
22import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
33import System.Random (randomIO)
4−import Streamly hiding (runStream)
5−import Streamly.Prelude (runStream, nil, yieldM)
4+import Streamly
5+import Streamly.Prelude (drain, nil, yieldM)
66
77main :: IO ()
8−main = runStream $ do
8+main = drain $ do
99 yieldM $ hSetBuffering stdout LineBuffering
1010 x <- loop "A " 2
1111 y <- loop "B " 2
test/parallel-loops.hs+1−1
@@ -7,7 +7,7 @@ import qualified Streamly.Prelude as S
77main :: IO ()
88main = do
99 hSetBuffering stdout LineBuffering
10− S.runStream $ do
10+ S.drain $ do
1111 x <- S.take 10 $ loop "A" `parallel` loop "B"
1212 S.yieldM $ myThreadId >>= putStr . show
1313 >> putStr " got "