on master help sign in
Use StreamD by default, expose StreamK as separate module

Basically remove depdency on GHC rewrite rules. The reason for separating StreamD and StreamK instead of using rewrite rules: * Separate types provide better reasoning for the programmer about performance. Each type has its own pros and cons and the programmer can choose the best one based on the use case. * rewrite rules are fragile, led to broken performance in the past dues to change in GHC. * Rewrite rules lead to optimization problems, blocking fusion in some cases, specifically when combining multiple operations e.g. (filter . drop). * Rewrite rules lead to problems when calling a function recursively. For example, the StreamD version of foldBreak cannot be used recursively when wrapped in rewrite rules because each recursive call adds a roundtrip conversion from D to K and back to D. We can use the StreamK versions of these though because the rewrite rule gets eliminated in that case. * If we have a unified module, we need two different versions of several operations e.g. appendK and appendD, both are useful in different cases.

commite249c4da2dd301ef0a2abb1db8ff3c717eddee0c authorHarendra Kumar <harendra@composewell.com> dateSat 21 Jan 2023, 19:05 (4 years ago) parent108af63
117 files changed+7142−2091
benchmark/Streamly/Benchmark/Data/Array/Common.hs+6−6 benchmark/Streamly/Benchmark/Data/Array/CommonImports.hs+4−3 benchmark/Streamly/Benchmark/Data/Array/Mut.hs+4−6 benchmark/Streamly/Benchmark/Data/Array/Stream.hs+28−22 benchmark/Streamly/Benchmark/Data/Fold.hs+2−2 benchmark/Streamly/Benchmark/Data/Fold/Window.hs+11−8 benchmark/Streamly/Benchmark/Data/Parser.hs+4−15 benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+8−9 benchmark/Streamly/Benchmark/Data/Parser/ParserK.hs+3−3 benchmark/Streamly/Benchmark/Data/Stream.hs+8 benchmark/Streamly/Benchmark/Data/Stream/Common.hs+122−62 benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs+56−38 benchmark/Streamly/Benchmark/Data/Stream/Exceptions.hs+36−28 benchmark/Streamly/Benchmark/Data/Stream/Expand.hs+108−76 benchmark/Streamly/Benchmark/Data/Stream/Generate.hs+43−25 benchmark/Streamly/Benchmark/Data/Stream/Lift.hs+29−27 benchmark/Streamly/Benchmark/Data/Stream/Reduce.hs+131−104 benchmark/Streamly/Benchmark/Data/Stream/Split.hs+3−2 benchmark/Streamly/Benchmark/Data/Stream/StreamK.hs+23−24 benchmark/Streamly/Benchmark/Data/Stream/ToStreamK.hs+14−10 benchmark/Streamly/Benchmark/Data/Stream/Transform.hs+137−30 benchmark/Streamly/Benchmark/Data/Unfold.hs+1−2 benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+23−16 benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+1−1 benchmark/Streamly/Benchmark/Unicode/Char.hs+1−1 benchmark/Streamly/Benchmark/Unicode/Stream.hs+12−4 benchmark/bench-runner/Main.hs+45−39 benchmark/lib/Streamly/Benchmark/Common.hs+1−1 benchmark/streamly-benchmarks.cabal+186−113 cabal.project.doctest+1−1 core/src/Streamly/Data/Stream.hs+40−45 core/src/Streamly/Data/Stream/StreamDK.hs+505 core/src/Streamly/Data/Stream/StreamK.hs+93 core/src/Streamly/Internal/Console/Stdio.hs+3−2 core/src/Streamly/Internal/Data/Array.hs+8−10 core/src/Streamly/Internal/Data/Array/Generic.hs+7−7 core/src/Streamly/Internal/Data/Array/Generic/Mut/Type.hs+3−2 core/src/Streamly/Internal/Data/Array/Mut.hs+3−6 core/src/Streamly/Internal/Data/Array/Mut/Stream.hs+6−9 core/src/Streamly/Internal/Data/Array/Mut/Type.hs+2−3 core/src/Streamly/Internal/Data/Array/Type.hs+12−6 core/src/Streamly/Internal/Data/Fold.hs+14−15 core/src/Streamly/Internal/Data/Fold/Chunked.hs+4−14 core/src/Streamly/Internal/Data/Fold/Type.hs+1−1 core/src/Streamly/Internal/Data/Parser.hs+10−4 core/src/Streamly/Internal/Data/Parser/Chunked.hs+9−10 core/src/Streamly/Internal/Data/Ring/Unboxed.hs+1−1 core/src/Streamly/Internal/Data/Stream.hs+3−32 core/src/Streamly/Internal/Data/Stream/Bottom.hs+7−7 core/src/Streamly/Internal/Data/Stream/Chunked.hs+65−61 core/src/Streamly/Internal/Data/Stream/Cross.hs+1−1 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+3−3 core/src/Streamly/Internal/Data/Stream/Exception.hs+6−6 core/src/Streamly/Internal/Data/Stream/Expand.hs+9−9 core/src/Streamly/Internal/Data/Stream/Reduce.hs+3−3 core/src/Streamly/Internal/Data/Stream/StreamD.hs+4 core/src/Streamly/Internal/Data/Stream/Container.hs → core/src/Streamly/Internal/Data/Stream/StreamD/Container.hs+29−28 core/src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs+165−12 core/src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+147−35 core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+771−42 core/src/Streamly/Internal/Data/Stream/StreamD/Lift.hs+36−8 core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+546−89 core/src/Streamly/Internal/Data/Stream/Top.hs → core/src/Streamly/Internal/Data/Stream/StreamD/Top.hs+8−45 core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+784−53 core/src/Streamly/Internal/Data/Stream/StreamD/Transformer.hs+68−2 core/src/Streamly/Internal/Data/Stream/StreamD/Type.hs+744−64 core/src/Streamly/Internal/Data/Stream/StreamDK.hs+41−145 core/src/Streamly/Internal/Data/Stream/StreamK.hs+207−21 core/src/Streamly/Internal/Data/Stream/StreamDK/Type.hs → core/src/Streamly/Internal/Data/Stream/StreamK/Alt.hs+143−3 core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs+754−69 core/src/Streamly/Internal/Data/Stream/Transform.hs+1−1 core/src/Streamly/Internal/Data/Stream/Type.hs+31−18 core/src/Streamly/Internal/Data/Unfold.hs+2−3 core/src/Streamly/Internal/FileSystem/File.hs+9−5 core/src/Streamly/Internal/FileSystem/Handle.hs+10−6 core/src/Streamly/Internal/Serialize/ToBytes.hs+17−17 core/src/Streamly/Internal/Unicode/Stream.hs+14−17 core/streamly-core.cabal+21−21 hie.yaml+10 src/Streamly/Data/Array/Foreign.hs+3−3 src/Streamly/Data/Stream/MkType.hs−7 src/Streamly/Internal/Data/SmallArray.hs+3−4 src/Streamly/Internal/Data/Stream/Ahead.hs+2−1 src/Streamly/Internal/Data/Stream/Async.hs+2−1 src/Streamly/Internal/Data/Stream/Concurrent.hs+8−11 src/Streamly/Internal/Data/Stream/Concurrent/Channel.hs+4−4 src/Streamly/Internal/Data/Stream/Concurrent/Channel/Operations.hs+2−2 src/Streamly/Internal/Data/Stream/Exception/Lifted.hs+4−6 src/Streamly/Internal/Data/Stream/IsStream.hs+13−9 src/Streamly/Internal/Data/Stream/IsStream/Common.hs+33−9 src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+118−29 src/Streamly/Internal/Data/Stream/IsStream/Exception.hs+6−6 src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+12−12 src/Streamly/Internal/Data/Stream/IsStream/Generate.hs+2−3 src/Streamly/Internal/Data/Stream/IsStream/Lift.hs+3−3 src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+10−8 src/Streamly/Internal/Data/Stream/IsStream/Top.hs+4−4 src/Streamly/Internal/Data/Stream/IsStream/Transform.hs+4−4 src/Streamly/Internal/Data/Stream/IsStream/Type.hs+18−8 src/Streamly/Internal/Data/Stream/MkType.hs+7−12 src/Streamly/Internal/Data/Stream/Parallel.hs+6−96 src/Streamly/Internal/Data/Stream/SVar/Eliminate.hs+5−4 src/Streamly/Internal/Data/Stream/SVar/Generate.hs+5−4 src/Streamly/Internal/Data/Stream/Serial.hs+172−101 src/Streamly/Internal/Data/Stream/Time.hs+3−3 src/Streamly/Internal/Data/Stream/Zip.hs+135 src/Streamly/Internal/Data/Stream/Zip/Concurrent.hs+2−2 src/Streamly/Internal/Data/Stream/ZipAsync.hs+1−1 src/Streamly/Internal/FileSystem/Event.hs+1−1 src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2 src/Streamly/Internal/FileSystem/FD.hs+4−4 src/Streamly/Internal/Network/Inet/TCP.hs+9−4 src/Streamly/Internal/Network/Socket.hs+10−7 src/Streamly/Internal/Unicode/Char.hs+2−3 src/Streamly/Prelude.hs+6 streamly.cabal+1 targets/Targets.hs+89−74
benchmark/Streamly/Benchmark/Data/Array/Common.hs+6−6
@@ -71,9 +71,9 @@ onArray
7171 -> m (Stream Int)
7272onArray value f arr = S.fold (A.writeN value) $ f $ S.unfold A.reader arr
7373
74−scanl' value n = composeN n $ onArray value $ S.scanl' (+) 0
75−scanl1' value n = composeN n $ onArray value $ S.scanl1' (+)
76−map value n = composeN n $ onArray value $ S.map (+1)
74+scanl' value n = composeN n $ onArray value $ S.scan (Fold.foldl' (+) 0)
75+scanl1' value n = composeN n $ onArray value $ Stream.scanl1' (+)
76+map value n = composeN n $ onArray value $ fmap (+1)
7777-- map n = composeN n $ A.map (+1)
7878
7979{-# INLINE eqInstance #-}
@@ -98,7 +98,7 @@ showInstance = P.show
9898
9999{-# INLINE pureFoldl' #-}
100100pureFoldl' :: MonadIO m => Stream Int -> m Int
101−pureFoldl' = S.foldl' (+) 0 . S.unfold A.reader
101+pureFoldl' = S.fold (Fold.foldl' (+) 0) . S.unfold A.reader
102102
103103-------------------------------------------------------------------------------
104104-- Elimination
@@ -106,11 +106,11 @@ pureFoldl' = S.foldl' (+) 0 . S.unfold A.reader
106106
107107{-# INLINE unfoldReadDrain #-}
108108unfoldReadDrain :: MonadIO m => Stream Int -> m ()
109−unfoldReadDrain = S.drain . S.unfold A.reader
109+unfoldReadDrain = S.fold Fold.drain . S.unfold A.reader
110110
111111{-# INLINE toStreamRevDrain #-}
112112toStreamRevDrain :: MonadIO m => Stream Int -> m ()
113−toStreamRevDrain = S.drain . A.readRev
113+toStreamRevDrain = S.fold Fold.drain . A.readRev
114114
115115-------------------------------------------------------------------------------
116116-- Bench groups
benchmark/Streamly/Benchmark/Data/Array/CommonImports.hs+4−3
@@ -5,11 +5,12 @@ import Control.Monad.IO.Class (MonadIO)
55import Data.Functor ((<&>))
66import System.Random (randomRIO)
77
8−import qualified Streamly.Prelude as S
9−import qualified Streamly.Internal.Data.Stream as Stream
8+import qualified Streamly.Data.Fold as Fold
9+import qualified Streamly.Data.Stream as S
10+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
1011
1112import Gauge
1213import Streamly.Benchmark.Common hiding (benchPureSrc)
13−import qualified Streamly.Benchmark.Prelude as P
14+import qualified Stream.Common as P
1415
1516import Prelude as P hiding (map)
benchmark/Streamly/Benchmark/Data/Array/Mut.hs+4−6
@@ -46,8 +46,7 @@ import Prelude
4646import qualified Streamly.Internal.Data.Array as Array
4747import qualified Streamly.Internal.Data.Array.Mut as MArray
4848import qualified Streamly.Internal.Data.Fold as Fold
49−import qualified Streamly.Internal.Data.Stream as Stream
50−import qualified Streamly.Prelude as IsStream (scanl1')
49+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
5150
5251import Gauge
5352import Streamly.Benchmark.Common hiding (benchPureSrc)
@@ -153,7 +152,7 @@ scanl' value n = composeN n $ onArray value $ Stream.scan (Fold.foldl' (+) 0)
153152
154153{-# INLINE scanl1' #-}
155154scanl1' :: MonadIO m => Int -> Int -> Stream Int -> m (Stream Int)
156−scanl1' value n = composeN n $ onArray value $ IsStream.scanl1' (+)
155+scanl1' value n = composeN n $ onArray value $ Stream.scanl1' (+)
157156
158157{-# INLINE map #-}
159158map :: MonadIO m => Int -> Int -> Stream Int -> m (Stream Int)
@@ -181,12 +180,11 @@ unfoldReadRevDrain = drain . Stream.unfold MArray.readerRev
181180
182181{-# INLINE toStreamDRevDrain #-}
183182toStreamDRevDrain :: MonadIO m => Stream Int -> m ()
184−toStreamDRevDrain =
185− drain . Stream.fromStreamD . MArray.toStreamDRev
183+toStreamDRevDrain = drain . MArray.toStreamDRev
186184
187185{-# INLINE toStreamDDrain #-}
188186toStreamDDrain :: MonadIO m => Stream Int -> m ()
189−toStreamDDrain = drain . Stream.fromStreamD . MArray.toStreamD
187+toStreamDDrain = drain . MArray.toStreamD
190188
191189{-# INLINE unfoldFold #-}
192190unfoldFold :: MonadIO m => Stream Int -> m Int
benchmark/Streamly/Benchmark/Data/Array/Stream.hs+28−22
@@ -27,21 +27,23 @@ module Main
2727
2828import Control.DeepSeq (NFData(..))
2929import Control.Monad (void, when)
30−import Control.Monad.Catch (MonadCatch, try, SomeException)
30+import Control.Monad.Catch (MonadCatch)
3131import Data.Functor.Identity (runIdentity)
3232import Data.Maybe (isJust)
3333import Data.Word (Word8)
34−import Streamly.Internal.Data.Stream (Stream)
34+import Streamly.Internal.Data.Stream.StreamD (Stream)
35+import Streamly.Internal.Data.Stream.StreamK (StreamK)
3536import System.IO (Handle)
3637import System.Random (randomRIO)
3738import Prelude hiding ()
3839
40+import qualified Streamly.Data.Stream as Stream
3941import qualified Streamly.Internal.Data.Array as Array
4042import qualified Streamly.Internal.Data.Stream.Chunked as ArrayStream
4143import qualified Streamly.Internal.Data.Fold as Fold
4244import qualified Streamly.Internal.Data.Parser as Parser
43−import qualified Streamly.Internal.Data.Stream as S
44−import qualified Streamly.Internal.Data.Stream.IsStream as Stream
45+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
46+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
4547import qualified Streamly.Internal.FileSystem.Handle as Handle
4648import qualified Streamly.Internal.Unicode.Stream as Unicode
4749
@@ -63,8 +65,8 @@ import Test.Inspection
6365-- XXX these can be moved to the common module
6466
6567{-# INLINE sourceUnfoldrM #-}
66−sourceUnfoldrM :: MonadIO m => Int -> Int -> Stream m Int
67−sourceUnfoldrM value n = S.unfoldrM step n
68+sourceUnfoldrM :: MonadIO m => Int -> Int -> Stream.Stream m Int
69+sourceUnfoldrM value n = Stream.unfoldrM step n
6870 where
6971 step cnt =
7072 if cnt > n + value
@@ -100,7 +102,7 @@ inspect $ 'toChunksLast `hasNoType` ''Step
100102toChunksSumLengths :: Handle -> IO Int
101103toChunksSumLengths inh =
102104 let s = Handle.readChunks inh
103− in Stream.sum (Stream.map Array.length s)
105+ in Stream.fold Fold.sum (Stream.map Array.length s)
104106
105107#ifdef INSPECTION
106108inspect $ hasNoTypeClasses 'toChunksSumLengths
@@ -135,7 +137,9 @@ inspect $ hasNoTypeClasses 'toChunksDecodeUtf8Arrays
135137-- | Count the number of lines in a file.
136138toChunksSplitOnSuffix :: Handle -> IO Int
137139toChunksSplitOnSuffix =
138− Stream.length . ArrayStream.splitOnSuffix 10 . Handle.readChunks
140+ Stream.fold Fold.length
141+ . ArrayStream.splitOnSuffix 10
142+ . Handle.readChunks
139143
140144#ifdef INSPECTION
141145inspect $ hasNoTypeClasses 'toChunksSplitOnSuffix
@@ -145,7 +149,10 @@ inspect $ 'toChunksSplitOnSuffix `hasNoType` ''Step
145149-- XXX use a word splitting combinator instead of splitOn and test it.
146150-- | Count the number of words in a file.
147151toChunksSplitOn :: Handle -> IO Int
148−toChunksSplitOn = Stream.length . ArrayStream.splitOn 32 . Handle.readChunks
152+toChunksSplitOn =
153+ Stream.fold Fold.length
154+ . ArrayStream.splitOn 32
155+ . Handle.readChunks
149156
150157#ifdef INSPECTION
151158inspect $ hasNoTypeClasses 'toChunksSplitOn
@@ -183,8 +190,7 @@ o_1_space_read_chunked env =
183190copyChunksSplitInterposeSuffix :: Handle -> Handle -> IO ()
184191copyChunksSplitInterposeSuffix inh outh =
185192 Stream.fold (Handle.write outh)
186− $ ArrayStream.interposeSuffix 10
187− $ ArrayStream.splitOnSuffix 10
193+ $ ArrayStream.interposeSuffix 10 . ArrayStream.splitOnSuffix 10
188194 $ Handle.readChunks inh
189195
190196#ifdef INSPECTION
@@ -197,9 +203,8 @@ inspect $ 'copyChunksSplitInterposeSuffix `hasNoType` ''Step
197203copyChunksSplitInterpose :: Handle -> Handle -> IO ()
198204copyChunksSplitInterpose inh outh =
199205 Stream.fold (Handle.write outh)
200− $ ArrayStream.interpose 32
201206 -- XXX this is not correct word splitting combinator
202− $ ArrayStream.splitOn 32
207+ $ ArrayStream.interpose 32 . ArrayStream.splitOn 32
203208 $ Handle.readChunks inh
204209
205210#ifdef INSPECTION
@@ -231,25 +236,26 @@ drainWhile p = Parser.takeWhile p Fold.drain
231236
232237{-# INLINE fold #-}
233238fold :: Stream IO (Array.Array Int) -> IO ()
234−fold s = void $ ArrayStream.foldBreak Fold.drain s
239+fold s = void $ ArrayStream.foldBreak Fold.drain $ StreamK.fromStream s
235240
236241{-# INLINE parse #-}
237242parse :: Int -> Stream IO (Array.Array Int) -> IO ()
238−parse value s = void $ ArrayStream.parseBreak (drainWhile (< value)) s
243+parse value s =
244+ void $ ArrayStream.parseBreak (drainWhile (< value)) $ StreamK.fromStream s
239245
240246{-# INLINE foldBreak #-}
241−foldBreak :: Stream IO (Array.Array Int) -> IO ()
247+foldBreak :: StreamK IO (Array.Array Int) -> IO ()
242248foldBreak s = do
243249 (r, s1) <- ArrayStream.foldBreak Fold.one s
244250 when (isJust r) $ foldBreak s1
245251
246252{-# INLINE parseBreak #-}
247−parseBreak :: Stream IO (Array.Array Int) -> IO ()
253+parseBreak :: StreamK IO (Array.Array Int) -> IO ()
248254parseBreak s = do
249− r <- try $ ArrayStream.parseBreak Parser.one s
255+ r <- ArrayStream.parseBreak Parser.one s
250256 case r of
251− Left (_ :: SomeException) -> return ()
252− Right (_, s1) -> parseBreak s1
257+ (Left _, _) -> return ()
258+ (Right _, s1) -> parseBreak s1
253259
254260o_1_space_serial_array ::
255261 Int -> [Array.Array Int] -> [Array.Array Int] -> [Benchmark]
@@ -259,7 +265,7 @@ o_1_space_serial_array bound arraysSmall arraysBig =
259265 , benchIO
260266 "foldBreak (recursive, small arrays)"
261267 (\_ -> Stream.fromList arraysSmall)
262− foldBreak
268+ (foldBreak . StreamK.fromStream)
263269 , benchIO "parse (of 100)" (\_ -> Stream.fromList arraysSmall)
264270 $ parse bound
265271 , benchIO "parse (single)" (\_ -> Stream.fromList arraysBig)
@@ -267,7 +273,7 @@ o_1_space_serial_array bound arraysSmall arraysBig =
267273 , benchIO
268274 "parseBreak (recursive, small arrays)"
269275 (\_ -> Stream.fromList arraysSmall)
270− parseBreak
276+ (parseBreak . StreamK.fromStream)
271277 ]
272278
273279-------------------------------------------------------------------------------
benchmark/Streamly/Benchmark/Data/Fold.hs+2−2
@@ -25,7 +25,7 @@ import Data.IntMap.Strict (IntMap)
2525import Data.Monoid (Last(..), Sum(..))
2626import System.Random (randomRIO)
2727
28−import Streamly.Internal.Data.Stream (Stream)
28+import Streamly.Internal.Data.Stream.StreamD (Stream)
2929import Streamly.Internal.Data.Fold (Fold(..))
3030import Streamly.Internal.Data.IsMap.HashMap ()
3131
@@ -34,7 +34,7 @@ import qualified Streamly.Internal.Data.Fold as FL
3434import qualified Streamly.Internal.Data.Fold.Container as FL
3535import qualified Streamly.Internal.Data.Unfold as Unfold
3636import qualified Streamly.Internal.Data.Pipe as Pipe
37−import qualified Streamly.Internal.Data.Stream as Stream
37+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
3838
3939import Gauge
4040import Streamly.Benchmark.Common
benchmark/Streamly/Benchmark/Data/Fold/Window.hs+11−8
@@ -4,25 +4,25 @@ module Main (main) where
44
55import Control.DeepSeq (NFData)
66import Streamly.Data.Fold (Fold)
7−import Streamly.Internal.Data.Stream (Stream)
7+import Streamly.Internal.Data.Stream.StreamD (Stream)
88import System.Random (randomRIO)
99
1010import qualified Streamly.Data.Fold as Fold
1111import qualified Streamly.Internal.Data.Ring.Unboxed as Ring
12−import qualified Streamly.Prelude as Stream
1312import qualified Streamly.Internal.Data.Fold.Window as Window
13+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
1414
1515import Gauge
1616
1717{-# INLINE source #-}
18−source :: (Monad m, Stream.IsStream t, Num a, Stream.Enumerable a) =>
19− Int -> a -> t m a
18+source :: (Monad m, Num a, Stream.Enumerable a) =>
19+ Int -> a -> Stream m a
2020source len from =
2121 Stream.enumerateFromThenTo from (from + 1) (from + fromIntegral len)
2222
2323{-# INLINE sourceDescending #-}
24−sourceDescending :: (Monad m, Stream.IsStream t, Num a, Stream.Enumerable a) =>
25− Int -> a -> t m a
24+sourceDescending :: (Monad m, Num a, Stream.Enumerable a) =>
25+ Int -> a -> Stream m a
2626sourceDescending len from =
2727 Stream.enumerateFromThenTo
2828 (from + fromIntegral len)
@@ -30,7 +30,7 @@ sourceDescending len from =
3030 from
3131
3232{-# INLINE sourceDescendingInt #-}
33−sourceDescendingInt :: (Monad m, Stream.IsStream t) => Int -> Int -> t m Int
33+sourceDescendingInt :: Monad m => Int -> Int -> Stream m Int
3434sourceDescendingInt = sourceDescending
3535
3636{-# INLINE benchWith #-}
@@ -56,7 +56,10 @@ benchScanWith src len name f =
5656 bench name
5757 $ nfIO
5858 $ randomRIO (1, 1 :: Int)
59− >>= Stream.drain . Stream.postscan f . src len . fromIntegral
59+ >>= Stream.fold Fold.drain
60+ . Stream.postscan f
61+ . src len
62+ . fromIntegral
6063
6164{-# INLINE benchWithPostscan #-}
6265benchWithPostscan :: Int -> String -> Fold IO Double a -> Benchmark
benchmark/Streamly/Benchmark/Data/Parser.hs+4−15
@@ -20,13 +20,13 @@ module Main
2020import Control.DeepSeq (NFData(..))
2121import Data.Foldable (asum)
2222import Data.Functor (($>))
23−import Data.Maybe (fromMaybe)
2423import Data.Monoid (Sum(..))
2524import GHC.Magic (inline)
2625import GHC.Magic (noinline)
2726import System.IO (Handle)
2827import System.Random (randomRIO)
2928import Streamly.Internal.Data.Parser (ParseError(..))
29+import Streamly.Internal.Data.Stream.StreamD (Stream)
3030import Prelude hiding
3131 (any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
3232
@@ -37,13 +37,12 @@ import qualified Streamly.FileSystem.Handle as Handle
3737import qualified Streamly.Internal.Data.Array as Array
3838import qualified Streamly.Internal.Data.Fold as Fold
3939import qualified Streamly.Internal.Data.Parser as PR
40−import qualified Streamly.Internal.Data.Stream as Stream
41−import qualified Streamly.Internal.Data.Stream.IsStream as IsStream (tail)
40+import qualified Streamly.Data.Stream as Stream
4241import qualified Streamly.Internal.Data.Producer as Producer
4342import qualified Streamly.Internal.Data.Producer.Source as Source
43+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
4444
4545import Gauge hiding (env)
46−import Streamly.Internal.Data.Stream (Stream)
4746import Streamly.Benchmark.Common
4847import Streamly.Benchmark.Common.Handle
4948
@@ -141,8 +140,7 @@ dropWhile value = Stream.parse (PR.dropWhile (<= value))
141140{-# INLINE takeStartBy #-}
142141takeStartBy :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
143142takeStartBy value stream = do
144− stream1 <- return . fromMaybe (Stream.fromPure (value + 1)) =<< IsStream.tail stream
145− let stream2 = value `Stream.cons` stream1
143+ let stream2 = value `Stream.cons` stream
146144 Stream.parse (PR.takeStartBy (== value) Fold.drain) stream2
147145
148146takeFramedByEsc_ :: Monad m => Int -> Stream m Char -> m (Either ParseError ())
@@ -429,14 +427,6 @@ parseIterate n =
429427 (Sum 0)
430428 . fmap Sum
431429
432−{-# INLINE parseBreak #-}
433−parseBreak :: Monad m => Stream m Int -> m ()
434−parseBreak s = do
435− r <- Stream.parseBreak PR.one s
436− case r of
437− (Left _, _) -> return ()
438− (Right _, s1) -> parseBreak s1
439−
440430{-# INLINE concatSequence #-}
441431concatSequence :: Monad m => Stream m Int -> m (Either ParseError ())
442432concatSequence = Stream.parse $ PR.concatSequence Fold.drain $ Stream.repeat PR.one
@@ -490,7 +480,6 @@ o_1_space_serial value =
490480 , benchIOSink value "shortest" $ shortestAllAny value
491481 , benchIOSink value "longest" $ longestAllAny value
492482 -}
493− , benchIOSink value "parseBreak (recursive)" parseBreak
494483 , benchIOSink value "parseMany (take 1)" (parseMany 1)
495484 , benchIOSink value "parseMany (take all)" (parseMany value)
496485 , benchIOSink value "parseIterate (take 1)" (parseIterate 1)
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+8−9
@@ -21,9 +21,8 @@ import Control.DeepSeq (NFData(..))
2121import Data.Foldable (asum)
2222import Data.Function ((&))
2323import Data.Functor (($>))
24−import Data.Maybe (fromMaybe)
25−import Streamly.Internal.Data.Stream (Stream)
2624import Streamly.Internal.Data.Parser (ParseError(..))
25+import Streamly.Internal.Data.Stream.StreamD (Stream)
2726import System.Random (randomRIO)
2827import Prelude hiding (any, all, take, sequence, sequenceA, sequence_, takeWhile, span)
2928
@@ -35,9 +34,8 @@ import qualified Streamly.Internal.Data.Fold as Fold
3534import qualified Streamly.Internal.Data.Parser.ParserD as PR
3635import qualified Streamly.Internal.Data.Producer as Producer
3736import qualified Streamly.Internal.Data.Producer.Source as Source
38−import qualified Streamly.Internal.Data.Stream as Stream
39−import qualified Streamly.Internal.Data.Stream.IsStream as IsStream (tail)
40−import qualified Streamly.Internal.Data.Stream.StreamD as D
37+import qualified Streamly.Data.Stream as Stream
38+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
4139
4240import Gauge
4341import Streamly.Benchmark.Common
@@ -75,7 +73,7 @@ listEqBy len = Stream.parseD (PR.listEqBy (==) [1 .. len])
7573
7674{-# INLINE streamEqBy #-}
7775streamEqBy :: Int -> Stream IO Int -> IO (Either ParseError ())
78−streamEqBy len = Stream.parseD (PR.streamEqBy (==) (D.enumerateFromToIntegral 1 len))
76+streamEqBy len = Stream.parseD (PR.streamEqBy (==) (Stream.enumerateFromToIntegral 1 len))
7977
8078{-# INLINE drainWhile #-}
8179drainWhile :: Monad m => (a -> Bool) -> PR.Parser a m ()
@@ -84,8 +82,7 @@ drainWhile p = PR.takeWhile p Fold.drain
8482{-# INLINE takeStartBy #-}
8583takeStartBy :: Monad m => Int -> Stream m Int -> m (Either ParseError ())
8684takeStartBy value stream = do
87− stream1 <- return . fromMaybe (Stream.fromPure (value + 1)) =<< IsStream.tail stream
88− let stream2 = value `Stream.cons` stream1
85+ let stream2 = value `Stream.cons` stream
8986 Stream.parseD (PR.takeStartBy (== value) Fold.drain) stream2
9087
9188{-# INLINE takeWhile #-}
@@ -202,7 +199,9 @@ longestAllAny value =
202199
203200{-# INLINE sequenceParser #-}
204201sequenceParser :: Monad m => Stream m Int -> m (Either ParseError ())
205−sequenceParser = Stream.parseD (PR.sequence (D.repeat (PR.satisfy $ const True)) Fold.drain)
202+sequenceParser =
203+ Stream.parseD
204+ (PR.sequence (Stream.repeat (PR.satisfy $ const True)) Fold.drain)
206205
207206-------------------------------------------------------------------------------
208207-- Spanning
benchmark/Streamly/Benchmark/Data/Parser/ParserK.hs+3−3
@@ -17,7 +17,7 @@ module Main
1717import Control.DeepSeq (NFData(..))
1818import Data.Foldable (asum)
1919import Streamly.Internal.Data.Parser (ParseError(..))
20−import Streamly.Internal.Data.Stream (Stream)
20+import Streamly.Internal.Data.Stream.StreamD (Stream)
2121import System.Random (randomRIO)
2222import Prelude hiding (any, all, take, sequence, sequenceA, takeWhile)
2323
@@ -27,7 +27,7 @@ import qualified Data.Traversable as TR
2727import qualified Streamly.Internal.Data.Fold as FL
2828import qualified Streamly.Internal.Data.Parser.ParserK.Type as PR
2929import qualified Streamly.Internal.Data.Parser.ParserD as PRD
30−import qualified Streamly.Internal.Data.Stream as Stream
30+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
3131
3232import Gauge
3333import Streamly.Benchmark.Common
@@ -62,7 +62,7 @@ benchIOSink value name f =
6262#ifdef FROM_PARSERK
6363#define PARSE_OP (Stream.parseD . PRD.fromParserK)
6464#else
65−#define PARSE_OP Stream.parseK
65+#define PARSE_OP Stream.parse
6666#endif
6767
6868{-# INLINE satisfy #-}
benchmark/Streamly/Benchmark/Data/Stream.hs+8
@@ -14,7 +14,9 @@ module Main (main) where
1414import Streamly.Benchmark.Common.Handle (mkHandleBenchEnv)
1515
1616import qualified Stream.Eliminate as Elimination
17+#ifndef USE_STREAMLY_CORE
1718import qualified Stream.Exceptions as Exceptions
19+#endif
1820import qualified Stream.Expand as NestedStream
1921import qualified Stream.Generate as Generation
2022import qualified Stream.Lift as Lift
@@ -27,7 +29,11 @@ import qualified Stream.Transform as Transformation
2729import Streamly.Benchmark.Common
2830
2931moduleName :: String
32+#ifdef USE_STREAMK
33+moduleName = "Data.Stream.StreamDK"
34+#else
3035moduleName = "Data.Stream"
36+#endif
3137
3238-------------------------------------------------------------------------------
3339-- Main
@@ -46,7 +52,9 @@ main = do
4652 allBenchmarks env size = Prelude.concat
4753 [ Generation.benchmarks moduleName size
4854 , Elimination.benchmarks moduleName size
55+#ifndef USE_STREAMLY_CORE
4956 , Exceptions.benchmarks moduleName env size
57+#endif
5058#ifdef USE_PRELUDE
5159 , Split.benchmarks moduleName env
5260#endif
benchmark/Streamly/Benchmark/Data/Stream/Common.hs+122−62
@@ -16,6 +16,9 @@
1616module Stream.Common
1717 ( MonadAsync
1818
19+ , fromStream
20+ , toStream
21+
1922 -- Generation
2023 , fromListM
2124 , fromFoldableM
@@ -41,8 +44,10 @@ module Stream.Common
4144 , benchIO
4245
4346 -- Benchmarking functions
47+#ifdef USE_STREAMK
4448 , concatStreamsWith
4549 , mergeMapWith
50+#endif
4651 , apDiscardFst
4752 , apDiscardSnd
4853 , apLiftA2
@@ -70,8 +75,6 @@ import Control.Applicative (liftA2)
7075import Control.DeepSeq (NFData)
7176import Control.Exception (try)
7277import GHC.Exception (ErrorCall)
73−import Streamly.Internal.Data.Stream (Stream)
74−import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
7578import System.Random (randomRIO)
7679
7780import qualified Streamly.Internal.Data.Fold as Fold
@@ -81,42 +84,97 @@ import qualified Streamly.Internal.Data.Pipe as Pipe
8184import Streamly.Prelude (foldl', scanl')
8285import qualified Streamly.Internal.Data.Stream.IsStream as Stream
8386import qualified Streamly.Prelude as Stream
87+import qualified Streamly.Prelude as StreamK
8488import Streamly.Benchmark.Prelude
8589 ( composeN, sourceConcatMapId, benchIOSink
86− , concatStreamsWith, mergeMapWith
90+ , concatStreamsWith
8791 )
8892#else
89−import qualified Streamly.Internal.Data.Stream as Stream
93+import Streamly.Internal.Data.Stream.StreamD (Stream)
94+import qualified Streamly.Internal.Data.Stream.StreamD as D
95+#ifdef USE_STREAMK
96+import Streamly.Internal.Data.Stream.StreamK (StreamK)
97+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
98+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
99+#else
100+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
101+import qualified Streamly.Internal.Data.Stream.StreamD as StreamK
102+#endif
90103#endif
91104
92105import Gauge
93106import Prelude hiding (mapM, replicate)
94107
108+#ifdef USE_STREAMK
109+toStream :: Applicative m => StreamK m a -> Stream m a
110+toStream = StreamK.toStream
111+
112+fromStream :: Monad m => Stream m a -> StreamK m a
113+fromStream = StreamK.fromStream
114+#else
115+fromStream :: a -> a
116+fromStream = id
117+
118+toStream :: a -> a
119+toStream = id
120+#endif
121+
95122#ifdef USE_PRELUDE
123+type Stream = Stream.SerialT
96124type MonadAsync m = Stream.MonadAsync m
125+mkCross = id
126+unCross = id
97127#else
98128type MonadAsync = Monad
129+
130+#ifdef USE_STREAMK
131+mkCross :: StreamK.Stream m a -> StreamK.CrossStreamK m a
132+mkCross = StreamK.mkCross
133+
134+unCross :: StreamK.CrossStreamK m a -> StreamK.Stream m a
135+unCross = StreamK.unCross
136+#else
137+mkCross :: Stream m a -> Stream.CrossStream m a
138+mkCross = Stream.mkCross
139+
140+unCross :: Stream.CrossStream m a -> Stream m a
141+unCross = Stream.unCross
142+#endif
99143#endif
100144
101−{-# INLINE append #-}
102−append :: Stream m a -> Stream m a -> Stream m a
103145#ifdef USE_PRELUDE
146+{-# INLINE append #-}
147+append :: Monad m => Stream m a -> Stream m a -> Stream m a
104148append = Stream.serial
105149#else
150+#ifdef USE_STREAMK
151+append :: StreamK m a -> StreamK m a -> StreamK m a
152+append = StreamK.append
153+#else
154+append :: Monad m => Stream m a -> Stream m a -> Stream m a
106155append = Stream.append
107156#endif
157+#endif
108158
109159{-# INLINE append2 #-}
110160append2 :: Monad m => Stream m a -> Stream m a -> Stream m a
111161#ifdef USE_PRELUDE
112162append2 = Stream.append
113163#else
114−append2 = Stream.append2
164+append2 = D.append
115165#endif
116166
117167{-# INLINE drain #-}
118168drain :: Monad m => Stream m a -> m ()
169+{-# INLINE toList #-}
170+toList :: Monad m => Stream m a -> m [a]
171+#ifdef USE_PRELUDE
172+drain = Stream.drain
173+toList = Stream.toList
174+#else
119175drain = Stream.fold Fold.drain
176+toList = Stream.fold Fold.toList
177+#endif
120178
121179{-# INLINE fromListM #-}
122180fromListM :: MonadAsync m => [m a] -> Stream m a
@@ -131,7 +189,7 @@ fromFoldableM :: MonadAsync m => [m a] -> Stream m a
131189#ifdef USE_PRELUDE
132190fromFoldableM = Stream.fromFoldableM
133191#else
134−fromFoldableM = Stream.sequence . Stream.fromFoldable
192+fromFoldableM = Stream.sequence . toStream . StreamK.fromFoldable
135193#endif
136194
137195{-# INLINE sourceUnfoldrM #-}
@@ -168,8 +226,8 @@ sourceUnfoldrAction value n = Stream.unfoldr step n
168226 else Just (return cnt, cnt + 1)
169227
170228{-# INLINE sourceFromFoldable #-}
171−sourceFromFoldable :: Int -> Int -> Stream m Int
172−sourceFromFoldable value n = Stream.fromFoldable [n..n+value]
229+sourceFromFoldable :: Monad m => Int -> Int -> Stream m Int
230+sourceFromFoldable value n = toStream $ StreamK.fromFoldable [n..n+value]
173231
174232#ifndef USE_PRELUDE
175233{-# INLINE benchIOSink #-}
@@ -194,43 +252,45 @@ benchIO :: (NFData b) => String -> (Int -> IO b) -> Benchmark
194252benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
195253
196254#ifndef USE_PRELUDE
255+#ifdef USE_STREAMK
197256{-# INLINE concatStreamsWith #-}
198257concatStreamsWith
199− :: (Stream IO Int -> Stream IO Int -> Stream IO Int)
258+ :: (StreamK IO Int -> StreamK IO Int -> StreamK IO Int)
200259 -> Int
201260 -> Int
202261 -> Int
203262 -> IO ()
204263concatStreamsWith op outer inner n =
205− drain $ Stream.concatMapWith op
206− (sourceUnfoldrM inner)
207− (sourceUnfoldrM outer n)
264+ drain $ toStream $ StreamK.concatMapWith op
265+ (fromStream . sourceUnfoldrM inner)
266+ (fromStream $ sourceUnfoldrM outer n)
208267
209268{-# INLINE mergeMapWith #-}
210269mergeMapWith
211− :: (Stream IO Int -> Stream IO Int -> Stream IO Int)
270+ :: (StreamK IO Int -> StreamK IO Int -> StreamK IO Int)
212271 -> Int
213272 -> Int
214273 -> Int
215274 -> IO ()
216275mergeMapWith op outer inner n =
217− drain $ Stream.mergeMapWith op
218− (sourceUnfoldrM inner)
219− (sourceUnfoldrM outer n)
276+ drain $ toStream $ StreamK.mergeMapWith op
277+ (fromStream . sourceUnfoldrM inner)
278+ (fromStream $ sourceUnfoldrM outer n)
279+#endif
220280
221281{-# INLINE sourceConcatMapId #-}
222282sourceConcatMapId :: (Monad m)
223283 => Int -> Int -> Stream m (Stream m Int)
224284sourceConcatMapId value n =
225− Stream.fromFoldable $ fmap (Stream.fromEffect . return) [n..n+value]
285+ Stream.fromList $ fmap (D.fromEffect . return) [n..n+value]
226286#endif
227287
228288{-# INLINE apDiscardFst #-}
229289apDiscardFst :: MonadAsync m =>
230290 Int -> Int -> m ()
231−apDiscardFst linearCount start = drain $ unCrossStream $
232− CrossStream (sourceUnfoldrM nestedCount2 start)
233− *> CrossStream (sourceUnfoldrM nestedCount2 start)
291+apDiscardFst linearCount start = drain $ toStream $ unCross $
292+ mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
293+ *> mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
234294
235295 where
236296
@@ -238,9 +298,9 @@ apDiscardFst linearCount start = drain $ unCrossStream $
238298
239299{-# INLINE apDiscardSnd #-}
240300apDiscardSnd :: MonadAsync m => Int -> Int -> m ()
241−apDiscardSnd linearCount start = drain $ unCrossStream $
242− CrossStream (sourceUnfoldrM nestedCount2 start)
243− <* CrossStream (sourceUnfoldrM nestedCount2 start)
301+apDiscardSnd linearCount start = drain $ toStream $ unCross $
302+ mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
303+ <* mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
244304
245305 where
246306
@@ -248,9 +308,9 @@ apDiscardSnd linearCount start = drain $ unCrossStream $
248308
249309{-# INLINE apLiftA2 #-}
250310apLiftA2 :: MonadAsync m => Int -> Int -> m ()
251−apLiftA2 linearCount start = drain $ unCrossStream $
252− liftA2 (+) (CrossStream (sourceUnfoldrM nestedCount2 start))
253− (CrossStream (sourceUnfoldrM nestedCount2 start))
311+apLiftA2 linearCount start = drain $ toStream $ unCross $
312+ liftA2 (+) (mkCross (fromStream $ sourceUnfoldrM nestedCount2 start))
313+ (mkCross (fromStream $ sourceUnfoldrM nestedCount2 start))
254314
255315 where
256316
@@ -258,9 +318,9 @@ apLiftA2 linearCount start = drain $ unCrossStream $
258318
259319{-# INLINE toNullAp #-}
260320toNullAp :: MonadAsync m => Int -> Int -> m ()
261−toNullAp linearCount start = drain $ unCrossStream $
262− (+) <$> CrossStream (sourceUnfoldrM nestedCount2 start)
263− <*> CrossStream (sourceUnfoldrM nestedCount2 start)
321+toNullAp linearCount start = drain $ toStream $ unCross $
322+ (+) <$> mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
323+ <*> mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
264324
265325 where
266326
@@ -268,9 +328,9 @@ toNullAp linearCount start = drain $ unCrossStream $
268328
269329{-# INLINE monadThen #-}
270330monadThen :: MonadAsync m => Int -> Int -> m ()
271−monadThen linearCount start = drain $ unCrossStream $ do
272− CrossStream (sourceUnfoldrM nestedCount2 start) >>
273− CrossStream (sourceUnfoldrM nestedCount2 start)
331+monadThen linearCount start = drain $ toStream $ unCross $ do
332+ mkCross (fromStream $ sourceUnfoldrM nestedCount2 start) >>
333+ mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
274334
275335 where
276336
@@ -278,9 +338,9 @@ monadThen linearCount start = drain $ unCrossStream $ do
278338
279339{-# INLINE toNullM #-}
280340toNullM :: MonadAsync m => Int -> Int -> m ()
281−toNullM linearCount start = drain $ unCrossStream $ do
282− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
283− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
341+toNullM linearCount start = drain $ toStream $ unCross $ do
342+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
343+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
284344 return $ x + y
285345
286346 where
@@ -289,56 +349,56 @@ toNullM linearCount start = drain $ unCrossStream $ do
289349
290350{-# INLINE toNullM3 #-}
291351toNullM3 :: MonadAsync m => Int -> Int -> m ()
292−toNullM3 linearCount start = drain $ unCrossStream $ do
293− x <- CrossStream (sourceUnfoldrM nestedCount3 start)
294− y <- CrossStream (sourceUnfoldrM nestedCount3 start)
295− z <- CrossStream (sourceUnfoldrM nestedCount3 start)
352+toNullM3 linearCount start = drain $ toStream $ unCross $ do
353+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount3 start)
354+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount3 start)
355+ z <- mkCross (fromStream $ sourceUnfoldrM nestedCount3 start)
296356 return $ x + y + z
297357 where
298358 nestedCount3 = round (fromIntegral linearCount**(1/3::Double))
299359
300360{-# INLINE filterAllOutM #-}
301361filterAllOutM :: MonadAsync m => Int -> Int -> m ()
302−filterAllOutM linearCount start = drain $ unCrossStream $ do
303− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
304− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
362+filterAllOutM linearCount start = drain $ toStream $ unCross $ do
363+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
364+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
305365 let s = x + y
306366 if s < 0
307367 then return s
308− else CrossStream Stream.nil
368+ else mkCross StreamK.nil
309369 where
310370 nestedCount2 = round (fromIntegral linearCount**(1/2::Double))
311371
312372{-# INLINE filterAllInM #-}
313373filterAllInM :: MonadAsync m => Int -> Int -> m ()
314−filterAllInM linearCount start = drain $ unCrossStream $ do
315− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
316− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
374+filterAllInM linearCount start = drain $ toStream $ unCross $ do
375+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
376+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
317377 let s = x + y
318378 if s > 0
319379 then return s
320− else CrossStream Stream.nil
380+ else mkCross StreamK.nil
321381 where
322382 nestedCount2 = round (fromIntegral linearCount**(1/2::Double))
323383
324384{-# INLINE filterSome #-}
325385filterSome :: MonadAsync m => Int -> Int -> m ()
326−filterSome linearCount start = drain $ unCrossStream $ do
327− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
328− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
386+filterSome linearCount start = drain $ toStream $ unCross $ do
387+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
388+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
329389 let s = x + y
330390 if s > 1100000
331391 then return s
332− else CrossStream Stream.nil
392+ else mkCross StreamK.nil
333393 where
334394 nestedCount2 = round (fromIntegral linearCount**(1/2::Double))
335395
336396{-# INLINE breakAfterSome #-}
337397breakAfterSome :: Int -> Int -> IO ()
338398breakAfterSome linearCount start = do
339− (_ :: Either ErrorCall ()) <- try $ drain $ unCrossStream $ do
340− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
341− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
399+ (_ :: Either ErrorCall ()) <- try $ drain $ toStream $ unCross $ do
400+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
401+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
342402 let s = x + y
343403 if s > 1100000
344404 then error "break"
@@ -349,9 +409,9 @@ breakAfterSome linearCount start = do
349409
350410{-# INLINE toListM #-}
351411toListM :: MonadAsync m => Int -> Int -> m [Int]
352−toListM linearCount start = Stream.fold Fold.toList $ unCrossStream $ do
353− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
354− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
412+toListM linearCount start = toList $ toStream $ unCross $ do
413+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
414+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
355415 return $ x + y
356416 where
357417 nestedCount2 = round (fromIntegral linearCount**(1/2::Double))
@@ -361,9 +421,9 @@ toListM linearCount start = Stream.fold Fold.toList $ unCrossStream $ do
361421{-# INLINE toListSome #-}
362422toListSome :: MonadAsync m => Int -> Int -> m [Int]
363423toListSome linearCount start =
364− Stream.fold Fold.toList $ Stream.take 10000 $ unCrossStream $ do
365− x <- CrossStream (sourceUnfoldrM nestedCount2 start)
366− y <- CrossStream (sourceUnfoldrM nestedCount2 start)
424+ toList $ Stream.take 10000 $ toStream $ unCross $ do
425+ x <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
426+ y <- mkCross (fromStream $ sourceUnfoldrM nestedCount2 start)
367427 return $ x + y
368428 where
369429 nestedCount2 = round (fromIntegral linearCount**(1/2::Double))
benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs+56−38
@@ -32,39 +32,41 @@ import System.Random (randomRIO)
3232
3333import qualified Data.Foldable as F
3434import qualified GHC.Exts as GHC
35+import qualified Streamly.Internal.Data.Fold as Fold
3536
3637#ifdef INSPECTION
3738import GHC.Types (SPEC(..))
3839import Test.Inspection
39−
4040import qualified Streamly.Internal.Data.Stream.StreamD as D
4141#endif
42−import qualified Streamly.Internal.Data.Fold as Fold
43−#ifdef USE_PRELUDE
44−import qualified Streamly.Internal.Data.Stream.IsStream as S
45−#else
46−import qualified Streamly.Internal.Data.Stream as S
47−#endif
4842
49−import Gauge
50−import Streamly.Internal.Data.Stream (Stream)
5143#ifdef USE_PRELUDE
5244import Streamly.Prelude (fromSerial)
5345import Streamly.Benchmark.Prelude
46+import qualified Streamly.Internal.Data.Stream.IsStream as S
47+import qualified Streamly.Internal.Data.Stream.IsStream as StreamK
5448#else
5549import Stream.Common
56− ( sourceUnfoldr
57− , sourceUnfoldrM
58− , sourceUnfoldrAction
59− , benchIOSink
60− )
50+import Streamly.Internal.Data.Stream.StreamD (Stream)
51+import qualified Streamly.Internal.Data.Stream.StreamD as S
52+#ifdef USE_STREAMK
53+import Streamly.Internal.Data.Stream.StreamK (StreamK)
54+import qualified Streamly.Data.Stream.StreamK as StreamK
55+#else
56+import qualified Streamly.Internal.Data.Stream.StreamD as StreamK
6157#endif
58+#endif
59+
60+import Gauge
6261import Streamly.Benchmark.Common
6362import Prelude hiding (length, sum, or, and, any, all, notElem, elem, (!!),
6463 lookup, repeat, minimum, maximum, product, last, mapM_, init)
6564import qualified Prelude
6665
6766#ifdef USE_PRELUDE
67+type Stream = S.SerialT
68+fromStream = id
69+
6870{-# INLINE repeat #-}
6971repeat :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
7072repeat count = S.take count . S.repeat
@@ -213,6 +215,9 @@ o_1_space_elimination_foldable value =
213215 , bench "minimum" $ nf (foldableMin value) 1
214216 , benchPureSink value "min (ord)" ordInstanceMin
215217 , bench "maximum" $ nf (foldableMax value) 1
218+ , bench "minimumBy" $ nf (`foldableMinBy` 1) value
219+ , bench "maximumBy" $ nf (`foldableMaxBy` 1) value
220+ , bench "minimumByList" $ nf (`foldableListMinBy` 1) value
216221 , bench "length . toList"
217222 $ nf (Prelude.length . foldableToList value) 1
218223 , bench "notElem" $ nf (foldableNotElem value) 1
@@ -235,28 +240,15 @@ o_1_space_elimination_foldable value =
235240 ]
236241 ]
237242
238−o_n_space_elimination_foldable :: Int -> [Benchmark]
239−o_n_space_elimination_foldable value =
240− -- Head recursive strict right folds.
241− [ bgroup "foldl"
242− -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
243− -- foldl1 which does not work in constant memory for our
244− -- implementation. It works in constant memory for lists but even for
245− -- lists it takes 15x more time compared to our foldl' based
246− -- implementation.
247− [ bench "minimumBy" $ nf (`foldableMinBy` 1) value
248− , bench "maximumBy" $ nf (`foldableMaxBy` 1) value
249− , bench "minimumByList" $ nf (`foldableListMinBy` 1) value
250− ]
251− ]
252−
253243-------------------------------------------------------------------------------
254244-- Stream folds
255245-------------------------------------------------------------------------------
256246
247+#ifndef USE_PRELUDE
257248instance NFData a => NFData (Stream Identity a) where
258249 {-# INLINE rnf #-}
259250 rnf xs = runIdentity $ S.fold (Fold.foldl' (\_ x -> rnf x) ()) xs
251+#endif
260252
261253{-# INLINE benchPureSink #-}
262254benchPureSink :: NFData b
@@ -283,9 +275,13 @@ benchIdentitySink value name f = bench name $ nf (f . sourceUnfoldr value) 1
283275-------------------------------------------------------------------------------
284276
285277{-# INLINE uncons #-}
278+#ifdef USE_STREAMK
279+uncons :: Monad m => StreamK m Int -> m ()
280+#else
286281uncons :: Monad m => Stream m Int -> m ()
282+#endif
287283uncons s = do
288− r <- S.uncons s
284+ r <- StreamK.uncons s
289285 case r of
290286 Nothing -> return ()
291287 Just (_, t) -> uncons t
@@ -310,9 +306,15 @@ foldrMElem e =
310306 else xs)
311307 (return False)
312308
309+#ifdef USE_STREAMK
313310{-# INLINE foldrToStream #-}
314−foldrToStream :: Monad m => Stream m Int -> m (Stream Identity Int)
315−foldrToStream = S.foldr S.cons S.nil
311+foldrToStream :: Monad m => Stream m Int -> m (StreamK Identity Int)
312+foldrToStream = S.foldr StreamK.cons StreamK.nil
313+#else
314+-- {-# INLINE foldrToStream #-}
315+-- foldrToStream :: Monad m => Stream m Int -> m (Stream Identity Int)
316+-- foldrToStream = S.foldr S.cons S.nil
317+#endif
316318
317319{-# INLINE foldrMBuild #-}
318320foldrMBuild :: Monad m => Stream m Int -> m [Int]
@@ -456,17 +458,34 @@ o_1_space_elimination_folds value =
456458 ]
457459 , bgroup "Identity"
458460 [ benchIdentitySink value "foldrMElem" (foldrMElem value)
461+#ifdef USE_STREAMK
459462 , benchIdentitySink value "foldrToStreamLength"
463+ (S.fold Fold.length . toStream . runIdentity . foldrToStream)
464+ {-
465+ , benchIdentitySink 16 "foldrToStreamLength (16)"
466+ (S.fold Fold.length . toStream . runIdentity . foldrToStream)
467+ -}
468+#else
469+ {-
470+ , benchIdentitySink 16 "foldrToStreamLength (16)"
460471 (S.fold Fold.length . runIdentity . foldrToStream)
472+ -}
473+#endif
474+ {-
475+ , benchPureSink 16 "foldrMToListLength (16)"
476+ (Prelude.length . runIdentity . foldrMBuild)
477+ -}
461478 , benchPureSink value "foldrMToListLength"
462479 (Prelude.length . runIdentity . foldrMBuild)
463480 ]
464481 ]
465482
466483 -- deconstruction
467− , benchIOSink value "uncons" uncons
484+ , benchIOSink value "uncons" (uncons . fromStream)
485+#ifndef USE_PRELUDE
468486 , benchHoistSink value "length . generalizeInner"
469487 (S.fold Fold.length . S.generalizeInner)
488+#endif
470489#ifdef USE_PRELUDE
471490 , benchIOSink value "init" init
472491
@@ -587,8 +606,10 @@ o_n_space_elimination_toList value =
587606 [ bgroup "toList"
588607 -- Converting the stream to a list or pure stream in a strict monad
589608 [ benchIOSink value "toList" S.toList
609+#ifndef USE_PRELUDE
590610 , benchIOSink value "toStream"
591611 (S.toStream :: (Stream IO Int -> IO (Stream Identity Int)))
612+#endif
592613 ]
593614 ]
594615#endif
@@ -718,11 +739,8 @@ benchmarks moduleName size =
718739#ifdef USE_PRELUDE
719740 ++ o_n_heap_elimination_foldl size
720741 ++ o_n_heap_elimination_toList size
721−#endif
722− , bgroup (o_n_space_prefix moduleName) $
723− o_n_space_elimination_foldable size
724−#ifdef USE_PRELUDE
725742 ++ o_n_space_elimination_toList size
726743#endif
727− ++ o_n_space_elimination_foldr size
744+ , bgroup (o_n_space_prefix moduleName) $
745+ o_n_space_elimination_foldr size
728746 ]
benchmark/Streamly/Benchmark/Data/Stream/Exceptions.hs+36−28
@@ -27,25 +27,21 @@ module Stream.Exceptions (benchmarks) where
2727
2828import Control.Exception (Exception, throwIO)
2929import Stream.Common (drain)
30−import Streamly.Internal.Data.Stream (Stream)
3130
3231import qualified Data.IORef as Ref
3332import qualified Data.Map.Strict as Map
34−import qualified Stream.Common as Common
3533
36−#ifndef USE_STREAMLY_CORE
3734import Control.Exception (SomeException)
3835import System.IO (Handle, hClose, hPutChar)
3936import qualified Streamly.FileSystem.Handle as FH
4037import qualified Streamly.Internal.FileSystem.Handle as IFH
4138import qualified Streamly.Internal.Data.Unfold as IUF
4239import qualified Streamly.Internal.Data.Unfold.Exception as IUF
43−#endif
4440
4541#ifdef USE_PRELUDE
4642import qualified Streamly.Internal.Data.Stream.IsStream as Stream
4743#else
48−import qualified Streamly.Internal.Data.Stream as Stream
44+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
4945import qualified Streamly.Internal.Data.Stream.Exception.Lifted as Stream
5046#endif
5147
@@ -60,18 +56,35 @@ import Test.Inspection
6056import qualified Streamly.Internal.Data.Stream.StreamD as D
6157#endif
6258
63−-------------------------------------------------------------------------------
64−-- stream exceptions
65−-------------------------------------------------------------------------------
59+#ifdef USE_PRELUDE
60+type Stream = Stream.SerialT
61+toStreamD = Stream.toStream
62+fromStreamD = Stream.fromStream
63+#else
64+type Stream = Stream.Stream
65+toStreamD :: a -> a
66+toStreamD = id
67+fromStreamD :: a -> a
68+fromStreamD = id
69+#endif
6670
67−{-# INLINE replicateM #-}
68−replicateM :: Common.MonadAsync m => Int -> m a -> Stream m a
71+afterUnsafe :: IO b -> Stream IO a -> Stream IO a
72+finallyUnsafe :: IO b -> Stream IO a -> Stream IO a
73+bracketUnsafe :: IO b -> (b -> IO c) -> (b -> Stream IO a) -> Stream IO a
6974#ifdef USE_PRELUDE
70−replicateM = Stream.replicateM
75+afterUnsafe = Stream.after_
76+finallyUnsafe = Stream.finally_
77+bracketUnsafe = Stream.bracket_
7178#else
72−replicateM n = Stream.sequence . Stream.replicate n
79+afterUnsafe = Stream.afterUnsafe
80+finallyUnsafe = Stream.finallyUnsafe
81+bracketUnsafe = Stream.bracketUnsafe
7382#endif
7483
84+-------------------------------------------------------------------------------
85+-- stream exceptions
86+-------------------------------------------------------------------------------
87+
7588data BenchException
7689 = BenchException1
7790 | BenchException2
@@ -101,7 +114,7 @@ retryNone length from = do
101114 where
102115
103116 source ref =
104− replicateM (from + length)
117+ Stream.replicateM (from + length)
105118 $ Ref.modifyIORef' ref (+ 1) >> Ref.readIORef ref
106119
107120retryAll :: Int -> Int -> IO ()
@@ -146,7 +159,6 @@ o_1_space_serial_exceptions length =
146159 ]
147160
148161-- XXX Move these to FileSystem.Handle benchmarks
149−#ifndef USE_STREAMLY_CORE
150162
151163-------------------------------------------------------------------------------
152164-- copy stream exceptions
@@ -177,7 +189,7 @@ inspect $ hasNoTypeClasses 'readWriteHandleExceptionStream
177189readWriteFinally_Stream :: Handle -> Handle -> IO ()
178190readWriteFinally_Stream inh devNull =
179191 let readEx =
180− Stream.finallyUnsafe (hClose inh) (Stream.unfold FH.reader inh)
192+ finallyUnsafe (hClose inh) (Stream.unfold FH.reader inh)
181193 in Stream.fold (FH.write devNull) readEx
182194
183195#ifdef INSPECTION
@@ -192,9 +204,9 @@ readWriteFinallyStream inh devNull =
192204-- | Send the file contents to /dev/null with exception handling
193205fromToBytesBracket_Stream :: Handle -> Handle -> IO ()
194206fromToBytesBracket_Stream inh devNull =
195− let readEx = Stream.bracketUnsafe (return ()) (\_ -> hClose inh)
196− (\_ -> IFH.read inh)
197− in IFH.putBytes devNull readEx
207+ let readEx = bracketUnsafe (return ()) (\_ -> hClose inh)
208+ (\_ -> fromStreamD $ IFH.read inh)
209+ in IFH.putBytes devNull (toStreamD readEx)
198210
199211#ifdef INSPECTION
200212inspect $ hasNoTypeClasses 'fromToBytesBracket_Stream
@@ -203,8 +215,8 @@ inspect $ hasNoTypeClasses 'fromToBytesBracket_Stream
203215fromToBytesBracketStream :: Handle -> Handle -> IO ()
204216fromToBytesBracketStream inh devNull =
205217 let readEx = Stream.bracket (return ()) (\_ -> hClose inh)
206− (\_ -> IFH.read inh)
207− in IFH.putBytes devNull readEx
218+ (\_ -> fromStreamD $ IFH.read inh)
219+ in IFH.putBytes devNull (toStreamD readEx)
208220
209221readWriteBeforeAfterStream :: Handle -> Handle -> IO ()
210222readWriteBeforeAfterStream inh devNull =
@@ -228,7 +240,7 @@ inspect $ 'readWriteAfterStream `hasNoType` ''D.Step
228240
229241readWriteAfter_Stream :: Handle -> Handle -> IO ()
230242readWriteAfter_Stream inh devNull =
231− let readEx = Stream.afterUnsafe (hClose inh) (Stream.unfold FH.reader inh)
243+ let readEx = afterUnsafe (hClose inh) (Stream.unfold FH.reader inh)
232244 in Stream.fold (FH.write devNull) readEx
233245
234246#ifdef INSPECTION
@@ -310,10 +322,10 @@ o_1_space_copy_exceptions_readChunks env =
310322-- | Send the file contents to /dev/null with exception handling
311323toChunksBracket_ :: Handle -> Handle -> IO ()
312324toChunksBracket_ inh devNull =
313− let readEx = Stream.bracketUnsafe
325+ let readEx = bracketUnsafe
314326 (return ())
315327 (\_ -> hClose inh)
316− (\_ -> IFH.readChunks inh)
328+ (\_ -> fromStreamD $ IFH.readChunks inh)
317329 in Stream.fold (IFH.writeChunks devNull) readEx
318330
319331#ifdef INSPECTION
@@ -325,7 +337,7 @@ toChunksBracket inh devNull =
325337 let readEx = Stream.bracket
326338 (return ())
327339 (\_ -> hClose inh)
328− (\_ -> IFH.readChunks inh)
340+ (\_ -> fromStreamD $ IFH.readChunks inh)
329341 in Stream.fold (IFH.writeChunks devNull) readEx
330342
331343o_1_space_copy_exceptions_toChunks :: BenchEnv -> [Benchmark]
@@ -338,16 +350,12 @@ o_1_space_copy_exceptions_toChunks env =
338350 ]
339351 ]
340352
341−#endif
342−
343353benchmarks :: String -> BenchEnv -> Int -> [Benchmark]
344354benchmarks moduleName _env size =
345355 [ bgroup (o_1_space_prefix moduleName) $ concat
346356 [ o_1_space_serial_exceptions size
347−#ifndef USE_STREAMLY_CORE
348357 , o_1_space_copy_exceptions_readChunks _env
349358 , o_1_space_copy_exceptions_toChunks _env
350359 , o_1_space_copy_stream_exceptions _env
351−#endif
352360 ]
353361 ]
benchmark/Streamly/Benchmark/Data/Stream/Expand.hs+108−76
@@ -25,11 +25,6 @@
2525
2626module Stream.Expand (benchmarks) where
2727
28−import Streamly.Internal.Data.Stream (Stream)
29−import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
30−
31−import qualified Control.Applicative as AP
32−
3328#ifdef INSPECTION
3429import GHC.Types (SPEC(..))
3530import Test.Inspection
@@ -38,19 +33,29 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
3833#endif
3934
4035import qualified Stream.Common as Common
36+import qualified Streamly.Internal.Data.Unfold as UF
37+
4138#ifdef USE_PRELUDE
4239import qualified Streamly.Internal.Data.Stream.IsStream as S
40+import qualified Streamly.Internal.Data.Stream.IsStream as StreamK
4341import Streamly.Benchmark.Prelude
4442 ( sourceFoldMapM, sourceFoldMapWith, sourceFoldMapWithM
4543 , sourceFoldMapWithStream, concatFoldableWith, concatForFoldableWith)
4644#else
47−import qualified Streamly.Internal.Data.Stream as S
48−#endif
49−import qualified Streamly.Internal.Data.Unfold as UF
45+import qualified Streamly.Internal.Data.Stream.StreamD as S
46+#ifdef USE_STREAMK
47+import Streamly.Internal.Data.Stream.StreamD (Stream)
48+import Streamly.Internal.Data.Stream.StreamK (StreamK, CrossStreamK)
49+import qualified Control.Applicative as AP
5050import qualified Streamly.Internal.Data.Fold as Fold
51+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
52+#else
53+import qualified Streamly.Internal.Data.Stream.StreamD as StreamK
54+#endif
55+#endif
5156
5257import Gauge
53−import Stream.Common hiding (append2)
58+import Stream.Common
5459import Streamly.Benchmark.Common
5560import Prelude hiding (concatMap)
5661
@@ -58,6 +63,7 @@ import Prelude hiding (concatMap)
5863-- Iteration/looping utilities
5964-------------------------------------------------------------------------------
6065
66+#ifdef USE_STREAMK
6167{-# INLINE iterateN #-}
6268iterateN :: (Int -> a -> a) -> a -> Int -> a
6369iterateN g initial count = f count initial
@@ -69,13 +75,16 @@ iterateN g initial count = f count initial
6975
7076-- Iterate a transformation over a singleton stream
7177{-# INLINE iterateSingleton #-}
72−iterateSingleton ::
73− (Int -> CrossStream m Int -> CrossStream m Int)
78+iterateSingleton :: Applicative m =>
79+ (Int -> CrossStreamK m Int -> CrossStreamK m Int)
7480 -> Int
7581 -> Int
7682 -> Stream m Int
7783iterateSingleton g count n =
78− unCrossStream $ iterateN g (CrossStream (S.fromPure n)) count
84+ toStream
85+ $ StreamK.unCross
86+ $ iterateN g (StreamK.mkCross (StreamK.fromPure n)) count
87+#endif
7988
8089{-
8190-- XXX need to check why this is slower than the explicit recursion above, even
@@ -104,50 +113,28 @@ _iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
104113{-# INLINE serial2 #-}
105114serial2 :: Int -> Int -> IO ()
106115serial2 count n =
107− drain $
108− Common.append (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
116+ drain $ toStream $
117+ Common.append
118+ (fromStream $ sourceUnfoldrM count n)
119+ (fromStream $ sourceUnfoldrM count (n + 1))
109120
110121{-# INLINE serial4 #-}
111122serial4 :: Int -> Int -> IO ()
112123serial4 count n =
113− drain $
124+ drain $ toStream $
114125 Common.append
115− (Common.append (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1)))
116126 (Common.append
117− (sourceUnfoldrM count (n + 2))
118− (sourceUnfoldrM count (n + 3)))
119−
120−{-# INLINE append2 #-}
121−append2 :: Int -> Int -> IO ()
122−append2 count n =
123− drain $
124− Common.append2 (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
125−
126−{-# INLINE append4 #-}
127−append4 :: Int -> Int -> IO ()
128−append4 count n =
129− drain $
130− Common.append2
131− (Common.append2
132− (sourceUnfoldrM count n)
133− (sourceUnfoldrM count (n + 1)))
134− (Common.append2
135− (sourceUnfoldrM count (n + 2))
136− (sourceUnfoldrM count (n + 3)))
137−
138−#ifdef INSPECTION
139−inspect $ hasNoTypeClasses 'append2
140−inspect $ 'append2 `hasNoType` ''SPEC
141−inspect $ 'append2 `hasNoType` ''D.AppendState
142−#endif
127+ (fromStream $ sourceUnfoldrM count n)
128+ (fromStream $ sourceUnfoldrM count (n + 1)))
129+ (Common.append
130+ (fromStream $ sourceUnfoldrM count (n + 2))
131+ (fromStream $ sourceUnfoldrM count (n + 3)))
143132
144133o_1_space_joining :: Int -> [Benchmark]
145134o_1_space_joining value =
146135 [ bgroup "joining"
147136 [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
148− , benchIOSrc1 "append (2,x/2)" (append2 (value `div` 2))
149137 , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
150− , benchIOSrc1 "append (2,2,x/4)" (append4 (value `div` 4))
151138 ]
152139 ]
153140
@@ -183,10 +170,11 @@ o_1_space_concatFoldable value =
183170{-# INLINE concatMap #-}
184171concatMap :: Int -> Int -> Int -> IO ()
185172concatMap outer inner n =
186− drain $ S.concatMap
187− (\_ -> sourceUnfoldrM inner n)
188− (sourceUnfoldrM outer n)
173+ drain $ toStream $ StreamK.concatMap
174+ (\_ -> fromStream $ sourceUnfoldrM inner n)
175+ (fromStream $ sourceUnfoldrM outer n)
189176
177+#ifndef USE_STREAMK
190178{-# INLINE concatMapM #-}
191179concatMapM :: Int -> Int -> Int -> IO ()
192180concatMapM outer inner n =
@@ -198,15 +186,16 @@ concatMapM outer inner n =
198186inspect $ hasNoTypeClasses 'concatMap
199187inspect $ 'concatMap `hasNoType` ''SPEC
200188#endif
189+#endif
201190
202191-- concatMap unfoldr/unfoldr
203192
204193{-# INLINE concatMapPure #-}
205194concatMapPure :: Int -> Int -> Int -> IO ()
206195concatMapPure outer inner n =
207− drain $ S.concatMap
208− (\_ -> sourceUnfoldr inner n)
209− (sourceUnfoldr outer n)
196+ drain $ toStream $ StreamK.concatMap
197+ (\_ -> fromStream $ sourceUnfoldr inner n)
198+ (fromStream $ sourceUnfoldr outer n)
210199
211200#ifdef INSPECTION
212201inspect $ hasNoTypeClasses 'concatMapPure
@@ -218,7 +207,8 @@ inspect $ 'concatMapPure `hasNoType` ''SPEC
218207{-# INLINE concatMapRepl #-}
219208concatMapRepl :: Int -> Int -> Int -> IO ()
220209concatMapRepl outer inner n =
221− drain $ S.concatMap (S.replicate inner) (sourceUnfoldrM outer n)
210+ drain $ toStream $ StreamK.concatMap
211+ (fromStream . S.replicate inner) (fromStream $ sourceUnfoldrM outer n)
222212
223213#ifdef INSPECTION
224214inspect $ hasNoTypeClasses 'concatMapRepl
@@ -227,6 +217,7 @@ inspect $ 'concatMapRepl `hasNoType` ''SPEC
227217
228218-- concatMapWith
229219
220+#ifdef USE_STREAMK
230221{-# INLINE concatMapWithSerial #-}
231222concatMapWithSerial :: Int -> Int -> Int -> IO ()
232223concatMapWithSerial = concatStreamsWith Common.append
@@ -236,6 +227,7 @@ inspect $ hasNoTypeClasses 'concatMapWithSerial
236227inspect $ 'concatMapWithSerial `hasNoType` ''SPEC
237228#endif
238229
230+{-
239231{-# INLINE concatMapWithAppend #-}
240232concatMapWithAppend :: Int -> Int -> Int -> IO ()
241233concatMapWithAppend = concatStreamsWith Common.append2
@@ -244,6 +236,7 @@ concatMapWithAppend = concatStreamsWith Common.append2
244236inspect $ hasNoTypeClasses 'concatMapWithAppend
245237inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
246238#endif
239+-}
247240
248241-- mergeMapWith
249242
@@ -251,9 +244,12 @@ inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
251244mergeMapWithSerial :: Int -> Int -> Int -> IO ()
252245mergeMapWithSerial = mergeMapWith Common.append
253246
247+{-
254248{-# INLINE mergeMapWithAppend #-}
255249mergeMapWithAppend :: Int -> Int -> Int -> IO ()
256250mergeMapWithAppend = mergeMapWith Common.append2
251+-}
252+#endif
257253
258254-- unfoldMany
259255
@@ -284,10 +280,6 @@ o_1_space_concat value = sqrtVal `seq`
284280 , benchIOSrc1 "concatMapPure (1 of n)"
285281 (concatMapPure 1 value)
286282
287− -- This is for comparison with foldMapWith
288− , benchIOSrc "concatMapId (n of 1) (fromFoldable)"
289− (S.concatMap id . sourceConcatMapId value)
290−
291283 , benchIOSrc1 "concatMap (n of 1)"
292284 (concatMap value 1)
293285 , benchIOSrc1 "concatMap (sqrt n of sqrt n)"
@@ -295,16 +287,25 @@ o_1_space_concat value = sqrtVal `seq`
295287 , benchIOSrc1 "concatMap (1 of n)"
296288 (concatMap 1 value)
297289
290+#ifndef USE_STREAMK
291+ -- This is for comparison with foldMapWith
292+ , benchIOSrc "concatMapId (n of 1) (fromFoldable)"
293+ (S.concatMap id . sourceConcatMapId value)
294+
298295 , benchIOSrc1 "concatMapM (n of 1)"
299296 (concatMapM value 1)
300297 , benchIOSrc1 "concatMapM (sqrt n of sqrt n)"
301298 (concatMapM sqrtVal sqrtVal)
302299 , benchIOSrc1 "concatMapM (1 of n)"
303300 (concatMapM 1 value)
301+#endif
304302
303+#ifdef USE_STREAMK
304+ {-
305305 -- This is for comparison with foldMapWith
306306 , benchIOSrc "concatMapWithId (n of 1) (fromFoldable)"
307− (S.concatMapWith Common.append id . sourceConcatMapId value)
307+ (toStream . S.concatMapWith Common.append id . sourceConcatMapId value)
308+ -}
308309
309310 , benchIOSrc1 "concatMapWith (n of 1)"
310311 (concatMapWithSerial value 1)
@@ -313,9 +314,12 @@ o_1_space_concat value = sqrtVal `seq`
313314 , benchIOSrc1 "concatMapWith (1 of n)"
314315 (concatMapWithSerial 1 value)
315316
317+ {-
316318 -- quadratic with number of outer streams
317319 , benchIOSrc1 "concatMapWithAppend (2 of n/2)"
318320 (concatMapWithAppend 2 (value `div` 2))
321+ -}
322+#endif
319323
320324 -- concatMap vs unfoldMany
321325 , benchIOSrc1 "concatMapRepl (sqrt n of sqrt n)"
@@ -329,8 +333,9 @@ o_1_space_concat value = sqrtVal `seq`
329333
330334 sqrtVal = round $ sqrt (fromIntegral value :: Double)
331335
332−o_n_space_concat :: Int -> [Benchmark]
333−o_n_space_concat value = sqrtVal `seq`
336+#ifdef USE_STREAMK
337+o_n_space_merge :: Int -> [Benchmark]
338+o_n_space_merge value = sqrtVal `seq`
334339 [ bgroup "concat"
335340 [
336341 -------------------mergeMapWith-----------------
@@ -344,16 +349,19 @@ o_n_space_concat value = sqrtVal `seq`
344349 , benchIOSrc1 "mergeMapWithSerial (2 of n/2)"
345350 (mergeMapWithSerial 2 (value `div` 2))
346351
352+ {-
347353 , benchIOSrc1 "mergeMapWithAppend (n of 1)"
348354 (mergeMapWithAppend value 1)
349355 , benchIOSrc1 "mergeMapWithAppend (sqrtVal of sqrtVal)"
350356 (mergeMapWithAppend sqrtVal sqrtVal)
357+ -}
351358 ]
352359 ]
353360
354361 where
355362
356363 sqrtVal = round $ sqrt (fromIntegral value :: Double)
364+#endif
357365
358366-------------------------------------------------------------------------------
359367-- Applicative
@@ -369,9 +377,10 @@ o_1_space_applicative value =
369377 ]
370378 ]
371379
380+#ifdef USE_STREAMK
372381o_n_space_applicative :: Int -> [Benchmark]
373382o_n_space_applicative value =
374− [ bgroup "Applicative"
383+ [ bgroup "iterated"
375384 [ benchIOSrc "(*>) (n times)" $
376385 iterateSingleton ((*>) . pure) value
377386 , benchIOSrc "(<*) (n times)" $
@@ -382,6 +391,7 @@ o_n_space_applicative value =
382391 iterateSingleton (AP.liftA2 (+) . pure) value
383392 ]
384393 ]
394+#endif
385395
386396-------------------------------------------------------------------------------
387397-- Monad
@@ -405,30 +415,46 @@ o_1_space_monad value =
405415 ]
406416 ]
407417
418+#ifdef USE_STREAMK
408419-- This is a good benchmark but inefficient way to compute primes. As we see a
409420-- new prime we keep appending a division filter for all the future numbers.
410421{-# INLINE sieve #-}
411−sieve :: Monad m => Stream m Int -> Stream m Int
412−sieve s = S.concatEffect $ do
413− r <- S.uncons s
422+sieve :: Monad m => StreamK m Int -> StreamK m Int
423+sieve s = StreamK.concatEffect $ do
424+ r <- StreamK.uncons s
414425 case r of
415426 Just (prime, rest) ->
416− pure $ prime `S.cons` sieve (S.filter (\n -> n `mod` prime /= 0) rest)
417− Nothing -> pure S.nil
427+ -- XXX Use K.filter or rewrite to K.filter
428+ let f = S.filter (\n -> n `mod` prime /= 0)
429+ in pure $ prime `StreamK.cons` sieve (fromStream $ f $ toStream rest)
430+ Nothing -> pure StreamK.nil
431+
432+o_n_space_iterated :: Int -> [Benchmark]
433+o_n_space_iterated value =
434+ [ bgroup "iterated"
435+ [
436+ benchIO "concatEffect prime sieve (n/4)"
437+ (\n ->
438+ S.fold Fold.sum
439+ $ toStream
440+ $ sieve
441+ $ fromStream
442+ $ S.enumerateFromTo 2 (value `div` 4 + n))
443+ , benchIOSrc "(>>) (n times)" $
444+ iterateSingleton ((>>) . pure) value
445+ , benchIOSrc "(>>=) (n times)" $
446+ iterateSingleton (\x xs -> xs >>= \y -> return (x + y)) value
447+ ]
448+ ]
449+#endif
418450
419451o_n_space_monad :: Int -> [Benchmark]
420452o_n_space_monad value =
421453 [ bgroup "Monad"
422− [ benchIOSrc "(>>) (n times)" $
423− iterateSingleton ((>>) . pure) value
424− , benchIOSrc "(>>=) (n times)" $
425− iterateSingleton (\x xs -> xs >>= \y -> return (x + y)) value
426− , benchIO "(>>=) (sqrt n x sqrt n) (toList)" $
454+ [ benchIO "(>>=) (sqrt n x sqrt n) (toList)" $
427455 toListM value
428456 , benchIO "(>>=) (sqrt n x sqrt n) (toListSome)" $
429457 toListSome value
430− , benchIO "naive prime sieve (n/4)"
431− (\n -> S.fold Fold.sum $ sieve $ S.enumerateFromTo 2 (value `div` 4 + n))
432458 ]
433459 ]
434460
@@ -436,6 +462,7 @@ o_n_space_monad value =
436462-- Joining
437463-------------------------------------------------------------------------------
438464
465+{-
439466toKv :: Int -> (Int, Int)
440467toKv p = (p, p)
441468
@@ -487,6 +514,7 @@ o_n_heap_buffering value =
487514
488515 halfVal = value `div` 2
489516 sqrtVal = round $ sqrt (fromIntegral value :: Double)
517+-}
490518
491519-------------------------------------------------------------------------------
492520-- Main
@@ -495,6 +523,7 @@ o_n_heap_buffering value =
495523-- In addition to gauge options, the number of elements in the stream can be
496524-- passed using the --stream-size option.
497525--
526+{-# ANN benchmarks "HLint: ignore" #-}
498527benchmarks :: String -> Int -> [Benchmark]
499528benchmarks moduleName size =
500529 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
@@ -505,19 +534,22 @@ benchmarks moduleName size =
505534 , o_1_space_concatFoldable size
506535#endif
507536 , o_1_space_concat size
508−
509537 , o_1_space_applicative size
510538 , o_1_space_monad size
511−
512539 ]
513540 , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
514541 [
515542 -- multi-stream
516− o_n_space_applicative size
517− , o_n_space_monad size
518− , o_n_space_concat size
543+ o_n_space_monad size
544+#ifdef USE_STREAMK
545+ , o_n_space_merge size
546+ , o_n_space_iterated size
547+ , o_n_space_applicative size
548+#endif
519549 ]
550+ {-
520551 , bgroup (o_n_heap_prefix moduleName) $
521552 -- multi-stream
522553 o_n_heap_buffering size
554+ -}
523555 ]
benchmark/Streamly/Benchmark/Data/Stream/Generate.hs+43−25
@@ -21,29 +21,28 @@ import Control.DeepSeq (NFData(..))
2121import Data.Functor.Identity (Identity(..))
2222
2323import qualified GHC.Exts as GHC
24−import qualified Stream.Common as Common
2524import qualified Streamly.Internal.Data.Fold as Fold
25+
2626#ifdef USE_PRELUDE
27+import Streamly.Prelude (MonadAsync)
28+import Stream.Common hiding (MonadAsync)
2729import Streamly.Benchmark.Prelude (sourceFromFoldableM, absTimes)
2830import qualified Streamly.Prelude as S
2931import qualified Streamly.Internal.Data.Stream.IsStream as Stream
3032#else
31−import qualified Streamly.Internal.Data.Stream as Stream
33+import Stream.Common
34+import Streamly.Internal.Data.Stream.StreamD (Stream)
35+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
36+#ifdef USE_STREAMK
37+import System.IO.Unsafe (unsafeInterleaveIO)
38+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
39+import qualified Stream.Common as Common
40+#endif
3241#endif
33−import qualified Prelude
3442
3543import Gauge
3644import Streamly.Benchmark.Common
37−import Streamly.Internal.Data.Stream (Stream)
38−import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
39−#ifdef USE_PRELUDE
40−import Streamly.Prelude (MonadAsync)
41−import Stream.Common hiding (MonadAsync)
42−#else
43−import Stream.Common
44−#endif
45−
46−import System.IO.Unsafe (unsafeInterleaveIO)
45+import qualified Prelude
4746
4847import Prelude hiding (repeat, replicate, iterate)
4948
@@ -51,6 +50,14 @@ import Prelude hiding (repeat, replicate, iterate)
5150-- Generation
5251-------------------------------------------------------------------------------
5352
53+#ifdef USE_PRELUDE
54+type Stream = Stream.SerialT
55+toStreamD = Stream.toStream
56+#else
57+toStreamD :: a -> a
58+toStreamD = id
59+#endif
60+
5461-------------------------------------------------------------------------------
5562-- fromList
5663-------------------------------------------------------------------------------
@@ -157,15 +164,15 @@ iterate count = Stream.take count . Stream.iterate (+1)
157164iterateM :: MonadAsync m => Int -> Int -> Stream m Int
158165iterateM count = Stream.take count . Stream.iterateM (return . (+1)) . return
159166
160−#ifdef USE_PRELUDE
161167{-# INLINE repeatM #-}
162−repeatM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
163−repeatM count = S.take count . S.repeatM . return
168+repeatM :: MonadAsync m => Int -> Int -> Stream m Int
169+repeatM count = Stream.take count . Stream.repeatM . return
164170
165171{-# INLINE replicateM #-}
166−replicateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
167−replicateM count = S.replicateM count . return
172+replicateM :: MonadAsync m => Int -> Int -> Stream m Int
173+replicateM count = Stream.replicateM count . return
168174
175+#ifdef USE_PRELUDE
169176{-# INLINE fromIndices #-}
170177fromIndices :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
171178fromIndices value n = S.take value $ S.fromIndices (+ n)
@@ -175,15 +182,17 @@ fromIndicesM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
175182fromIndicesM value n = S.take value $ S.fromIndicesM (return <$> (+ n))
176183#endif
177184
185+#ifdef USE_STREAMK
178186{-# INLINE mfixUnfold #-}
179187mfixUnfold :: Int -> Int -> Stream IO (Int, Int)
180−mfixUnfold count start = Stream.mfix f
188+mfixUnfold count start = toStream $ StreamK.mfix f
181189 where
182− f action = unCrossStream $ do
190+ f action = StreamK.unCross $ do
183191 let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act
184− x <- CrossStream (Common.fromListM [incr 1 action, incr 2 action])
185− y <- CrossStream (Common.sourceUnfoldr count start)
192+ x <- StreamK.mkCross (fromStream $ Common.fromListM [incr 1 action, incr 2 action])
193+ y <- StreamK.mkCross (fromStream $ Common.sourceUnfoldr count start)
186194 return (x, y)
195+#endif
187196
188197o_1_space_generation :: Int -> [Benchmark]
189198o_1_space_generation value =
@@ -201,37 +210,46 @@ o_1_space_generation value =
201210 , benchIOSrc "fracFromTo" (sourceFracFromTo value)
202211 , benchIOSrc "fromList" (sourceFromList value)
203212 , benchIOSrc "fromListM" (sourceFromListM value)
204− , benchPureSrc "IsList.fromList" (sourceIsList value)
205− , benchPureSrc "IsString.fromString" (sourceIsString value)
213+ , benchPureSrc "IsList.fromList" (toStreamD . sourceIsList value)
214+ , benchPureSrc "IsString.fromString" (toStreamD . sourceIsString value)
206215 , benchIOSrc "enumerateFrom" (enumerateFrom value)
207216 , benchIOSrc "enumerateFromTo" (enumerateFromTo value)
208217 , benchIOSrc "enumerateFromThen" (enumerateFromThen value)
209218 , benchIOSrc "enumerateFromThenTo" (enumerateFromThenTo value)
210219 , benchIOSrc "enumerate" (enumerate value)
211220 , benchIOSrc "enumerateTo" (enumerateTo value)
212−#ifdef USE_PRELUDE
213221 , benchIOSrc "repeatM" (repeatM value)
214222 , benchIOSrc "replicateM" (replicateM value)
223+#ifdef USE_PRELUDE
215224 , benchIOSrc "fromIndices" (fromIndices value)
216225 , benchIOSrc "fromIndicesM" (fromIndicesM value)
217226#endif
218227
219228 -- These essentially test cons and consM
229+#ifdef USE_STREAMK
220230 , benchIOSrc "fromFoldable" (sourceFromFoldable value)
231+ -- , benchIOSrc "fromFoldable 16" (sourceFromFoldable 16)
232+#else
233+ -- , benchIOSrc "fromFoldable 16" (sourceFromFoldable 16)
234+#endif
221235
222236#ifdef USE_PRELUDE
223237 , benchIOSrc "fromFoldableM" (sourceFromFoldableM value)
224238 , benchIOSrc "absTimes" $ absTimes value
225239#endif
240+#ifdef USE_STREAMK
226241 , Common.benchIOSrc "mfix_10" (mfixUnfold 10)
227242 , Common.benchIOSrc "mfix_100" (mfixUnfold 100)
228243 , Common.benchIOSrc "mfix_1000" (mfixUnfold 1000)
244+#endif
229245 ]
230246 ]
231247
248+#ifndef USE_PRELUDE
232249instance NFData a => NFData (Stream Identity a) where
233250 {-# INLINE rnf #-}
234251 rnf xs = runIdentity $ Stream.fold (Fold.foldl' (\_ x -> rnf x) ()) xs
252+#endif
235253
236254o_n_heap_generation :: Int -> [Benchmark]
237255o_n_heap_generation value =
benchmark/Streamly/Benchmark/Data/Stream/Lift.hs+29−27
@@ -18,24 +18,33 @@ module Stream.Lift (benchmarks) where
1818import Control.DeepSeq (NFData(..))
1919import Control.Monad.State.Strict (StateT, get, put)
2020import Data.Functor.Identity (Identity)
21−import Stream.Common
22− (benchIO, sourceUnfoldr, sourceUnfoldrM, benchIOSrc, drain)
21+import Stream.Common (sourceUnfoldr, sourceUnfoldrM, benchIOSrc)
2322import System.Random (randomRIO)
23+
24+import qualified Stream.Common as Common
25+import qualified Streamly.Internal.Data.Fold as Fold
26+
2427#ifdef USE_PRELUDE
2528import qualified Streamly.Internal.Data.Stream.IsStream as Stream
2629#else
27−import qualified Streamly.Internal.Data.Stream as Stream
28−#endif
29−import qualified Streamly.Internal.Data.Fold as Fold
30−import qualified Stream.Common as Common
30+import Streamly.Internal.Data.Stream.StreamD (Stream)
31+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
32+#ifdef USE_STREAMK
33+import Stream.Common (benchIO, drain)
34+import Streamly.Internal.Data.Stream.StreamK (StreamK)
35+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
3136import qualified Control.Monad.State.Strict as State
37+#endif
38+#endif
3239
3340import Gauge
34−import Streamly.Internal.Data.Stream (Stream)
3541import Streamly.Benchmark.Common
36−
3742import Prelude hiding (reverse, tail)
3843
44+#ifdef USE_PRELUDE
45+type Stream = Stream.SerialT
46+#endif
47+
3948-------------------------------------------------------------------------------
4049-- Monad transformation (hoisting etc.)
4150-------------------------------------------------------------------------------
@@ -76,12 +85,15 @@ o_1_space_hoisting value =
7685 [ bgroup "hoisting"
7786 [ benchIOSrc "evalState" (evalStateT value)
7887 , benchIOSrc "withState" (withState value)
88+#ifndef USE_PRELUDE
7989 , benchHoistSink value "generalizeInner"
8090 ((\xs -> Stream.fold Fold.length xs :: IO Int)
8191 . Stream.generalizeInner)
92+#endif
8293 ]
8394 ]
8495
96+#ifdef USE_STREAMK
8597{-# INLINE iterateStateIO #-}
8698iterateStateIO ::
8799 Monad m
@@ -95,28 +107,17 @@ iterateStateIO n = do
95107 iterateStateIO n
96108 else return x
97109
110+-- XXX This is basically testing the perf of concatEffect, change it to just
111+-- use concatEffect and move it along with other concatMap benchmarks.
98112{-# INLINE iterateStateT #-}
99−iterateStateT :: Int -> Stream (StateT Int IO) Int
100−iterateStateT n = Stream.concatEffect $ do
113+iterateStateT :: Int -> StreamK (StateT Int IO) Int
114+iterateStateT n = StreamK.concatEffect $ do
101115 x <- get
102116 if x > n
103117 then do
104118 put (x - 1)
105119 return $ iterateStateT n
106− else return $ Stream.fromPure x
107−
108−{-# INLINE iterateState #-}
109−{-# SPECIALIZE iterateState :: Int -> Stream (StateT Int IO) Int #-}
110−iterateState :: Monad m =>
111− Int
112− -> Stream (StateT Int m) Int
113−iterateState n = Stream.concatEffect $ do
114− x <- get
115− if x > n
116− then do
117− put (x - 1)
118− return $ iterateState n
119− else return $ Stream.fromPure x
120+ else return $ StreamK.fromPure x
120121
121122o_n_heap_transformer :: Int -> [Benchmark]
122123o_n_heap_transformer value =
@@ -124,11 +125,10 @@ o_n_heap_transformer value =
124125 [ benchIO "StateT Int IO (n times) (baseline)" $ \n ->
125126 State.evalStateT (iterateStateIO n) value
126127 , benchIO "Stream (StateT Int IO) (n times)" $ \n ->
127− State.evalStateT (drain (iterateStateT n)) value
128− , benchIO "MonadState Int m => Stream m Int" $ \n ->
129− State.evalStateT (drain (iterateState n)) value
128+ State.evalStateT (drain $ Common.toStream (iterateStateT n)) value
130129 ]
131130 ]
131+#endif
132132
133133-------------------------------------------------------------------------------
134134-- Main
@@ -140,5 +140,7 @@ o_n_heap_transformer value =
140140benchmarks :: String -> Int -> [Benchmark]
141141benchmarks moduleName size =
142142 [ bgroup (o_1_space_prefix moduleName) (o_1_space_hoisting size)
143+#ifdef USE_STREAMK
143144 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_transformer size)
145+#endif
144146 ]
benchmark/Streamly/Benchmark/Data/Stream/Reduce.hs+131−104
@@ -21,17 +21,21 @@ import Control.DeepSeq (NFData(..))
2121import Control.Monad.IO.Class (MonadIO(..))
2222import Data.Monoid (Sum(..))
2323import GHC.Generics (Generic)
24−import Streamly.Internal.Data.Stream (Stream)
2524
2625import qualified Streamly.Internal.Data.Refold.Type as Refold
2726import qualified Streamly.Internal.Data.Fold as FL
2827import qualified Stream.Common as Common
29−#ifdef USE_PRELUDE
30−import Control.Monad (when)
31−import Data.Proxy (Proxy(..))
28+
29+#ifndef USE_STREAMLY_CORE
3230import Data.HashMap.Strict (HashMap)
31+import Data.Proxy (Proxy(..))
3332import Streamly.Internal.Data.IsMap.HashMap ()
33+#endif
34+
35+#ifdef USE_PRELUDE
36+import Control.Monad (when)
3437import qualified Streamly.Internal.Data.Stream.IsStream as S
38+import qualified Streamly.Prelude as S
3539import Streamly.Prelude (fromSerial)
3640import Streamly.Benchmark.Prelude hiding
3741 ( benchIO, benchIOSrc, sourceUnfoldrM, apDiscardFst, apDiscardSnd, apLiftA2
@@ -39,7 +43,22 @@ import Streamly.Benchmark.Prelude hiding
3943 , filterSome, breakAfterSome, toListM, toListSome, transformMapM
4044 , transformComposeMapM, transformTeeMapM, transformZipMapM)
4145#else
42−import qualified Streamly.Internal.Data.Stream as S
46+
47+import Streamly.Internal.Data.Stream.StreamD (Stream)
48+import qualified Streamly.Internal.Data.Stream.StreamD as S
49+#ifndef USE_STREAMLY_CORE
50+import qualified Streamly.Data.Stream.Prelude as S
51+import qualified Streamly.Internal.Data.Stream.Time as S
52+#endif
53+
54+#ifdef USE_STREAMK
55+import Streamly.Internal.Data.Stream.StreamK (StreamK)
56+import qualified Streamly.Internal.Data.Parser as PR
57+import qualified Streamly.Internal.Data.Stream.StreamK as K
58+#else
59+import qualified Streamly.Internal.Data.Stream.StreamD as K
60+#endif
61+
4362#endif
4463
4564import Gauge
@@ -47,45 +66,27 @@ import Streamly.Benchmark.Common
4766import Stream.Common
4867import Prelude hiding (reverse, tail)
4968
50−-------------------------------------------------------------------------------
51−-- Iteration/looping utilities
52−-------------------------------------------------------------------------------
53−
54−{-# INLINE iterateN #-}
55−iterateN :: (Int -> a -> a) -> a -> Int -> a
56−iterateN g initial count = f count initial
57−
58− where
59−
60− f (0 :: Int) x = x
61− f i x = f (i - 1) (g i x)
69+#ifdef USE_PRELUDE
70+type Stream = S.SerialT
71+#endif
6272
63−-- Iterate a transformation over a singleton stream
64−{-# INLINE iterateSingleton #-}
65−iterateSingleton ::
66− (Int -> Stream m Int -> Stream m Int)
67− -> Int
73+-- Apply transformation g count times on a stream of length len
74+#ifdef USE_STREAMK
75+{-# INLINE iterateSource #-}
76+iterateSource ::
77+ MonadAsync m
78+ => (StreamK m Int -> StreamK m Int)
6879 -> Int
69− -> Stream m Int
70−iterateSingleton g count n = iterateN g (S.fromPure n) count
71−
72−{-
73−-- XXX need to check why this is slower than the explicit recursion above, even
74−-- if the above code is written in a foldr like head recursive way. We also
75−-- need to try this with foldlM' once #150 is fixed.
76−-- However, it is perhaps best to keep the iteration benchmarks independent of
77−-- foldrM and any related fusion issues.
78−{-# INLINE _iterateSingleton #-}
79−_iterateSingleton ::
80− Monad m
81− => (Int -> Stream m Int -> Stream m Int)
8280 -> Int
8381 -> Int
84− -> Stream m Int
85−_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
86−-}
82+ -> StreamK m Int
83+iterateSource g count len n = f count (fromStream $ sourceUnfoldrM len n)
8784
88−-- Apply transformation g count times on a stream of length len
85+ where
86+
87+ f (0 :: Int) stream = stream
88+ f i stream = f (i - 1) (g stream)
89+#else
8990{-# INLINE iterateSource #-}
9091iterateSource ::
9192 MonadAsync m
@@ -100,28 +101,7 @@ iterateSource g count len n = f count (sourceUnfoldrM len n)
100101
101102 f (0 :: Int) stream = stream
102103 f i stream = f (i - 1) (g stream)
103−
104−-------------------------------------------------------------------------------
105−-- Functor
106−-------------------------------------------------------------------------------
107−
108−o_n_space_functor :: Int -> [Benchmark]
109−o_n_space_functor value =
110− [ bgroup "Functor"
111− [ benchIO "(+) (n times) (baseline)" $ \i0 ->
112− iterateN (\i acc -> acc >>= \n -> return $ i + n) (return i0) value
113− , benchIOSrc "(<$) (n times)" $
114− iterateSingleton (<$) value
115− , benchIOSrc "fmap (n times)" $
116− iterateSingleton (fmap . (+)) value
117− {-
118− , benchIOSrc fromSerial "_(<$) (n times)" $
119− _iterateSingleton (<$) value
120− , benchIOSrc fromSerial "_fmap (n times)" $
121− _iterateSingleton (fmap . (+)) value
122− -}
123− ]
124− ]
104+#endif
125105
126106-------------------------------------------------------------------------------
127107-- Grouping transformations
@@ -195,6 +175,16 @@ refoldIterateM =
195175 (Refold.take 2 Refold.sconcat) (return (Sum 0))
196176 . fmap Sum
197177
178+#ifdef USE_STREAMK
179+{-# INLINE parseBreak #-}
180+parseBreak :: Monad m => StreamK m Int -> m ()
181+parseBreak s = do
182+ r <- K.parseBreak PR.one s
183+ case r of
184+ (Left _, _) -> return ()
185+ (Right _, s1) -> parseBreak s1
186+#endif
187+
198188o_1_space_grouping :: Int -> [Benchmark]
199189o_1_space_grouping value =
200190 -- Buffering operations using heap proportional to group/window sizes.
@@ -216,31 +206,54 @@ o_1_space_grouping value =
216206 , benchIOSink value "refoldMany" refoldMany
217207 , benchIOSink value "foldIterateM" foldIterateM
218208 , benchIOSink value "refoldIterateM" refoldIterateM
209+#ifdef USE_STREAMK
210+ , benchIOSink value "parseBreak (recursive)" (parseBreak . fromStream)
211+#endif
212+
213+#ifndef USE_STREAMLY_CORE
214+ , benchIOSink value "classifySessionsOf (10000 buckets)"
215+ (classifySessionsOf (getKey 10000))
216+ , benchIOSink value "classifySessionsOf (64 buckets)"
217+ (classifySessionsOf (getKey 64))
218+ , benchIOSink value "classifySessionsOfHash (10000 buckets)"
219+ (classifySessionsOfHash (getKey 10000))
220+ , benchIOSink value "classifySessionsOfHash (64 buckets)"
221+ (classifySessionsOfHash (getKey 64))
222+#endif
219223 ]
220224 ]
221225
226+#ifndef USE_STREAMLY_CORE
227+ where
228+
229+ getKey :: Int -> Int -> Int
230+ getKey n = (`mod` n)
231+#endif
232+
222233-------------------------------------------------------------------------------
223234-- Size conserving transformations (reordering, buffering, etc.)
224235-------------------------------------------------------------------------------
225236
237+#ifndef USE_PRELUDE
226238{-# INLINE reverse #-}
227239reverse :: MonadIO m => Int -> Stream m Int -> m ()
228−reverse n = composeN n S.reverse
240+reverse n = composeN n (toStream . K.reverse . fromStream)
229241
230242{-# INLINE reverse' #-}
231243reverse' :: MonadIO m => Int -> Stream m Int -> m ()
232−reverse' n = composeN n S.reverse'
244+reverse' n = composeN n S.reverseUnbox
245+#endif
233246
234247o_n_heap_buffering :: Int -> [Benchmark]
235248o_n_heap_buffering value =
236249 [ bgroup "buffered"
237250 [
251+#ifndef USE_PRELUDE
238252 -- Reversing a stream
239253 benchIOSink value "reverse" (reverse 1)
240254 , benchIOSink value "reverse'" (reverse' 1)
241−
242−#ifdef USE_PRELUDE
243− , benchIOSink value "mkAsync" (mkAsync fromSerial)
255+#else
256+ benchIOSink value "mkAsync" (mkAsync fromSerial)
244257#endif
245258 ]
246259 ]
@@ -249,9 +262,9 @@ o_n_heap_buffering value =
249262-- Grouping/Splitting
250263-------------------------------------------------------------------------------
251264
252−#ifdef USE_PRELUDE
265+#ifndef USE_STREAMLY_CORE
253266{-# INLINE classifySessionsOf #-}
254−classifySessionsOf :: MonadAsync m => (Int -> Int) -> Stream m Int -> m ()
267+classifySessionsOf :: S.MonadAsync m => (Int -> Int) -> Stream m Int -> m ()
255268classifySessionsOf getKey =
256269 Common.drain
257270 . S.classifySessionsOf
@@ -260,7 +273,7 @@ classifySessionsOf getKey =
260273 . fmap (\x -> (getKey x, x))
261274
262275{-# INLINE classifySessionsOfHash #-}
263−classifySessionsOfHash :: MonadAsync m =>
276+classifySessionsOfHash :: S.MonadAsync m =>
264277 (Int -> Int) -> Stream m Int -> m ()
265278classifySessionsOfHash getKey =
266279 Common.drain
@@ -269,25 +282,6 @@ classifySessionsOfHash getKey =
269282 1 False (const (return False)) 3 (FL.take 10 FL.sum)
270283 . S.timestamped
271284 . fmap (\x -> (getKey x, x))
272−
273−o_n_space_grouping :: Int -> [Benchmark]
274−o_n_space_grouping value =
275− -- Buffering operations using heap proportional to group/window sizes.
276− [ bgroup "grouping"
277− [ benchIOSink value "classifySessionsOf (10000 buckets)"
278− (classifySessionsOf (getKey 10000))
279− , benchIOSink value "classifySessionsOf (64 buckets)"
280− (classifySessionsOf (getKey 64))
281− , benchIOSink value "classifySessionsOfHash (10000 buckets)"
282− (classifySessionsOfHash (getKey 10000))
283− , benchIOSink value "classifySessionsOfHash (64 buckets)"
284− (classifySessionsOfHash (getKey 64))
285− ]
286− ]
287−
288− where
289−
290− getKey n = (`mod` n)
291285#endif
292286
293287-------------------------------------------------------------------------------
@@ -349,8 +343,9 @@ data Pair a b =
349343 deriving (Generic, NFData)
350344
351345{-# INLINE sumProductFold #-}
352−sumProductFold :: Monad m => Stream m Int -> m (Int, Int)
353−sumProductFold = Common.foldl' (\(s, p) x -> (s + x, p * x)) (0, 1)
346+sumProductFold :: Monad m => Stream m Int -> m (Pair Int Int)
347+sumProductFold =
348+ Common.foldl' (\(Pair s p) x -> Pair (s + x) (p * x)) (Pair 0 1)
354349
355350{-# INLINE sumProductScan #-}
356351sumProductScan :: Monad m => Stream m Int -> m (Pair Int Int)
@@ -398,6 +393,46 @@ o_1_space_transformations_mixedX4 value =
398393-- Iterating a transformation over and over again
399394-------------------------------------------------------------------------------
400395
396+#ifdef USE_STREAMK
397+{-
398+-- this is quadratic
399+{-# INLINE iterateScan #-}
400+iterateScan :: MonadAsync m => Int -> Int -> Int -> Stream m Int
401+iterateScan count len = toStream . iterateSource (K.scanl' (+) 0) count len
402+-}
403+
404+{-# INLINE iterateMapM #-}
405+iterateMapM :: MonadAsync m => Int -> Int -> Int -> Stream m Int
406+iterateMapM count len = toStream . iterateSource (K.mapM return) count len
407+
408+{-# INLINE iterateFilterEven #-}
409+iterateFilterEven :: MonadAsync m => Int -> Int -> Int -> Stream m Int
410+iterateFilterEven count len =
411+ toStream . iterateSource (K.filter even) count len
412+
413+{-# INLINE iterateTakeAll #-}
414+iterateTakeAll :: MonadAsync m => Int -> Int -> Int -> Int -> Stream m Int
415+iterateTakeAll value count len =
416+ toStream . iterateSource (K.take (value + 1)) count len
417+
418+{-# INLINE iterateDropOne #-}
419+iterateDropOne :: MonadAsync m => Int -> Int -> Int -> Stream m Int
420+iterateDropOne count len = toStream . iterateSource (K.drop 1) count len
421+
422+{-# INLINE iterateDropWhileTrue #-}
423+iterateDropWhileTrue :: MonadAsync m
424+ => Int -> Int -> Int -> Int -> Stream m Int
425+iterateDropWhileTrue value count len =
426+ toStream . iterateSource (K.dropWhile (<= (value + 1))) count len
427+
428+{-# INLINE iterateDropWhileFalse #-}
429+iterateDropWhileFalse :: MonadAsync m
430+ => Int -> Int -> Int -> Int -> Stream m Int
431+iterateDropWhileFalse value count len =
432+ toStream . iterateSource (K.dropWhile (> (value + 1))) count len
433+
434+#else
435+
401436-- this is quadratic
402437{-# INLINE iterateScan #-}
403438iterateScan :: MonadAsync m => Int -> Int -> Int -> Stream m Int
@@ -426,15 +461,11 @@ iterateTakeAll value = iterateSource (S.take (value + 1))
426461iterateDropOne :: MonadAsync m => Int -> Int -> Int -> Stream m Int
427462iterateDropOne = iterateSource (S.drop 1)
428463
429−{-# INLINE iterateDropWhileFalse #-}
430−iterateDropWhileFalse :: MonadAsync m
431− => Int -> Int -> Int -> Int -> Stream m Int
432−iterateDropWhileFalse value = iterateSource (S.dropWhile (> (value + 1)))
433−
434464{-# INLINE iterateDropWhileTrue #-}
435465iterateDropWhileTrue :: MonadAsync m
436466 => Int -> Int -> Int -> Int -> Stream m Int
437467iterateDropWhileTrue value = iterateSource (S.dropWhile (<= (value + 1)))
468+#endif
438469
439470#ifdef USE_PRELUDE
440471{-# INLINE tail #-}
@@ -455,8 +486,10 @@ o_n_stack_iterated :: Int -> [Benchmark]
455486o_n_stack_iterated value = by10 `seq` by100 `seq`
456487 [ bgroup "iterated"
457488 [ benchIOSrc "mapM (n/10 x 10)" $ iterateMapM by10 10
489+#ifndef USE_STREAMK
458490 , benchIOSrc "scanl' (quadratic) (n/100 x 100)" $
459491 iterateScan by100 100
492+#endif
460493#ifdef USE_PRELUDE
461494 , benchIOSrc "scanl1' (n/10 x 10)" $ iterateScanl1 by10 10
462495#endif
@@ -465,8 +498,10 @@ o_n_stack_iterated value = by10 `seq` by100 `seq`
465498 , benchIOSrc "takeAll (n/10 x 10)" $
466499 iterateTakeAll value by10 10
467500 , benchIOSrc "dropOne (n/10 x 10)" $ iterateDropOne by10 10
501+#ifdef USE_STREAMK
468502 , benchIOSrc "dropWhileFalse (n/10 x 10)" $
469503 iterateDropWhileFalse value by10 10
504+#endif
470505 , benchIOSrc "dropWhileTrue (n/10 x 10)" $
471506 iterateDropWhileTrue value by10 10
472507#ifdef USE_PRELUDE
@@ -530,13 +565,5 @@ benchmarks moduleName size =
530565 , o_1_space_pipesX4 size
531566 ]
532567 , bgroup (o_n_stack_prefix moduleName) (o_n_stack_iterated size)
533− , bgroup (o_n_heap_prefix moduleName) $ Prelude.concat
534− [
535−#ifdef USE_PRELUDE
536− o_n_space_grouping size
537− ,
538−#endif
539− o_n_space_functor size
540− , o_n_heap_buffering size
541− ]
568+ , bgroup (o_n_heap_prefix moduleName) (o_n_heap_buffering size)
542569 ]
benchmark/Streamly/Benchmark/Data/Stream/Split.hs+3−2
@@ -143,7 +143,7 @@ splitOnSeq str inh =
143143-- | Split on a word8 sequence.
144144splitOnSeq100k :: Handle -> IO Int
145145splitOnSeq100k inh = do
146− arr <- A.fromStream $ S.replicate 100000 123
146+ arr <- A.fromStream $ IP.toStream $ S.fromSerial $ S.replicate 100000 123
147147 (S.length $ IP.splitOnSeq arr FL.drain
148148 $ S.unfold FH.read inh) -- >>= print
149149
@@ -220,8 +220,9 @@ o_1_space_reduce_read_split env =
220220splitOnSeqUtf8 :: String -> Handle -> IO Int
221221splitOnSeqUtf8 str inh =
222222 (S.length $ IP.splitOnSeq (A.fromList str) FL.drain
223+ $ IP.fromStream
223224 $ IUS.decodeUtf8Arrays
224− $ IFH.getChunks inh) -- >>= print
225+ $ IFH.readChunks inh) -- >>= print
225226
226227o_1_space_reduce_toChunks_split :: BenchEnv -> [Benchmark]
227228o_1_space_reduce_toChunks_split env =
benchmark/Streamly/Benchmark/Data/Stream/StreamK.hs+23−24
@@ -33,7 +33,6 @@ import Prelude hiding
3333import qualified Prelude as P
3434import qualified Data.List as List
3535
36−import qualified Streamly.Internal.Control.Concurrent as S
3736import qualified Streamly.Internal.Data.Stream.StreamK.Type as S
3837import qualified Streamly.Internal.Data.Stream.StreamK as S
3938
@@ -129,7 +128,7 @@ unfoldr streamLen n = S.unfoldr step n
129128 else Just (cnt, cnt + 1)
130129
131130{-# INLINE unfoldrM #-}
132−unfoldrM :: S.MonadAsync m => Int -> Int -> Stream m Int
131+unfoldrM :: Monad m => Int -> Int -> Stream m Int
133132unfoldrM streamLen n = S.unfoldrMWith S.consM step n
134133 where
135134 step cnt =
@@ -142,7 +141,7 @@ repeat :: Int -> Int -> Stream m Int
142141repeat streamLen = S.take streamLen . S.repeat
143142
144143{-# INLINE repeatM #-}
145−repeatM :: S.MonadAsync m => Int -> Int -> Stream m Int
144+repeatM :: Monad m => Int -> Int -> Stream m Int
146145repeatM streamLen = S.take streamLen . S.repeatM . return
147146
148147{-# INLINE replicate #-}
@@ -150,7 +149,7 @@ replicate :: Int -> Int -> Stream m Int
150149replicate = S.replicate
151150
152151{-# INLINE replicateM #-}
153−replicateM :: S.MonadAsync m => Int -> Int -> Stream m Int
152+replicateM :: Monad m => Int -> Int -> Stream m Int
154153replicateM streamLen = S.replicateMWith S.consM streamLen . return
155154
156155{-# INLINE iterate #-}
@@ -158,7 +157,7 @@ iterate :: Int -> Int -> Stream m Int
158157iterate streamLen = S.take streamLen . S.iterate (+1)
159158
160159{-# INLINE iterateM #-}
161−iterateM :: S.MonadAsync m => Int -> Int -> Stream m Int
160+iterateM :: Monad m => Int -> Int -> Stream m Int
162161iterateM streamLen = S.take streamLen . S.iterateM (return . (+1)) . return
163162
164163{-# INLINE fromFoldable #-}
@@ -166,7 +165,7 @@ fromFoldable :: Int -> Int -> Stream m Int
166165fromFoldable streamLen n = S.fromFoldable [n..n+streamLen]
167166
168167{-# INLINE fromFoldableM #-}
169−fromFoldableM :: S.MonadAsync m => Int -> Int -> Stream m Int
168+fromFoldableM :: Monad m => Int -> Int -> Stream m Int
170169fromFoldableM streamLen n =
171170 Prelude.foldr S.consM S.nil (Prelude.fmap return [n..n+streamLen])
172171
@@ -180,12 +179,13 @@ concatMapFoldableWith f g = Prelude.foldr (f . g) S.nil
180179
181180{-# INLINE concatMapFoldableSerial #-}
182181concatMapFoldableSerial :: Int -> Int -> Stream m Int
183−concatMapFoldableSerial streamLen n = concatMapFoldableWith S.serial S.fromPure [n..n+streamLen]
182+concatMapFoldableSerial streamLen n =
183+ concatMapFoldableWith S.append S.fromPure [n..n+streamLen]
184184
185185{-# INLINE concatMapFoldableSerialM #-}
186186concatMapFoldableSerialM :: Monad m => Int -> Int -> Stream m Int
187187concatMapFoldableSerialM streamLen n =
188− concatMapFoldableWith S.serial (S.fromEffect . return) [n..n+streamLen]
188+ concatMapFoldableWith S.append (S.fromEffect . return) [n..n+streamLen]
189189
190190-------------------------------------------------------------------------------
191191-- Elimination
@@ -270,11 +270,11 @@ fmapK :: Monad m => Int -> Stream m Int -> m ()
270270fmapK n = composeN n $ P.fmap (+ 1)
271271
272272{-# INLINE mapM #-}
273−mapM :: S.MonadAsync m => Int -> Stream m Int -> m ()
273+mapM :: Monad m => Int -> Stream m Int -> m ()
274274mapM n = composeN n $ S.mapMWith S.consM return
275275
276276{-# INLINE mapMSerial #-}
277−mapMSerial :: S.MonadAsync m => Int -> Stream m Int -> m ()
277+mapMSerial :: Monad m => Int -> Stream m Int -> m ()
278278mapMSerial n = composeN n $ S.mapMSerial return
279279
280280{-# INLINE filterEven #-}
@@ -326,7 +326,7 @@ foldlS :: Monad m => Int -> Stream m Int -> m ()
326326foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
327327
328328{-# INLINE intersperse #-}
329−intersperse :: S.MonadAsync m => Int -> Int -> Stream m Int -> m ()
329+intersperse :: Monad m => Int -> Int -> Stream m Int -> m ()
330330intersperse streamLen n = composeN n $ S.intersperse streamLen
331331
332332-------------------------------------------------------------------------------
@@ -335,8 +335,7 @@ intersperse streamLen n = composeN n $ S.intersperse streamLen
335335
336336{-# INLINE iterateSource #-}
337337iterateSource
338− :: S.MonadAsync m
339− => Int -> (Stream m Int -> Stream m Int) -> Int -> Int -> Stream m Int
338+ :: Monad m => Int -> (Stream m Int -> Stream m Int) -> Int -> Int -> Stream m Int
340339iterateSource iterStreamLen g i n = f i (unfoldrM iterStreamLen n)
341340 where
342341 f (0 :: Int) m = g m
@@ -344,37 +343,37 @@ iterateSource iterStreamLen g i n = f i (unfoldrM iterStreamLen n)
344343
345344-- this is quadratic
346345{-# INLINE iterateScan #-}
347−iterateScan :: S.MonadAsync m => Int -> Int -> Int -> Stream m Int
346+iterateScan :: Monad m => Int -> Int -> Int -> Stream m Int
348347iterateScan iterStreamLen maxIters =
349348 iterateSource iterStreamLen (S.scanl' (+) 0) (maxIters `div` 10)
350349
351350-- this is quadratic
352351{-# INLINE iterateDropWhileFalse #-}
353−iterateDropWhileFalse :: S.MonadAsync m => Int -> Int -> Int -> Int -> Stream m Int
352+iterateDropWhileFalse :: Monad m => Int -> Int -> Int -> Int -> Stream m Int
354353iterateDropWhileFalse streamLen iterStreamLen maxIters =
355354 iterateSource iterStreamLen (S.dropWhile (> streamLen)) (maxIters `div` 10)
356355
357356{-# INLINE iterateMapM #-}
358−iterateMapM :: S.MonadAsync m => Int -> Int -> Int -> Stream m Int
357+iterateMapM :: Monad m => Int -> Int -> Int -> Stream m Int
359358iterateMapM iterStreamLen =
360359 iterateSource iterStreamLen (S.mapMWith S.consM return)
361360
362361{-# INLINE iterateFilterEven #-}
363−iterateFilterEven :: S.MonadAsync m => Int -> Int -> Int -> Stream m Int
362+iterateFilterEven :: Monad m => Int -> Int -> Int -> Stream m Int
364363iterateFilterEven iterStreamLen = iterateSource iterStreamLen (S.filter even)
365364
366365{-# INLINE iterateTakeAll #-}
367−iterateTakeAll :: S.MonadAsync m => Int -> Int -> Int -> Int -> Stream m Int
366+iterateTakeAll :: Monad m => Int -> Int -> Int -> Int -> Stream m Int
368367iterateTakeAll streamLen iterStreamLen =
369368 iterateSource iterStreamLen (S.take streamLen)
370369
371370{-# INLINE iterateDropOne #-}
372−iterateDropOne :: S.MonadAsync m => Int -> Int -> Int -> Stream m Int
371+iterateDropOne :: Monad m => Int -> Int -> Int -> Stream m Int
373372iterateDropOne iterStreamLen = iterateSource iterStreamLen (S.drop 1)
374373
375374{-# INLINE iterateDropWhileTrue #-}
376−iterateDropWhileTrue :: S.MonadAsync m =>
377− Int -> Int -> Int -> Int -> Stream m Int
375+iterateDropWhileTrue ::
376+ Monad m => Int -> Int -> Int -> Int -> Stream m Int
378377iterateDropWhileTrue streamLen iterStreamLen =
379378 iterateSource iterStreamLen (S.dropWhile (<= streamLen))
380379
@@ -392,7 +391,7 @@ zipWithM src = drain $ S.zipWithM (curry return) src src
392391
393392{-# INLINE sortByK #-}
394393sortByK :: (a -> a -> Ordering) -> Stream m a -> Stream m a
395−sortByK f = S.concatPairsWith (S.mergeBy f) S.fromPure
394+sortByK f = S.mergeMapWith (S.mergeBy f) S.fromPure
396395
397396{-# INLINE sortBy #-}
398397sortBy :: Monad m => Stream m Int -> m ()
@@ -494,7 +493,7 @@ sourceConcatMapId val n =
494493{-# INLINE concatMapBySerial #-}
495494concatMapBySerial :: Int -> Int -> Int -> IO ()
496495concatMapBySerial outer inner n =
497− S.drain $ S.concatMapWith S.serial
496+ S.drain $ S.concatMapWith S.append
498497 (unfoldrM inner)
499498 (unfoldrM outer n)
500499
@@ -731,7 +730,7 @@ o_1_space_concat streamLen =
731730 -- This is for comparison with concatMapFoldableWith
732731 , benchIOSrc1 "concatMapWithId (n of 1) (fromFoldable)"
733732 (S.drain
734− . S.concatMapWith S.serial id
733+ . S.concatMapWith S.append id
735734 . sourceConcatMapId streamLen)
736735
737736 , benchIOSrc1 "concatMapBy serial (n of 1)"
benchmark/Streamly/Benchmark/Data/Stream/ToStreamK.hs+14−10
@@ -33,11 +33,9 @@ import Prelude hiding
3333import qualified Prelude as P
3434-- import qualified Data.List as List
3535
36−import qualified Streamly.Internal.Control.Concurrent as S
3736import qualified Streamly.Internal.Data.Stream.StreamK.Type as S
3837import qualified Streamly.Internal.Data.Stream.StreamK as S
3938import qualified Streamly.Internal.Data.Stream.StreamD as D
40−-- import qualified Streamly.Internal.Data.Stream as Stream
4139import qualified Streamly.Internal.Data.Fold as Fold
4240
4341import Gauge (bgroup, Benchmark, defaultMain)
@@ -64,7 +62,7 @@ unfoldrD streamLen n = D.toStreamK (D.unfoldr step n)
6462 else Just (cnt, cnt + 1)
6563
6664{-# INLINE unfoldrMD #-}
67−unfoldrMD :: S.MonadAsync m => Int -> Int -> Stream m Int
65+unfoldrMD :: Monad m => Int -> Int -> Stream m Int
6866unfoldrMD streamLen n = D.toStreamK (D.unfoldrM step n)
6967 where
7068 step cnt =
@@ -84,7 +82,7 @@ unfoldrK streamLen n = S.unfoldr step n
8482-}
8583
8684{-# INLINE unfoldrMK #-}
87−unfoldrMK :: S.MonadAsync m => Int -> Int -> Stream m Int
85+unfoldrMK :: Monad m => Int -> Int -> Stream m Int
8886unfoldrMK streamLen n = S.unfoldrMWith S.consM step n
8987 where
9088 step cnt =
@@ -97,7 +95,7 @@ repeat :: Monad m => Int -> Int -> Stream m Int
9795repeat streamLen = S.take streamLen . D.toStreamK . D.repeat
9896
9997{-# INLINE repeatM #-}
100−repeatM :: S.MonadAsync m => Int -> Int -> Stream m Int
98+repeatM :: Monad m => Int -> Int -> Stream m Int
10199repeatM streamLen = S.take streamLen . D.toStreamK . D.repeatM . return
102100
103101{-# INLINE replicate #-}
@@ -105,7 +103,7 @@ replicate :: Monad m => Int -> Int -> Stream m Int
105103replicate x y = D.toStreamK $ D.replicate x y
106104
107105{-# INLINE replicateM #-}
108−replicateM :: S.MonadAsync m => Int -> Int -> Stream m Int
106+replicateM :: Monad m => Int -> Int -> Stream m Int
109107replicateM streamLen = D.toStreamK . D.replicateM streamLen . return
110108
111109{-# INLINE iterate #-}
@@ -113,7 +111,7 @@ iterate :: Monad m => Int -> Int -> Stream m Int
113111iterate streamLen = S.take streamLen . D.toStreamK . D.iterate (+1)
114112
115113{-# INLINE iterateM #-}
116−iterateM :: S.MonadAsync m => Int -> Int -> Stream m Int
114+iterateM :: Monad m => Int -> Int -> Stream m Int
117115iterateM streamLen = S.take streamLen . D.toStreamK . D.iterateM (return . (+1)) . return
118116
119117{-# INLINE fromFoldable #-}
@@ -121,7 +119,7 @@ fromFoldable :: Int -> Int -> Stream m Int
121119fromFoldable streamLen n = S.fromFoldable [n..n+streamLen]
122120
123121{-# INLINE fromFoldableM #-}
124−fromFoldableM :: S.MonadAsync m => Int -> Int -> Stream m Int
122+fromFoldableM :: Monad m => Int -> Int -> Stream m Int
125123fromFoldableM streamLen n =
126124 Prelude.foldr S.consM S.nil (Prelude.fmap return [n..n+streamLen])
127125
@@ -225,7 +223,7 @@ composeN n f =
225223{-# INLINE scanl' #-}
226224scanl' :: Monad m => Int -> Stream m Int -> m ()
227225scanl' n =
228− composeN n (D.toStreamK . D.scanOnce (Fold.foldl' (+) 0) . D.fromStreamK)
226+ composeN n (D.toStreamK . D.scan (Fold.foldl' (+) 0) . D.fromStreamK)
229227
230228{-# INLINE map #-}
231229map :: Monad m => Int -> Stream m Int -> m ()
@@ -238,7 +236,7 @@ fmapK n = composeN n $ P.fmap (+ 1)
238236-}
239237
240238{-# INLINE mapM #-}
241−mapM :: S.MonadAsync m => Int -> Stream m Int -> m ()
239+mapM :: Monad m => Int -> Stream m Int -> m ()
242240mapM n = composeN n (D.toStreamK . D.mapM return . D.fromStreamK)
243241
244242{-
@@ -620,6 +618,8 @@ filterAllInNestedList str = do
620618moduleName :: String
621619moduleName = "Data.Stream.ToStreamK"
622620
621+-- Generation of StreamK using StreamD generation functions and eleminating
622+-- using StreamK drain.
623623o_1_space_generation :: Int -> Benchmark
624624o_1_space_generation streamLen =
625625 bgroup "generation"
@@ -640,6 +640,7 @@ o_1_space_generation streamLen =
640640 , benchFold "concatMapFoldableWithM" drain (concatMapFoldableSerialM streamLen)
641641 ]
642642
643+-- Generating using StreamK and eliminating using StreamD folds.
643644o_1_space_elimination :: Int -> Benchmark
644645o_1_space_elimination streamLen =
645646 bgroup "elimination"
@@ -671,6 +672,8 @@ o_1_space_nested streamLen =
671672 streamLen3 = round (P.fromIntegral streamLen**(1/3::P.Double)) -- triple nested loop
672673-}
673674
675+-- Generate using StreamK and transform using StreamD transformation functions
676+-- and then drain using StreamK.
674677o_1_space_transformation :: Int -> Benchmark
675678o_1_space_transformation streamLen =
676679 bgroup "transformation"
@@ -694,6 +697,7 @@ o_1_space_transformationX4 streamLen =
694697 -- , benchFold "concatMap" (concatMap 4) (unfoldrM streamLen16)
695698 ]
696699
700+-- Generate using K, fold using K, concat using D.concatMap
697701o_1_space_concat :: Int -> Benchmark
698702o_1_space_concat streamLen =
699703 bgroup "concat"
benchmark/Streamly/Benchmark/Data/Stream/Transform.hs+137−30
@@ -26,32 +26,47 @@
2626
2727module Stream.Transform (benchmarks) where
2828
29−import Control.DeepSeq (NFData(..))
3029import Control.Monad.IO.Class (MonadIO(..))
31−import Data.Functor.Identity (Identity(..))
3230
3331import System.Random (randomRIO)
3432
3533import qualified Streamly.Internal.Data.Fold as FL
3634
37−import qualified Prelude
3835import qualified Stream.Common as Common
39−import qualified Streamly.Internal.Data.Fold as Fold
4036import qualified Streamly.Internal.Data.Unfold as Unfold
37+
4138#ifdef USE_PRELUDE
39+import Control.DeepSeq (NFData(..))
40+import Data.Functor.Identity (Identity(..))
41+import qualified Prelude
42+import qualified Streamly.Internal.Data.Fold as Fold
4243import qualified Streamly.Internal.Data.Stream.IsStream as Stream
4344import Streamly.Internal.Data.Time.Units
4445#else
46+import Streamly.Internal.Data.Stream.StreamD (Stream)
47+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
48+#ifndef USE_STREAMLY_CORE
4549import qualified Streamly.Internal.Data.Stream.Time as Stream
46−import qualified Streamly.Internal.Data.Stream as Stream
50+#endif
51+#ifdef USE_STREAMK
52+import Control.DeepSeq (NFData(..))
53+import Data.Functor.Identity (Identity(..))
54+import qualified Prelude
55+import qualified Streamly.Internal.Data.Fold as Fold
56+import Streamly.Internal.Data.Stream.StreamK (StreamK)
57+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
58+#endif
4759#endif
4860
4961import Gauge
50−import Streamly.Internal.Data.Stream (Stream)
5162import Stream.Common hiding (scanl')
5263import Streamly.Benchmark.Common
5364import Prelude hiding (sequence, mapM)
5465
66+#ifdef USE_PRELUDE
67+type Stream = Stream.SerialT
68+#endif
69+
5570-------------------------------------------------------------------------------
5671-- Pipelines (stream-to-stream transformations)
5772-------------------------------------------------------------------------------
@@ -64,32 +79,36 @@ import Prelude hiding (sequence, mapM)
6479-- Traversable Instance
6580-------------------------------------------------------------------------------
6681
82+#ifdef USE_STREAMK
6783{-# INLINE traversableTraverse #-}
68−traversableTraverse :: Stream Identity Int -> IO (Stream Identity Int)
84+traversableTraverse :: StreamK Identity Int -> IO (StreamK Identity Int)
6985traversableTraverse = traverse return
7086
7187{-# INLINE traversableSequenceA #-}
72−traversableSequenceA :: Stream Identity Int -> IO (Stream Identity Int)
88+traversableSequenceA :: StreamK Identity Int -> IO (StreamK Identity Int)
7389traversableSequenceA = sequenceA . Prelude.fmap return
7490
7591{-# INLINE traversableMapM #-}
76−traversableMapM :: Stream Identity Int -> IO (Stream Identity Int)
92+traversableMapM :: StreamK Identity Int -> IO (StreamK Identity Int)
7793traversableMapM = Prelude.mapM return
7894
7995{-# INLINE traversableSequence #-}
80−traversableSequence :: Stream Identity Int -> IO (Stream Identity Int)
96+traversableSequence :: StreamK Identity Int -> IO (StreamK Identity Int)
8197traversableSequence = Prelude.sequence . Prelude.fmap return
8298
8399{-# INLINE benchPureSinkIO #-}
84100benchPureSinkIO
85101 :: NFData b
86− => Int -> String -> (Stream Identity Int -> IO b) -> Benchmark
102+ => Int -> String -> (StreamK Identity Int -> IO b) -> Benchmark
87103benchPureSinkIO value name f =
88− bench name $ nfIO $ randomRIO (1, 1) >>= f . sourceUnfoldr value
104+ bench name
105+ $ nfIO $ randomRIO (1, 1) >>= f . fromStream . sourceUnfoldr value
89106
90−instance NFData a => NFData (Stream Identity a) where
107+instance NFData a => NFData (StreamK Identity a) where
91108 {-# INLINE rnf #-}
92− rnf xs = runIdentity $ Stream.fold (Fold.foldl' (\_ x -> rnf x) ()) xs
109+ rnf xs =
110+ runIdentity
111+ $ Stream.fold (Fold.foldl' (\_ x -> rnf x) ()) (toStream xs)
93112
94113o_n_space_traversable :: Int -> [Benchmark]
95114o_n_space_traversable value =
@@ -102,6 +121,7 @@ o_n_space_traversable value =
102121 , benchPureSinkIO value "sequence" traversableSequence
103122 ]
104123 ]
124+#endif
105125
106126-------------------------------------------------------------------------------
107127-- maps and scans
@@ -166,13 +186,21 @@ timestamped :: (MonadAsync m) => Stream m Int -> m ()
166186timestamped = Stream.drain . Stream.timestamped
167187#endif
168188
189+#ifdef USE_STREAMK
169190{-# INLINE foldrS #-}
170191foldrS :: MonadIO m => Int -> Stream m Int -> m ()
171−foldrS n = composeN n $ Stream.foldrS Stream.cons Stream.nil
192+foldrS n =
193+ composeN n (toStream . StreamK.foldrS StreamK.cons StreamK.nil . fromStream)
172194
173195{-# INLINE foldrSMap #-}
174196foldrSMap :: MonadIO m => Int -> Stream m Int -> m ()
175−foldrSMap n = composeN n $ Stream.foldrS (\x xs -> x + 1 `Stream.cons` xs) Stream.nil
197+foldrSMap n =
198+ composeN n
199+ ( toStream
200+ . StreamK.foldrS (\x xs -> x + 1 `StreamK.cons` xs) StreamK.nil
201+ . fromStream
202+ )
203+#endif
176204
177205{-
178206{-# INLINE foldrT #-}
@@ -195,14 +223,17 @@ o_1_space_mapping value =
195223 [ bgroup
196224 "mapping"
197225 [
226+#ifdef USE_STREAMK
198227 -- Right folds
199228 benchIOSink value "foldrS" (foldrS 1)
200229 , benchIOSink value "foldrSMap" (foldrSMap 1)
230+ ,
231+#endif
201232 -- , benchIOSink value "foldrT" (foldrT 1)
202233 -- , benchIOSink value "foldrTMap" (foldrTMap 1)
203234
204235 -- Mapping
205− , benchIOSink value "map" (mapN 1)
236+ benchIOSink value "map" (mapN 1)
206237 , bench "sequence" $ nfIO $ randomRIO (1, 1000) >>= \n ->
207238 sequence (sourceUnfoldrAction value n)
208239 , benchIOSink value "mapM" (mapM 1)
@@ -273,6 +304,73 @@ o_1_space_functor value =
273304 ]
274305 ]
275306
307+-------------------------------------------------------------------------------
308+-- Iteration/looping utilities
309+-------------------------------------------------------------------------------
310+
311+{-# INLINE iterateN #-}
312+iterateN :: (Int -> a -> a) -> a -> Int -> a
313+iterateN g initial count = f count initial
314+
315+ where
316+
317+ f (0 :: Int) x = x
318+ f i x = f (i - 1) (g i x)
319+
320+#ifdef USE_STREAMK
321+-- Iterate a transformation over a singleton stream
322+{-# INLINE iterateSingleton #-}
323+iterateSingleton :: Applicative m =>
324+ (Int -> StreamK m Int -> StreamK m Int)
325+ -> Int
326+ -> Int
327+ -> Stream m Int
328+iterateSingleton g count n = toStream $ iterateN g (StreamK.fromPure n) count
329+#else
330+-- Iterate a transformation over a singleton stream
331+{-# INLINE iterateSingleton #-}
332+iterateSingleton :: Applicative m =>
333+ (Int -> Stream m Int -> Stream m Int)
334+ -> Int
335+ -> Int
336+ -> Stream m Int
337+iterateSingleton g count n = iterateN g (Stream.fromPure n) count
338+#endif
339+
340+{-
341+-- XXX need to check why this is slower than the explicit recursion above, even
342+-- if the above code is written in a foldr like head recursive way. We also
343+-- need to try this with foldlM' once #150 is fixed.
344+-- However, it is perhaps best to keep the iteration benchmarks independent of
345+-- foldrM and any related fusion issues.
346+{-# INLINE _iterateSingleton #-}
347+_iterateSingleton ::
348+ Monad m
349+ => (Int -> Stream m Int -> Stream m Int)
350+ -> Int
351+ -> Int
352+ -> Stream m Int
353+_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
354+-}
355+
356+o_n_space_iterated :: Int -> [Benchmark]
357+o_n_space_iterated value =
358+ [ bgroup "iterated"
359+ [ benchIO "(+) (n times) (baseline)" $ \i0 ->
360+ iterateN (\i acc -> acc >>= \n -> return $ i + n) (return i0) value
361+ , benchIOSrc "(<$) (n times)" $
362+ iterateSingleton (<$) value
363+ , benchIOSrc "fmap (n times)" $
364+ iterateSingleton (fmap . (+)) value
365+ {-
366+ , benchIOSrc fromSerial "_(<$) (n times)" $
367+ _iterateSingleton (<$) value
368+ , benchIOSrc fromSerial "_fmap (n times)" $
369+ _iterateSingleton (fmap . (+)) value
370+ -}
371+ ]
372+ ]
373+
276374-------------------------------------------------------------------------------
277375-- Size reducing transformations (filtering)
278376-------------------------------------------------------------------------------
@@ -317,6 +415,7 @@ takeWhileTrue value n = composeN n $ Stream.takeWhile (<= (value + 1))
317415takeWhileMTrue :: MonadIO m => Int -> Int -> Stream m Int -> m ()
318416takeWhileMTrue value n = composeN n $ Stream.takeWhileM (return . (<= (value + 1)))
319417
418+#if !defined(USE_STREAMLY_CORE) && !defined(USE_PRELUDE)
320419{-# INLINE takeInterval #-}
321420takeInterval :: Double -> Int -> Stream IO Int -> IO ()
322421takeInterval i n = composeN n (Stream.takeInterval i)
@@ -329,6 +428,18 @@ takeInterval i n = composeN n (Stream.takeInterval i)
329428-- inspect $ 'takeInterval `hasNoType` ''D.Step
330429#endif
331430
431+{-# INLINE dropInterval #-}
432+dropInterval :: Double -> Int -> Stream IO Int -> IO ()
433+dropInterval i n = composeN n (Stream.dropInterval i)
434+
435+-- Inspection testing is disabled for dropInterval
436+-- Enable it when looking at it throughly
437+#ifdef INSPECTION
438+-- inspect $ hasNoTypeClasses 'dropInterval
439+-- inspect $ 'dropInterval `hasNoType` ''D.Step
440+#endif
441+#endif
442+
332443{-# INLINE dropOne #-}
333444dropOne :: MonadIO m => Int -> Stream m Int -> m ()
334445dropOne n = composeN n $ Stream.drop 1
@@ -356,17 +467,6 @@ _intervalsOfSum :: MonadAsync m => Double -> Int -> Stream m Int -> m ()
356467_intervalsOfSum i n = composeN n (Stream.intervalsOf i FL.sum)
357468#endif
358469
359−{-# INLINE dropInterval #-}
360−dropInterval :: Double -> Int -> Stream IO Int -> IO ()
361−dropInterval i n = composeN n (Stream.dropInterval i)
362−
363−-- Inspection testing is disabled for dropInterval
364−-- Enable it when looking at it throughly
365−#ifdef INSPECTION
366−-- inspect $ hasNoTypeClasses 'dropInterval
367−-- inspect $ 'dropInterval `hasNoType` ''D.Step
368−#endif
369−
370470{-# INLINE findIndices #-}
371471findIndices :: MonadIO m => Int -> Int -> Stream m Int -> m ()
372472findIndices value n = composeN n $ Stream.findIndices (== (value + 1))
@@ -421,8 +521,10 @@ o_1_space_filtering value =
421521 -- , benchIOSink value "takeWhileM-true" (_takeWhileMTrue value 1)
422522 , benchIOSink value "drop-one" (dropOne 1)
423523 , benchIOSink value "drop-all" (dropAll value 1)
524+#if !defined(USE_STREAMLY_CORE) && !defined(USE_PRELUDE)
424525 , benchIOSink value "takeInterval-all" (takeInterval 10000 1)
425526 , benchIOSink value "dropInterval-all" (dropInterval 10000 1)
527+#endif
426528 , benchIOSink value "dropWhile-true" (dropWhileTrue value 1)
427529 -- , benchIOSink value "dropWhileM-true" (_dropWhileMTrue value 1)
428530 , benchIOSink
@@ -574,7 +676,12 @@ benchmarks moduleName size =
574676 , o_1_space_indexingX4 size
575677 ]
576678 , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
577− [ o_n_space_traversable size
578− , o_n_space_mapping size
679+ [
680+#ifdef USE_STREAMK
681+ o_n_space_traversable size
682+ ,
683+#endif
684+ o_n_space_mapping size
685+ , o_n_space_iterated size
579686 ]
580687 ]
benchmark/Streamly/Benchmark/Data/Unfold.hs+1−2
@@ -32,11 +32,10 @@ import qualified Streamly.FileSystem.Handle as FH
3232import qualified Streamly.Internal.Data.Fold as FL
3333import qualified Streamly.Internal.Data.Unfold as UF
3434import qualified Streamly.Internal.Data.Unfold.Exception as UF
35−import qualified Streamly.Internal.Data.Stream as S
35+import qualified Streamly.Internal.Data.Stream.StreamD as S
3636import qualified Streamly.Internal.Data.Stream.StreamD as D
3737import qualified Streamly.Internal.Data.Stream.StreamK as K
3838
39−
4039import Gauge hiding (env)
4140import Prelude hiding (take, filter, zipWith, map, mapM, takeWhile)
4241import Streamly.Benchmark.Common
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+23−16
@@ -29,14 +29,16 @@ import GHC.Magic (inline)
2929import GHC.Magic (noinline)
3030import System.IO (Handle)
3131
32+import qualified Streamly.Data.Stream as Stream
33+import qualified Streamly.Data.Fold as Fold
3234import qualified Streamly.FileSystem.Handle as FH
3335import qualified Streamly.Internal.Data.Array as A
3436import qualified Streamly.Internal.Data.Array.Type as AT
3537import qualified Streamly.Internal.Data.Fold as FL
36−import qualified Streamly.Internal.Data.Stream.IsStream as IP
38+import qualified Streamly.Internal.Data.Stream.StreamD as IP
3739import qualified Streamly.Internal.FileSystem.Handle as IFH
3840import qualified Streamly.Internal.Unicode.Stream as IUS
39−import qualified Streamly.Prelude as S
41+import qualified Streamly.Data.Stream.Prelude as S
4042import qualified Streamly.Unicode.Stream as SS
4143
4244import Gauge hiding (env)
@@ -61,7 +63,7 @@ import Test.Inspection
6163
6264-- | Get the last byte from a file bytestream.
6365readLast :: Handle -> IO (Maybe Word8)
64−readLast = S.last . S.unfold FH.reader
66+readLast = S.fold Fold.last . S.unfold FH.reader
6567
6668#ifdef INSPECTION
6769inspect $ hasNoTypeClasses 'readLast
@@ -73,7 +75,7 @@ inspect $ 'readLast `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
7375-- assert that flattenArrays constructors are not present
7476-- | Count the number of bytes in a file.
7577readCountBytes :: Handle -> IO Int
76−readCountBytes = S.length . S.unfold FH.reader
78+readCountBytes = S.fold Fold.length . S.unfold FH.reader
7779
7880#ifdef INSPECTION
7981inspect $ hasNoTypeClasses 'readCountBytes
@@ -85,7 +87,7 @@ inspect $ 'readCountBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
8587-- | Count the number of lines in a file.
8688readCountLines :: Handle -> IO Int
8789readCountLines =
88− S.length
90+ S.fold Fold.length
8991 . IUS.lines FL.drain
9092 . SS.decodeLatin1
9193 . S.unfold FH.reader
@@ -100,7 +102,7 @@ inspect $ 'readCountLines `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
100102-- | Count the number of words in a file.
101103readCountWords :: Handle -> IO Int
102104readCountWords =
103− S.length
105+ S.fold Fold.length
104106 . IUS.words FL.drain
105107 . SS.decodeLatin1
106108 . S.unfold FH.reader
@@ -112,7 +114,7 @@ inspect $ hasNoTypeClasses 'readCountWords
112114
113115-- | Sum the bytes in a file.
114116readSumBytes :: Handle -> IO Word8
115−readSumBytes = S.sum . S.unfold FH.reader
117+readSumBytes = S.fold Fold.sum . S.unfold FH.reader
116118
117119#ifdef INSPECTION
118120inspect $ hasNoTypeClasses 'readSumBytes
@@ -132,19 +134,19 @@ inspect $ 'readSumBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
132134-- fusion-plugin to propagate INLINE phase information such that this problem
133135-- does not occur.
134136readDrain :: Handle -> IO ()
135−readDrain inh = S.drain $ S.unfold FH.reader inh
137+readDrain inh = S.fold Fold.drain $ S.unfold FH.reader inh
136138
137139-- XXX investigate why we need an INLINE in this case (GHC)
138140{-# INLINE readDecodeLatin1 #-}
139141readDecodeLatin1 :: Handle -> IO ()
140142readDecodeLatin1 inh =
141− S.drain
143+ S.fold Fold.drain
142144 $ SS.decodeLatin1
143145 $ S.unfold FH.reader inh
144146
145147readDecodeUtf8 :: Handle -> IO ()
146148readDecodeUtf8 inh =
147− S.drain
149+ S.fold Fold.drain
148150 $ SS.decodeUtf8
149151 $ S.unfold FH.reader inh
150152
@@ -187,7 +189,7 @@ o_1_space_reduce_read env =
187189-- | Count the number of lines in a file.
188190getChunksConcatUnfoldCountLines :: Handle -> IO Int
189191getChunksConcatUnfoldCountLines inh =
190− S.length
192+ S.fold Fold.length
191193 $ IUS.lines FL.drain
192194 $ SS.decodeLatin1
193195 -- XXX replace with toBytes
@@ -212,15 +214,18 @@ o_1_space_reduce_toBytes env =
212214-------------------------------------------------------------------------------
213215
214216chunksOfSum :: Int -> Handle -> IO Int
215−chunksOfSum n inh = S.length $ S.chunksOf n FL.sum (S.unfold FH.reader inh)
217+chunksOfSum n inh =
218+ S.fold Fold.length $ IP.chunksOf n FL.sum (S.unfold FH.reader inh)
216219
217220foldManyPostChunksOfSum :: Int -> Handle -> IO Int
218221foldManyPostChunksOfSum n inh =
219− S.length $ IP.foldManyPost (FL.take n FL.sum) (S.unfold FH.reader inh)
222+ S.fold Fold.length
223+ $ IP.foldManyPost (FL.take n FL.sum) (S.unfold FH.reader inh)
220224
221225foldManyChunksOfSum :: Int -> Handle -> IO Int
222226foldManyChunksOfSum n inh =
223− S.length $ IP.foldMany (FL.take n FL.sum) (S.unfold FH.reader inh)
227+ S.fold Fold.length
228+ $ IP.foldMany (FL.take n FL.sum) (S.unfold FH.reader inh)
224229
225230-- XXX investigate why we need an INLINE in this case (GHC)
226231-- Even though allocations remain the same in both cases inlining improves time
@@ -230,7 +235,8 @@ foldManyChunksOfSum n inh =
230235chunksOf :: Int -> Handle -> IO Int
231236chunksOf n inh =
232237 -- writeNUnsafe gives 2.5x boost here over writeN.
233− S.length $ S.chunksOf n (AT.writeNUnsafe n) (S.unfold FH.reader inh)
238+ S.fold Fold.length
239+ $ IP.chunksOf n (AT.writeNUnsafe n) (S.unfold FH.reader inh)
234240
235241#ifdef INSPECTION
236242inspect $ hasNoTypeClasses 'chunksOf
@@ -243,7 +249,8 @@ inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
243249
244250{-# INLINE arraysOf #-}
245251arraysOf :: Int -> Handle -> IO Int
246−arraysOf n inh = S.length $ IP.arraysOf n (S.unfold FH.reader inh)
252+arraysOf n inh =
253+ S.fold Fold.length $ Stream.arraysOf n (S.unfold FH.reader inh)
247254
248255o_1_space_reduce_read_grouped :: BenchEnv -> [Benchmark]
249256o_1_space_reduce_read_grouped env =
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+1−1
@@ -32,7 +32,7 @@ import qualified Streamly.FileSystem.Handle as FH
3232import qualified Streamly.Internal.Data.Unfold as IUF
3333import qualified Streamly.Internal.FileSystem.Handle as IFH
3434import qualified Streamly.Data.Array as A
35−import qualified Streamly.Prelude as S
35+import qualified Streamly.Data.Stream.Prelude as S
3636
3737import Gauge hiding (env)
3838import Streamly.Benchmark.Common.Handle
benchmark/Streamly/Benchmark/Unicode/Char.hs+1−1
@@ -24,7 +24,7 @@ import Streamly.Internal.Unicode.Char
2424import Streamly.Benchmark.Common (o_1_space_prefix)
2525
2626import qualified Streamly.Internal.Data.Array as Array
27−import qualified Streamly.Internal.Data.Stream.IsStream as IsStream
27+import qualified Streamly.Internal.Data.Stream.StreamD as IsStream
2828import qualified System.Directory as Dir
2929
3030--------------------------------------------------------------------------------
benchmark/Streamly/Benchmark/Unicode/Stream.hs+12−4
@@ -20,12 +20,14 @@
2020{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
2121#endif
2222
23+import Streamly.Data.Stream (Stream)
24+import Streamly.Data.Fold (Fold)
2325import Prelude hiding (last, length)
2426import System.IO (Handle)
2527
2628import qualified Streamly.Data.Array as Array
2729import qualified Streamly.Data.Fold as Fold
28−import qualified Streamly.Internal.Data.Stream.IsStream as Stream
30+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
2931import qualified Streamly.Internal.Data.Unfold as Unfold
3032import qualified Streamly.Internal.FileSystem.Handle as Handle
3133import qualified Streamly.Internal.Unicode.Array as UnicodeArr
@@ -75,13 +77,19 @@ o_1_space_decode_encode_chunked env =
7577-- copy with group/ungroup transformations
7678-------------------------------------------------------------------------------
7779
80+{-# INLINE splitOnSuffix #-}
81+splitOnSuffix
82+ :: (Monad m)
83+ => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
84+splitOnSuffix predicate f = Stream.foldMany (Fold.takeEndBy_ predicate f)
85+
7886{-# NOINLINE linesUnlinesCopy #-}
7987linesUnlinesCopy :: Handle -> Handle -> IO ()
8088linesUnlinesCopy inh outh =
8189 Stream.fold (Handle.write outh)
8290 $ Unicode.encodeLatin1'
8391 $ Unicode.unlines Unfold.fromList
84− $ Stream.splitOnSuffix (== '\n') Fold.toList
92+ $ splitOnSuffix (== '\n') Fold.toList
8593 $ Unicode.decodeLatin1
8694 $ Stream.unfold Handle.reader inh
8795
@@ -90,7 +98,7 @@ linesUnlinesArrayWord8Copy :: Handle -> Handle -> IO ()
9098linesUnlinesArrayWord8Copy inh outh =
9199 Stream.fold (Handle.write outh)
92100 $ Stream.interposeSuffix 10 Array.reader
93− $ Stream.splitOnSuffix (== 10) Array.write
101+ $ splitOnSuffix (== 10) Array.write
94102 $ Stream.unfold Handle.reader inh
95103
96104-- XXX splitSuffixOn requires -funfolding-use-threshold=150 for better fusion
@@ -280,7 +288,7 @@ _copyStreamUtf8Parser :: Handle -> Handle -> IO ()
280288_copyStreamUtf8Parser inh outh =
281289 Stream.fold (Handle.write outh)
282290 $ Unicode.encodeUtf8
283− $ Stream.rights $ Stream.parseMany
291+ $ Stream.catRights $ Stream.parseMany
284292 (Unicode.parseCharUtf8With Unicode.TransliterateCodingFailure)
285293 $ Stream.unfold Handle.reader inh
286294
benchmark/bench-runner/Main.hs+45−39
@@ -14,20 +14,19 @@ rtsOpts exeName benchName0 = unwords [general, exeSpecific, benchSpecific]
1414
1515 -- Drop All.
1616 benchName = drop 4 benchName0
17+
1718 general
1819 | "o-1-sp" `isInfixOf` benchName = "-K36K -M16M"
1920 | "o-n-h" `isInfixOf` benchName = "-K36K -M32M"
2021 | "o-n-st" `isInfixOf` benchName = "-K1M -M16M"
2122 | "o-n-sp" `isInfixOf` benchName = "-K1M -M32M"
2223 | otherwise = ""
24+
2325 exeSpecific
2426 | "Prelude.Concurrent" `isSuffixOf` exeName = "-K512K -M384M"
2527 | otherwise = ""
28+
2629 benchSpecific
27− | "Data.Stream.StreamD/o-n-space.elimination.toList" == benchName =
28− "-K2M"
29− | "Data.Stream.StreamK/o-n-space.elimination.toList" == benchName =
30− "-K2M"
3130 | "Prelude.Parallel/o-n-heap.mapping.mapM" == benchName = "-M256M"
3231 | "Prelude.Parallel/o-n-heap.monad-outer-product."
3332 `isPrefixOf` benchName = "-M256M"
@@ -35,25 +34,6 @@ rtsOpts exeName benchName0 = unwords [general, exeSpecific, benchSpecific]
3534 `isPrefixOf` benchName = "-K2M -M256M"
3635 | "Prelude.Rate/o-1-space." `isPrefixOf` benchName = "-K128K"
3736 | "Prelude.Rate/o-1-space.asyncly." `isPrefixOf` benchName = "-K128K"
38− | "Data.Stream/o-1-space.mixed.sum-product-fold" == benchName =
39− "-K64M"
40− | "Data.Stream/o-n-heap.grouping.classifySessionsOf"
41− `isPrefixOf` benchName = "-K1M -M32M"
42− | "Data.Stream/o-n-heap.Functor." `isPrefixOf` benchName =
43− "-K4M -M32M"
44− | "Data.Stream/o-n-heap.transformer." `isPrefixOf` benchName =
45− "-K8M -M64M"
46− | "Data.Stream/o-n-space.Functor." `isPrefixOf` benchName =
47− "-K4M -M64M"
48− | "Data.Stream/o-n-space.Applicative." `isPrefixOf` benchName =
49− "-K8M -M128M"
50− | "Data.Stream/o-n-space.Monad." `isPrefixOf` benchName =
51− "-K8M -M64M"
52− | "Data.Stream/o-n-space.grouping." `isPrefixOf` benchName = ""
53− | "Data.Stream/o-n-space." `isPrefixOf` benchName = "-K4M"
54− | "Data.Stream.ConcurrentInterleaved/o-1-space.monad-outer-product.toNullAp" `isPrefixOf` benchName = "-M32M"
55− | "Data.Stream.ConcurrentEager/o-1-space.monad-outer-product.toNullAp" `isPrefixOf` benchName = "-M2048M -K4M"
56− | "Data.Stream.ConcurrentEager/o-1-space." `isPrefixOf` benchName = "-M512M -K4M"
5737 | "Prelude.WSerial/o-n-space." `isPrefixOf` benchName = "-K4M"
5838 | "Prelude.Async/o-n-space.monad-outer-product." `isPrefixOf` benchName =
5939 "-K4M"
@@ -66,27 +46,53 @@ rtsOpts exeName benchName0 = unwords [general, exeSpecific, benchSpecific]
6646 "-M64M"
6747 | "Prelude.WAsync/o-n-space.monad-outer-product." `isPrefixOf` benchName =
6848 "-K4M"
69− | "Data.Parser.ParserD/o-1-space.some" == benchName = "-K8M"
70− | "Data.Parser/o-1-space.some" == benchName = "-K8M"
71− | "Data.Parser.ParserD/o-1-space.manyTill" == benchName = "-K4M"
72− | "Data.Parser/o-1-space.manyTill" == benchName = "-K4M"
73− | "Data.Parser/o-n-heap.manyAlt" == benchName = "-K4M -M128M"
74− | "Data.Parser/o-n-heap.someAlt" == benchName = "-K4M -M128M"
75− | "Data.Parser/o-n-heap.choice" == benchName = "-K16M -M32M"
76− | "Data.Parser.ParserK/o-n-heap.manyAlt" == benchName = "-K4M -M128M"
77− | "Data.Parser.ParserK/o-n-heap.someAlt" == benchName = "-K4M -M128M"
78− | "Data.Parser.ParserK/o-n-heap.sequence" == benchName = "-M64M"
79− | "Data.Parser.ParserK/o-n-heap.sequenceA" == benchName = "-M64M"
49+
50+ -----------------------------------------------------------------------
51+
52+ | "Data.Stream.StreamD/o-n-space.elimination.toList" == benchName =
53+ "-K2M"
54+ | "Data.Stream.StreamK/o-n-space.elimination.toList" == benchName =
55+ "-K2M"
56+
57+ -----------------------------------------------------------------------
58+
59+ | "Data.Stream/o-1-space.grouping.classifySessionsOf"
60+ `isPrefixOf` benchName = "-K512K"
61+ | "Data.Stream/o-n-space.foldr.foldrM/"
62+ `isPrefixOf` benchName = "-K4M"
63+ | "Data.Stream/o-n-space.iterated."
64+ `isPrefixOf` benchName = "-K4M"
65+
66+ | "Data.Stream.StreamDK/o-1-space.grouping.classifySessionsOf"
67+ `isPrefixOf` benchName = "-K512K"
68+ | "Data.Stream.StreamDK/o-n-space.foldr.foldrM/"
69+ `isPrefixOf` benchName = "-K4M"
70+ | "Data.Stream.StreamDK/o-n-space.iterated."
71+ `isPrefixOf` benchName = "-K4M -M64M"
72+ | "Data.Stream.StreamDK/o-n-space.traversable."
73+ `isPrefixOf` benchName = "-K2M"
74+
75+ | "Data.Stream.ConcurrentInterleaved/o-1-space.monad-outer-product.toNullAp"
76+ `isPrefixOf` benchName = "-M32M"
77+
78+ | "Data.Stream.ConcurrentEager/o-1-space.monad-outer-product.toNullAp"
79+ `isPrefixOf` benchName = "-M512M"
80+ | "Data.Stream.ConcurrentEager/o-1-space."
81+ `isPrefixOf` benchName = "-M128M"
82+
83+ ----------------------------------------------------------------------
84+
8085 | "Data.Array" `isPrefixOf` benchName
8186 && "/o-1-space.generation.read" `isSuffixOf` benchName = "-M32M"
8287 | "Data.Array" `isPrefixOf` benchName
8388 && "/o-1-space.generation.show" `isSuffixOf` benchName = "-M32M"
84− | "Data.Array.Generic/o-1-space.transformationX4.map" == benchName = "-M32M"
85− | "Data.Array/o-1-space.elimination.foldable.foldl"
86− `isPrefixOf` benchName = "-K8M"
87− | "Data.Array/o-1-space.elimination.foldable.sum" == benchName =
88− "-K8M"
89+ | "Data.Array.Generic/o-1-space.transformationX4.map"
90+ `isPrefixOf` benchName = "-M32M"
91+
92+ -----------------------------------------------------------------------
93+
8994 | "Unicode.Char/o-1-space." `isPrefixOf` benchName = "-M32M"
95+
9096 | otherwise = ""
9197
9298speedOpts :: String -> String -> Maybe Quickness
benchmark/lib/Streamly/Benchmark/Common.hs+1−1
@@ -54,7 +54,7 @@ import Data.Functor.Identity (Identity, runIdentity)
5454import System.Random (randomRIO)
5555
5656import qualified Streamly.Internal.Data.Fold as Fold
57−import qualified Streamly.Internal.Data.Stream as S
57+import qualified Streamly.Internal.Data.Stream.StreamD as S
5858
5959import Gauge
6060
benchmark/streamly-benchmarks.cabal+186−113
@@ -73,15 +73,49 @@ flag use-prelude
7373-- Common stanzas
7474-------------------------------------------------------------------------------
7575
76+common default-extensions
77+ default-extensions:
78+ BangPatterns
79+ CApiFFI
80+ CPP
81+ ConstraintKinds
82+ DeriveDataTypeable
83+ DeriveGeneric
84+ DeriveTraversable
85+ ExistentialQuantification
86+ FlexibleContexts
87+ FlexibleInstances
88+ GeneralizedNewtypeDeriving
89+ InstanceSigs
90+ KindSignatures
91+ LambdaCase
92+ MagicHash
93+ MultiParamTypeClasses
94+ PatternSynonyms
95+ RankNTypes
96+ RecordWildCards
97+ ScopedTypeVariables
98+ TupleSections
99+ TypeApplications
100+ TypeFamilies
101+ TypeOperators
102+ ViewPatterns
103+
104+ -- MonoLocalBinds, enabled by TypeFamilies, causes performance
105+ -- regressions. Disable it. This must come after TypeFamilies,
106+ -- otherwise TypeFamilies will enable it again.
107+ NoMonoLocalBinds
108+
109+ -- UndecidableInstances -- Does not show any perf impact
110+ -- UnboxedTuples -- interferes with (#.)
111+
76112common compile-options
113+ import: default-extensions
77114 default-language: Haskell2010
78115
79116 if flag(use-streamly-core)
80117 cpp-options: -DUSE_STREAMLY_CORE
81118
82− if flag(use-prelude)
83− cpp-options: -DUSE_PRELUDE
84−
85119 if flag(dev)
86120 cpp-options: -DDEVBUILD
87121
@@ -191,7 +225,7 @@ library
191225 hs-source-dirs: lib
192226 exposed-modules: Streamly.Benchmark.Common
193227 , Streamly.Benchmark.Common.Handle
194− if !flag(use-streamly-core)
228+ if flag(use-prelude)
195229 exposed-modules: Streamly.Benchmark.Prelude
196230
197231
@@ -232,12 +266,37 @@ benchmark Data.Stream
232266 Stream.Transform
233267 Stream.Reduce
234268 Stream.Expand
269+ Stream.Lift
270+ Stream.Common
271+ if !flag(use-streamly-core)
272+ other-modules:
235273 Stream.Exceptions
274+ if flag(limit-build-mem)
275+ if flag(dev)
276+ ghc-options: +RTS -M3500M -RTS
277+ else
278+ ghc-options: +RTS -M2500M -RTS
279+
280+benchmark Data.Stream.StreamDK
281+ import: bench-options
282+ type: exitcode-stdio-1.0
283+ hs-source-dirs: Streamly/Benchmark/Data
284+ main-is: Stream.hs
285+ other-modules:
286+ Stream.Generate
287+ Stream.Eliminate
288+ Stream.Transform
289+ Stream.Reduce
290+ Stream.Expand
236291 Stream.Lift
237292 Stream.Common
238− if flag(use-prelude)
293+ if !flag(use-streamly-core)
239294 other-modules:
240− Stream.Split
295+ Stream.Exceptions
296+ -- Cannot use USE_STREAMK and USE_PRELUDE together
297+ if flag(use-prelude)
298+ buildable: False
299+ cpp-options: -DUSE_STREAMK
241300 if flag(limit-build-mem)
242301 if flag(dev)
243302 ghc-options: +RTS -M3500M -RTS
@@ -296,92 +355,6 @@ benchmark Data.Stream.ConcurrentOrdered
296355 else
297356 buildable: True
298357
299−benchmark Prelude.WSerial
300− import: bench-options
301− type: exitcode-stdio-1.0
302− hs-source-dirs: Streamly/Benchmark/Prelude
303− main-is: WSerial.hs
304− if flag(use-streamly-core) || impl(ghcjs)
305− buildable: False
306− else
307− buildable: True
308− if flag(limit-build-mem)
309− ghc-options: +RTS -M750M -RTS
310−
311−benchmark Prelude.Merge
312− import: bench-options
313− type: exitcode-stdio-1.0
314− hs-source-dirs: Streamly/Benchmark/Prelude
315− main-is: Merge.hs
316− if flag(use-streamly-core) || impl(ghcjs)
317− buildable: False
318− else
319− buildable: True
320−
321−benchmark Prelude.ZipSerial
322− import: bench-options
323− type: exitcode-stdio-1.0
324− hs-source-dirs: Streamly/Benchmark/Prelude
325− main-is: ZipSerial.hs
326− if flag(use-streamly-core) || impl(ghcjs)
327− buildable: False
328− else
329− buildable: True
330−
331−benchmark Prelude.ZipAsync
332− import: bench-options
333− type: exitcode-stdio-1.0
334− hs-source-dirs: Streamly/Benchmark/Prelude
335− main-is: ZipAsync.hs
336− if flag(use-streamly-core) || impl(ghcjs)
337− buildable: False
338− else
339− buildable: True
340− if flag(limit-build-mem)
341− ghc-options: +RTS -M1000M -RTS
342−
343−benchmark Prelude.Ahead
344− import: bench-options-threaded
345− type: exitcode-stdio-1.0
346− hs-source-dirs: Streamly/Benchmark/Prelude
347− main-is: Ahead.hs
348− if flag(use-streamly-core) || impl(ghcjs)
349− buildable: False
350− else
351− buildable: True
352−
353−benchmark Prelude.Async
354− import: bench-options-threaded
355− type: exitcode-stdio-1.0
356− hs-source-dirs: Streamly/Benchmark/Prelude
357− main-is: Async.hs
358− if flag(use-streamly-core) || impl(ghcjs)
359− buildable: False
360− else
361− buildable: True
362−
363−benchmark Prelude.WAsync
364− import: bench-options-threaded
365− type: exitcode-stdio-1.0
366− hs-source-dirs: Streamly/Benchmark/Prelude
367− main-is: WAsync.hs
368− if flag(use-streamly-core) || impl(ghcjs)
369− buildable: False
370− else
371− buildable: True
372−
373−benchmark Prelude.Parallel
374− import: bench-options-threaded
375− type: exitcode-stdio-1.0
376− hs-source-dirs: Streamly/Benchmark/Prelude
377− main-is: Parallel.hs
378− if flag(use-streamly-core) || impl(ghcjs)
379− buildable: False
380− else
381− buildable: True
382− if flag(limit-build-mem)
383− ghc-options: +RTS -M2000M -RTS
384−
385358
386359benchmark Data.Unfold
387360 import: bench-options
@@ -483,8 +456,6 @@ benchmark Data.Stream.StreamD
483456 main-is: StreamD.hs
484457 if impl(ghcjs)
485458 buildable: False
486− else
487− buildable: True
488459
489460benchmark Data.Stream.StreamK
490461 import: bench-options
@@ -493,20 +464,16 @@ benchmark Data.Stream.StreamK
493464 main-is: StreamK.hs
494465 if impl(ghcjs)
495466 buildable: False
496− else
497− buildable: True
498467
499468benchmark Data.Stream.ToStreamK
500469 import: bench-options
501470 type: exitcode-stdio-1.0
502471 hs-source-dirs: Streamly/Benchmark/Data/Stream
503472 main-is: ToStreamK.hs
504− if impl(ghcjs)
473+ if !flag(dev) || impl(ghcjs)
505474 buildable: False
506− else
507− buildable: True
508475
509−benchmark Data.Stream.StreamDK
476+benchmark Data.Stream.StreamK.Alt
510477 import: bench-options
511478 type: exitcode-stdio-1.0
512479 hs-source-dirs: Streamly/Benchmark/Data/Stream
@@ -518,11 +485,115 @@ executable nano-bench
518485 import: bench-options
519486 hs-source-dirs: .
520487 main-is: NanoBenchmarks.hs
521− if flag(dev)
522− buildable: True
523− else
488+ if !flag(dev)
524489 buildable: False
525490
491+-------------------------------------------------------------------------------
492+-- Streamly.Prelude
493+-------------------------------------------------------------------------------
494+
495+benchmark Prelude
496+ import: bench-options
497+ type: exitcode-stdio-1.0
498+ hs-source-dirs: Streamly/Benchmark/Data
499+ main-is: Stream.hs
500+ other-modules:
501+ Stream.Generate
502+ Stream.Eliminate
503+ Stream.Transform
504+ Stream.Reduce
505+ Stream.Expand
506+ Stream.Lift
507+ Stream.Common
508+ Stream.Split
509+ Stream.Exceptions
510+ cpp-options: -DUSE_PRELUDE
511+ if !flag(use-prelude)
512+ buildable: False
513+ if flag(limit-build-mem)
514+ if flag(dev)
515+ ghc-options: +RTS -M3500M -RTS
516+ else
517+ ghc-options: +RTS -M2500M -RTS
518+
519+benchmark Prelude.WSerial
520+ import: bench-options
521+ type: exitcode-stdio-1.0
522+ hs-source-dirs: Streamly/Benchmark/Prelude
523+ main-is: WSerial.hs
524+ cpp-options: -DUSE_PRELUDE
525+ if !flag(use-prelude)
526+ buildable: False
527+ if flag(limit-build-mem)
528+ ghc-options: +RTS -M750M -RTS
529+
530+benchmark Prelude.Merge
531+ import: bench-options
532+ type: exitcode-stdio-1.0
533+ hs-source-dirs: Streamly/Benchmark/Prelude
534+ main-is: Merge.hs
535+ cpp-options: -DUSE_PRELUDE
536+ if !flag(use-prelude)
537+ buildable: False
538+
539+benchmark Prelude.ZipSerial
540+ import: bench-options
541+ type: exitcode-stdio-1.0
542+ hs-source-dirs: Streamly/Benchmark/Prelude
543+ main-is: ZipSerial.hs
544+ cpp-options: -DUSE_PRELUDE
545+ if !flag(use-prelude)
546+ buildable: False
547+
548+benchmark Prelude.ZipAsync
549+ import: bench-options
550+ type: exitcode-stdio-1.0
551+ hs-source-dirs: Streamly/Benchmark/Prelude
552+ main-is: ZipAsync.hs
553+ cpp-options: -DUSE_PRELUDE
554+ if !flag(use-prelude)
555+ buildable: False
556+ if flag(limit-build-mem)
557+ ghc-options: +RTS -M1000M -RTS
558+
559+benchmark Prelude.Ahead
560+ import: bench-options-threaded
561+ type: exitcode-stdio-1.0
562+ hs-source-dirs: Streamly/Benchmark/Prelude
563+ main-is: Ahead.hs
564+ cpp-options: -DUSE_PRELUDE
565+ if !flag(use-prelude)
566+ buildable: False
567+
568+benchmark Prelude.Async
569+ import: bench-options-threaded
570+ type: exitcode-stdio-1.0
571+ hs-source-dirs: Streamly/Benchmark/Prelude
572+ main-is: Async.hs
573+ cpp-options: -DUSE_PRELUDE
574+ if !flag(use-prelude)
575+ buildable: False
576+
577+benchmark Prelude.WAsync
578+ import: bench-options-threaded
579+ type: exitcode-stdio-1.0
580+ hs-source-dirs: Streamly/Benchmark/Prelude
581+ main-is: WAsync.hs
582+ cpp-options: -DUSE_PRELUDE
583+ if !flag(use-prelude)
584+ buildable: False
585+
586+benchmark Prelude.Parallel
587+ import: bench-options-threaded
588+ type: exitcode-stdio-1.0
589+ hs-source-dirs: Streamly/Benchmark/Prelude
590+ main-is: Parallel.hs
591+ cpp-options: -DUSE_PRELUDE
592+ if !flag(use-prelude)
593+ buildable: False
594+ if flag(limit-build-mem)
595+ ghc-options: +RTS -M2000M -RTS
596+
526597-------------------------------------------------------------------------------
527598-- Concurrent Streams
528599-------------------------------------------------------------------------------
@@ -532,30 +603,27 @@ benchmark Prelude.Concurrent
532603 type: exitcode-stdio-1.0
533604 hs-source-dirs: Streamly/Benchmark/Prelude
534605 main-is: Concurrent.hs
535− if flag(use-streamly-core)
606+ cpp-options: -DUSE_PRELUDE
607+ if !flag(use-prelude)
536608 buildable: False
537− else
538− buildable: True
539609
540610benchmark Prelude.Adaptive
541611 import: bench-options-threaded
542612 type: exitcode-stdio-1.0
543613 hs-source-dirs: Streamly/Benchmark/Prelude
544614 main-is: Adaptive.hs
545− if flag(use-streamly-core) || impl(ghcjs)
615+ cpp-options: -DUSE_PRELUDE
616+ if !flag(use-prelude)
546617 buildable: False
547− else
548− buildable: True
549618
550619benchmark Prelude.Rate
551620 import: bench-options-threaded
552621 type: exitcode-stdio-1.0
553622 hs-source-dirs: Streamly/Benchmark/Prelude
554623 main-is: Rate.hs
555− if flag(use-streamly-core) || impl(ghcjs)
624+ cpp-options: -DUSE_PRELUDE
625+ if !flag(use-prelude)
556626 buildable: False
557− else
558− buildable: True
559627
560628-------------------------------------------------------------------------------
561629-- Array Benchmarks
@@ -564,8 +632,10 @@ benchmark Prelude.Rate
564632benchmark Data.Array.Generic
565633 import: bench-options
566634 type: exitcode-stdio-1.0
567− hs-source-dirs: .
635+ hs-source-dirs: ., Streamly/Benchmark/Data
568636 main-is: Streamly/Benchmark/Data/Array/Generic.hs
637+ other-modules:
638+ Stream.Common
569639 if flag(use-streamly-core)
570640 buildable: False
571641 else
@@ -585,7 +655,10 @@ benchmark Data.SmallArray
585655benchmark Data.Array
586656 import: bench-options
587657 type: exitcode-stdio-1.0
658+ hs-source-dirs: ., Streamly/Benchmark/Data
588659 main-is: Streamly/Benchmark/Data/Array.hs
660+ other-modules:
661+ Stream.Common
589662 if flag(use-streamly-core)
590663 buildable: False
591664 else
cabal.project.doctest+1−1
@@ -1,6 +1,6 @@
11packages: streamly.cabal
22 , core/streamly-core.cabal
3− , docs/streamly-docs.cabal
3+ -- , docs/streamly-docs.cabal
44 -- for QuickCheck in property doctests
55 , test/streamly-tests.cabal
66
core/src/Streamly/Data/Stream.hs+40−45
@@ -25,24 +25,6 @@
2525-- allows high performance combinatorial programming even when using byte level
2626-- streams. Streamly API is similar to Haskell lists.
2727--
28−-- Streams can be constructed like lists, except that they use 'nil' instead of
29−-- '[]' and 'cons' instead of ':'.
30−--
31−-- `cons` adds a pure value at the head of the stream:
32−--
33−-- >>> import Streamly.Data.Stream (Stream, cons, consM, nil)
34−-- >>> stream = 1 `cons` 2 `cons` nil :: Stream IO Int
35−-- >>> Stream.fold Fold.toList stream -- IO [Int]
36−-- [1,2]
37−--
38−-- `consM` adds an effect at the head of the stream:
39−--
40−-- >>> stream = effect 1 `consM` effect 2 `consM` nil
41−-- >>> Stream.fold Fold.toList stream
42−-- 1
43−-- 2
44−-- [1,2]
45−--
4628-- == Console Echo Example
4729--
4830-- In the following example, 'repeatM' generates an infinite stream of 'String'
@@ -69,25 +51,40 @@
6951-- "Data.List" like functions and many more powerful combinators to perform
7052-- common programming tasks.
7153--
54+-- == Performance Notes
55+--
56+-- The 'Stream' type represents a stream by composing state as data which
57+-- enables stream fusion. Stream fusion generates a tight loop without any
58+-- constructor allocations between the stages, providing C like performance for
59+-- the loop. Stream fusion works when multiple functions are combined in a
60+-- pipeline statically. Therefore, the operations in this module must be
61+-- inlined and must not be used recursively to allow for stream fusion.
62+--
63+-- Using the 'Stream' type binary stream construction operations like 'cons',
64+-- 'append' etc. degrade quadratically (O(n^2)) when combined many times. If
65+-- you need to combine these operations, say more than 50 times in a single
66+-- loop, then you should use the continuation style stream type 'StreamK'
67+-- instead. Also, if you need to use these operations in a recursive loop you
68+-- should use 'StreamK' instead.
69+--
70+-- The 'StreamK' type represents a stream by composing function calls,
71+-- therefore, a function call overhead is incurred at each composition. It is
72+-- quite fast in general but may be a few times slower than a fused stream.
73+-- However, it allows for scalable dynamic composition and control flow
74+-- manipulation. Using the 'StreamK' type binary operations like 'cons' and
75+-- 'append' provide linear (O(n)) performance.
76+--
77+-- 'Stream' and 'StreamK' types can be interconverted.
78+--
7279-- == Useful Idioms
7380--
7481-- >>> fromListM = Stream.sequence . Stream.fromList
75−-- >>> fromFoldableM = Stream.sequence . Stream.fromFoldable
7682-- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
7783--
7884-- Also see "Streamly.Internal.Data.Stream" module for many more @Pre-release@
7985-- combinators. See the <https://github.com/composewell/streamly-examples>
8086-- repository for many more real world examples of stream programming.
8187--
82−-- == Performance Notes
83−--
84−-- Operations annotated as /CPS/ force the use of continuation passing style
85−-- streams. Therefore, such operations are not subjected to stream fusion.
86−-- However, for some of these operations you can find fusible alternatives in
87−-- the internal modules, which are perfectly fine to use but you need to
88−-- understand the implications especially the O(n^2) nature of those
89−-- operations..
90−--
9188module Streamly.Data.Stream
9289 (
9390 Stream
@@ -107,8 +104,6 @@ module Streamly.Data.Stream
107104 , nilM
108105 , cons
109106 , consM
110− -- , cons2 -- fused version
111− -- , consM2 -- fused version
112107
113108 -- ** Unfolding
114109 -- | 'unfoldrM' is the most general way of generating a stream efficiently.
@@ -152,7 +147,6 @@ module Streamly.Data.Stream
152147 -- | Convert an input structure, container or source into a stream. All of
153148 -- these can be expressed in terms of primitives.
154149 , fromList
155− , fromFoldable
156150
157151 -- ** From Unfolds
158152 -- | Most of the above stream generation operations can also be expressed
@@ -366,28 +360,31 @@ module Streamly.Data.Stream
366360 , append
367361
368362 -- ** Interleaving
363+ -- | When interleaving more than two streams you may want to interleave
364+ -- them pairwise creating a balanced binary merge tree.
369365 , interleave
370− -- , interleave2
371366
372367 -- ** Merging
373− -- | Merging of @n@ streams can be performed by combining the streams pair
368+ -- | When merging more than two streams you may want to merging them
369+ -- pairwise creating a balanced binary merge tree.
370+ --
371+ -- Merging of @n@ streams can be performed by combining the streams pair
374372 -- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used
375373 -- with 'concatMapWith' it will have O(n^2) performance.
376374
377375 , mergeBy
378376 , mergeByM
379− -- , mergeBy2
380− -- , mergeByM2
381377
382378 -- ** Zipping
383− -- | Zipping of @n@ streams can be performed by combining the streams pair
379+ -- | When zipping more than two streams you may want to zip them
380+ -- pairwise creating a balanced binary tree.
381+ --
382+ -- Zipping of @n@ streams can be performed by combining the streams pair
384383 -- wise using 'mergeMapWith' with O(n * log n) time complexity. If used
385384 -- with 'concatMapWith' it will have O(n^2) performance.
386385 , zipWith
387386 , zipWithM
388− -- , zipWith2
389− -- , zipWithM2
390− , ZipStream (..)
387+ -- , ZipStream (..)
391388
392389 -- ** Cross Product
393390 -- XXX The argument order in this operation is such that it seems we are
@@ -399,7 +396,7 @@ module Streamly.Data.Stream
399396 , crossWith
400397 -- , cross
401398 -- , joinInner
402− , CrossStream (..)
399+ -- , CrossStream (..)
403400
404401 -- * Unfold Each
405402 , unfoldMany
@@ -428,21 +425,18 @@ module Streamly.Data.Stream
428425 --
429426
430427 , concatEffect
431− , concatMapWith
432428 , concatMap
433429 , concatMapM
434− , mergeMapWith
435430
436431 -- * Repeated Fold
437432 , foldMany -- XXX Rename to foldRepeat
438433 , parseMany
439− , arraysOf
434+ , Array.arraysOf
440435
441436 -- * Buffered Operations
442437 -- | Operations that require buffering of the stream.
443438 -- Reverse is essentially a left fold followed by an unfold.
444439 , reverse
445− , sortBy
446440
447441 -- * Multi-Stream folds
448442 -- | Operations that consume multiple streams at the same time.
@@ -496,7 +490,8 @@ module Streamly.Data.Stream
496490 )
497491where
498492
499−import Streamly.Internal.Data.Stream
493+import qualified Streamly.Internal.Data.Array.Type as Array
494+import Streamly.Internal.Data.Stream.StreamD
500495import Prelude
501496 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
502497 foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
core/src/Streamly/Data/Stream/StreamDK.hsnew file+505
@@ -0,0 +1,505 @@
1+-- |
2+-- Module : Streamly.Data.Stream.StreamDK
3+-- Copyright : (c) 2017 Composewell Technologies
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+-- Stability : released
8+-- Portability : GHC
9+--
10+-- To run examples in this module:
11+--
12+-- >>> import qualified Streamly.Data.Fold as Fold
13+-- >>> import qualified Streamly.Data.Stream.StreamDK as Stream
14+--
15+-- We will add some more imports in the examples as needed.
16+--
17+-- For effectful streams we will use the following IO action:
18+--
19+-- >>> effect n = print n >> return n
20+--
21+-- = Overview
22+--
23+-- Streamly is a framework for modular data flow based programming and
24+-- declarative concurrency. Powerful stream fusion framework in streamly
25+-- allows high performance combinatorial programming even when using byte level
26+-- streams. Streamly API is similar to Haskell lists.
27+--
28+-- Streams can be constructed like lists, except that they use 'nil' instead of
29+-- '[]' and 'cons' instead of ':'.
30+--
31+-- `cons` adds a pure value at the head of the stream:
32+--
33+-- >>> import Streamly.Data.Stream.StreamDK (Stream, cons, consM, nil)
34+-- >>> stream = 1 `cons` 2 `cons` nil :: Stream IO Int
35+-- >>> Stream.fold Fold.toList stream -- IO [Int]
36+-- [1,2]
37+--
38+-- `consM` adds an effect at the head of the stream:
39+--
40+-- >>> stream = effect 1 `consM` effect 2 `consM` nil
41+-- >>> Stream.fold Fold.toList stream
42+-- 1
43+-- 2
44+-- [1,2]
45+--
46+-- == Console Echo Example
47+--
48+-- In the following example, 'repeatM' generates an infinite stream of 'String'
49+-- by repeatedly performing the 'getLine' IO action. 'mapM' then applies
50+-- 'putStrLn' on each element in the stream converting it to stream of '()'.
51+-- Finally, 'drain' folds the stream to IO discarding the () values, thus
52+-- producing only effects.
53+--
54+-- >>> import Data.Function ((&))
55+--
56+-- >>> :{
57+-- echo =
58+-- Stream.repeatM getLine -- Stream IO String
59+-- & Stream.mapM putStrLn -- Stream IO ()
60+-- & Stream.fold Fold.drain -- IO ()
61+-- :}
62+--
63+-- This is a console echo program. It is an example of a declarative loop
64+-- written using streaming combinators. Compare it with an imperative @while@
65+-- loop.
66+--
67+-- Hopefully, this gives you an idea how we can program declaratively by
68+-- representing loops using streams. In this module, you can find all
69+-- "Data.List" like functions and many more powerful combinators to perform
70+-- common programming tasks.
71+--
72+-- == Useful Idioms
73+--
74+-- >>> fromListM = Stream.sequence . Stream.fromList
75+-- >>> fromFoldableM = Stream.sequence . Stream.fromFoldable
76+-- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
77+--
78+-- Also see "Streamly.Internal.Data.Stream.StreamDK" module for many more @Pre-release@
79+-- combinators. See the <https://github.com/composewell/streamly-examples>
80+-- repository for many more real world examples of stream programming.
81+--
82+-- == Performance Notes
83+--
84+-- Operations annotated as /CPS/ force the use of continuation passing style
85+-- streams. Therefore, such operations are not subjected to stream fusion.
86+-- However, for some of these operations you can find fusible alternatives in
87+-- the internal modules, which are perfectly fine to use but you need to
88+-- understand the implications especially the O(n^2) nature of those
89+-- operations..
90+--
91+module Streamly.Data.Stream.StreamDK
92+ (
93+ Stream
94+ -- * Construction
95+ -- | Functions ending in the general shape @b -> Stream m a@.
96+ --
97+ -- See also: "Streamly.Internal.Data.Stream.Generate" for
98+ -- @Pre-release@ functions.
99+
100+ -- ** Primitives
101+ -- | Primitives to construct a stream from pure values or monadic actions.
102+ -- All other stream construction and generation combinators described later
103+ -- can be expressed in terms of these primitives. However, the special
104+ -- versions provided in this module can be much more efficient in most
105+ -- cases. Users can create custom combinators using these primitives.
106+ , nil
107+ , nilM
108+ , cons
109+ , consM
110+ -- , cons2 -- fused version
111+ -- , consM2 -- fused version
112+
113+ -- ** Unfolding
114+ -- | 'unfoldrM' is the most general way of generating a stream efficiently.
115+ -- All other generation operations can be expressed using it.
116+ , unfoldr
117+ , unfoldrM
118+
119+ -- ** From Values
120+ -- | Generate a monadic stream from a seed value or values.
121+ , fromPure
122+ , fromEffect
123+ , repeat
124+ , repeatM
125+ , replicate
126+ , replicateM
127+
128+ -- Note: Using enumeration functions e.g. 'Prelude.enumFromThen' turns out
129+ -- to be slightly faster than the idioms like @[from, then..]@.
130+ --
131+ -- ** Enumeration
132+ -- | We can use the 'Enum' type class to enumerate a type producing a list
133+ -- and then convert it to a stream:
134+ --
135+ -- @
136+ -- 'fromList' $ 'Prelude.enumFromThen' from then
137+ -- @
138+ --
139+ -- However, this is not particularly efficient.
140+ -- The 'Enumerable' type class provides corresponding functions that
141+ -- generate a stream instead of a list, efficiently.
142+
143+ , Enumerable (..)
144+ , enumerate
145+ , enumerateTo
146+
147+ -- ** Iteration
148+ , iterate
149+ , iterateM
150+
151+ -- ** From Containers
152+ -- | Convert an input structure, container or source into a stream. All of
153+ -- these can be expressed in terms of primitives.
154+ , fromList
155+ , fromFoldable
156+
157+ -- ** From Unfolds
158+ -- | Most of the above stream generation operations can also be expressed
159+ -- using the corresponding unfolds in the "Streamly.Data.Unfold" module.
160+ , unfold -- XXX rename to fromUnfold?
161+
162+ -- * Elimination
163+ -- | Functions ending in the general shape @Stream m a -> m b@ or @Stream m
164+ -- a -> m (b, Stream m a)@
165+ --
166+ -- See also: "Streamly.Internal.Data.Stream.Eliminate" for @Pre-release@
167+ -- functions.
168+
169+-- EXPLANATION: In imperative terms a fold can be considered as a loop over the stream
170+-- that reduces the stream to a single value.
171+-- Left and right folds both use a fold function @f@ and an identity element
172+-- @z@ (@zero@) to deconstruct a recursive data structure and reconstruct a
173+-- new data structure. The new structure may be a recursive construction (a
174+-- container) or a non-recursive single value reduction of the original
175+-- structure.
176+--
177+-- Both right and left folds are mathematical duals of each other, they are
178+-- functionally equivalent. Operationally, a left fold on a left associated
179+-- structure behaves exactly in the same way as a right fold on a right
180+-- associated structure. Similarly, a left fold on a right associated structure
181+-- behaves in the same way as a right fold on a left associated structure.
182+-- However, the behavior of a right fold on a right associated structure is
183+-- operationally different (even though functionally equivalent) than a left
184+-- fold on the same structure.
185+--
186+-- On right associated structures like Haskell @cons@ lists or Streamly
187+-- streams, a lazy right fold is naturally suitable for lazy recursive
188+-- reconstruction of a new structure, while a strict left fold is naturally
189+-- suitable for efficient reduction. In right folds control is in the hand of
190+-- the @puller@ whereas in left folds the control is in the hand of the
191+-- @pusher@.
192+--
193+-- The behavior of right and left folds are described in detail in the
194+-- individual fold's documentation. To illustrate the two folds for right
195+-- associated @cons@ lists:
196+--
197+-- > foldr :: (a -> b -> b) -> b -> [a] -> b
198+-- > foldr f z [] = z
199+-- > foldr f z (x:xs) = x `f` foldr f z xs
200+-- >
201+-- > foldl :: (b -> a -> b) -> b -> [a] -> b
202+-- > foldl f z [] = z
203+-- > foldl f z (x:xs) = foldl f (z `f` x) xs
204+--
205+-- @foldr@ is conceptually equivalent to:
206+--
207+-- > foldr f z [] = z
208+-- > foldr f z [x] = f x z
209+-- > foldr f z xs = foldr f (foldr f z (tail xs)) [head xs]
210+--
211+-- @foldl@ is conceptually equivalent to:
212+--
213+-- > foldl f z [] = z
214+-- > foldl f z [x] = f z x
215+-- > foldl f z xs = foldl f (foldl f z (init xs)) [last xs]
216+--
217+-- Left and right folds are duals of each other.
218+--
219+-- @
220+-- foldr f z xs = foldl (flip f) z (reverse xs)
221+-- foldl f z xs = foldr (flip f) z (reverse xs)
222+-- @
223+--
224+-- More generally:
225+--
226+-- @
227+-- foldr f z xs = foldl g id xs z where g k x = k . f x
228+-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
229+-- @
230+--
231+
232+-- NOTE: Folds are inherently serial as each step needs to use the result of
233+-- the previous step. However, it is possible to fold parts of the stream in
234+-- parallel and then combine the results using a monoid.
235+
236+ -- ** Primitives
237+ -- Consuming a part of the stream and returning the rest. Functions
238+ -- ending in the general shape @Stream m a -> m (b, Stream m a)@
239+ , uncons
240+
241+ -- ** Folding
242+ -- XXX Need to have a general parse operation here which can be used to
243+ -- express all others.
244+ , fold -- XXX rename to run? We can have a Stream.run and Fold.run.
245+ -- XXX fold1 can be achieved using Monoids or Refolds.
246+ -- XXX We can call this just "break" and parseBreak as "munch"
247+ , foldBreak
248+
249+ -- XXX should we have a Fold returning function in stream module?
250+ -- , foldAdd
251+ -- , buildl
252+
253+ -- ** Parsing
254+ , parse
255+ , parseBreak
256+
257+ -- -- ** Lazy Right Folds
258+ -- Consuming a stream to build a right associated expression, suitable
259+ -- for lazy evaluation. Evaluation of the input happens when the output of
260+ -- the fold is evaluated, the fold output is a lazy thunk.
261+ --
262+ -- This is suitable for stream transformation operations, for example,
263+ -- operations like mapping a function over the stream.
264+ -- , foldrM
265+ -- , foldr
266+
267+ -- * Mapping
268+ -- | Stateless one-to-one transformations. Use 'fmap' for mapping a pure
269+ -- function on a stream.
270+
271+ -- EXPLANATION:
272+ -- In imperative terms a map operation can be considered as a loop over
273+ -- the stream that transforms the stream into another stream by performing
274+ -- an operation on each element of the stream.
275+ --
276+ -- 'map' is the least powerful transformation operation with strictest
277+ -- guarantees. A map, (1) is a stateless loop which means that no state is
278+ -- allowed to be carried from one iteration to another, therefore,
279+ -- operations on different elements are guaranteed to not affect each
280+ -- other, (2) is a strictly one-to-one transformation of stream elements
281+ -- which means it guarantees that no elements can be added or removed from
282+ -- the stream, it can merely transform them.
283+ , sequence
284+ , mapM
285+ -- , trace -- XXX Use "tracing" map instead?
286+ , tap
287+ , delay
288+
289+ -- * Scanning
290+ -- | Stateful one-to-one transformations.
291+ --
292+ -- See also: "Streamly.Internal.Data.Stream.Transform" for
293+ -- @Pre-release@ functions.
294+ , scan
295+ , postscan
296+ -- XXX postscan1 can be implemented using Monoids or Refolds.
297+ -- Indexing can be considered as a special type of zipping where we zip a
298+ -- stream with an index stream.
299+ , indexed
300+
301+ -- * Insertion
302+ -- | Add elements to the stream.
303+
304+ -- Inserting elements is a special case of interleaving/merging streams.
305+ , insertBy
306+ , intersperseM
307+ , intersperseM_
308+ , intersperse
309+
310+ -- * Filtering
311+ -- | Remove elements from the stream.
312+
313+ -- ** Stateless Filters
314+ -- | 'mapMaybeM' is the most general stateless filtering operation. All
315+ -- other filtering operations can be expressed using it.
316+
317+ -- EXPLANATION:
318+ -- In imperative terms a filter over a stream corresponds to a loop with a
319+ -- @continue@ clause for the cases when the predicate fails.
320+
321+ , mapMaybe
322+ , mapMaybeM
323+ , filter
324+ , filterM
325+
326+ -- Filter and concat
327+ , catMaybes
328+ , catLefts
329+ , catRights
330+ , catEithers
331+
332+ -- ** Stateful Filters
333+ -- | 'scanMaybe' is the most general stateful filtering operation. The
334+ -- filtering folds (folds returning a 'Maybe' type) in
335+ -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
336+ -- perform stateful filtering operations in general.
337+ , scanMaybe
338+ , take
339+ , takeWhile
340+ , takeWhileM
341+ , drop
342+ , dropWhile
343+ , dropWhileM
344+
345+ -- XXX These are available as scans in folds. We need to check the
346+ -- performance though. If these are common and we need convenient stream
347+ -- ops then we can expose these.
348+
349+ -- , deleteBy
350+ -- , uniq
351+ -- , uniqBy
352+
353+ -- -- ** Sampling
354+ -- , strideFromThen
355+
356+ -- -- ** Searching
357+ -- Finding the presence or location of an element, a sequence of elements
358+ -- or another stream within a stream.
359+
360+ -- -- ** Searching Elements
361+ -- , findIndices
362+ -- , elemIndices
363+
364+ -- * Combining Two Streams
365+ -- ** Appending
366+ , append
367+
368+ -- ** Interleaving
369+ , interleave
370+ -- , interleave2
371+
372+ -- ** Merging
373+ -- | Merging of @n@ streams can be performed by combining the streams pair
374+ -- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used
375+ -- with 'concatMapWith' it will have O(n^2) performance.
376+
377+ , mergeBy
378+ , mergeByM
379+ -- , mergeBy2
380+ -- , mergeByM2
381+
382+ -- ** Zipping
383+ -- | Zipping of @n@ streams can be performed by combining the streams pair
384+ -- wise using 'mergeMapWith' with O(n * log n) time complexity. If used
385+ -- with 'concatMapWith' it will have O(n^2) performance.
386+ , zipWith
387+ , zipWithM
388+ -- , zipWith2
389+ -- , zipWithM2
390+ , ZipStream (..)
391+
392+ -- ** Cross Product
393+ -- XXX The argument order in this operation is such that it seems we are
394+ -- transforming the first stream using the second stream because the second
395+ -- stream is evaluated many times or buffered and better be finite, first
396+ -- stream could potentially be infinite. In the tradition of using the
397+ -- transformed stream at the end we can have a flipped version called
398+ -- "crossMap" or "nestWith".
399+ , crossWith
400+ -- , cross
401+ -- , joinInner
402+ , CrossStream (..)
403+
404+ -- * Unfold Each
405+ , unfoldMany
406+ , intercalate
407+ , intercalateSuffix
408+
409+ -- * Stream of streams
410+ -- | Stream operations like map and filter represent loop processing in
411+ -- imperative programming terms. Similarly, the imperative concept of
412+ -- nested loops are represented by streams of streams. The 'concatMap'
413+ -- operation represents nested looping.
414+ -- A 'concatMap' operation loops over the input stream and then for each
415+ -- element of the input stream generates another stream and then loops over
416+ -- that inner stream as well producing effects and generating a single
417+ -- output stream.
418+ --
419+ -- One dimension loops are just a special case of nested loops. For
420+ -- example, 'concatMap' can degenerate to a simple map operation:
421+ --
422+ -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
423+ --
424+ -- Similarly, 'concatMap' can perform filtering by mapping an element to a
425+ -- 'nil' stream:
426+ --
427+ -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
428+ --
429+
430+ , concatEffect
431+ , concatMapWith
432+ , concatMap
433+ , concatMapM
434+ , mergeMapWith
435+
436+ -- * Repeated Fold
437+ , foldMany -- XXX Rename to foldRepeat
438+ , parseMany
439+ , arraysOf
440+
441+ -- * Buffered Operations
442+ -- | Operations that require buffering of the stream.
443+ -- Reverse is essentially a left fold followed by an unfold.
444+ , reverse
445+ , sortBy
446+
447+ -- * Multi-Stream folds
448+ -- | Operations that consume multiple streams at the same time.
449+ , eqBy
450+ , cmpBy
451+ , isPrefixOf
452+ , isSubsequenceOf
453+
454+ -- trimming sequences
455+ , stripPrefix
456+
457+ -- Exceptions and resource management depend on the "exceptions" package
458+ -- XXX We can have IO Stream operations not depending on "exceptions"
459+ -- in Exception.Base
460+
461+ -- * Exceptions
462+ -- | Most of these combinators inhibit stream fusion, therefore, when
463+ -- possible, they should be called in an outer loop to mitigate the cost.
464+ -- For example, instead of calling them on a stream of chars call them on a
465+ -- stream of arrays before flattening it to a stream of chars.
466+ --
467+ -- See also: "Streamly.Internal.Data.Stream.Exception" for
468+ -- @Pre-release@ functions.
469+
470+ , onException
471+ , handle
472+
473+ -- * Resource Management
474+ -- | 'bracket' is the most general resource management operation, all other
475+ -- operations can be expressed using it. These functions have IO suffix
476+ -- because the allocation and cleanup functions are IO actions. For
477+ -- generalized allocation and cleanup functions see the functions without
478+ -- the IO suffix in the "streamly" package.
479+ , before
480+ , afterIO
481+ , finallyIO
482+ , bracketIO
483+ , bracketIO3
484+
485+ -- * Transforming Inner Monad
486+
487+ , morphInner
488+ , liftInner
489+ , runReaderT
490+ , runStateT
491+
492+ -- -- * Stream Types
493+ -- $serial
494+ -- , Interleave
495+ -- , Zip
496+ )
497+where
498+
499+import Streamly.Internal.Data.Stream.StreamDK
500+import Prelude
501+ hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
502+ foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
503+ notElem, maximum, minimum, head, last, tail, length, null,
504+ reverse, iterate, init, and, or, lookup, foldr1, (!!),
505+ scanl, scanl1, repeat, replicate, concatMap, span)
core/src/Streamly/Data/Stream/StreamK.hsnew file+93
@@ -0,0 +1,93 @@
1+-- |
2+-- Module : Streamly.Data.Stream.StreamK
3+-- Copyright : (c) 2017 Composewell Technologies
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+-- Stability : released
8+-- Portability : GHC
9+--
10+-- Streams using Continuation Passing Style (CPS). See notes in "Data.Stream"
11+-- module to know when to use this module.
12+
13+-- primitive/open loop operations that can be used recursively e.g. uncons,
14+-- foldBreak, parseBreak should not be converted from StreamD for use in
15+-- StreamK, instead native StreamK impl should be used.
16+--
17+-- Closed loop operations like repeat, replicate, iterate etc can be converted
18+-- from StreamD.
19+--
20+-- In the last phase any operation like (toStreamK . f . toStreamD) should be
21+-- rewritten to a K version of f.
22+-- XXX Need to add rewrite rules for all missing StreamD operations.
23+--
24+module Streamly.Data.Stream.StreamK
25+ (
26+ StreamK
27+
28+ -- * Construction
29+ -- ** Primitives
30+ , nil
31+ , nilM
32+ , cons
33+ , consM
34+
35+ -- ** From Values
36+ , fromPure
37+ , fromEffect
38+
39+ -- ** From Stream
40+ , fromStream
41+ , toStream
42+
43+ -- ** From Containers
44+ , fromFoldable
45+
46+ -- * Elimination
47+
48+ -- ** Primitives
49+ , uncons
50+
51+ -- -- ** Folding
52+ -- , foldBreak
53+
54+ -- ** Parsing
55+ , parseBreak
56+
57+ -- * Combining Two Streams
58+ -- ** Appending
59+ , append
60+
61+ -- ** Interleaving
62+ , interleave
63+
64+ -- ** Merging
65+ , mergeBy
66+ , mergeByM
67+
68+ -- ** Zipping
69+ , zipWith
70+ , zipWithM
71+
72+ -- ** Cross Product
73+ -- XXX is "bind/concatFor" better to have than crossWith?
74+ -- crossWith f xs1 xs2 = concatFor xs1 (\x -> fmap (f x) xs2)
75+ , crossWith
76+ -- , cross
77+ -- , joinInner
78+ -- , CrossStreamK (..)
79+
80+ -- * Stream of streams
81+ , concatEffect
82+ -- , concatMap
83+ , concatMapWith
84+ , mergeMapWith
85+
86+ -- * Buffered Operations
87+ , reverse
88+ , sortBy
89+ )
90+where
91+
92+import Streamly.Internal.Data.Stream.StreamK
93+import Prelude hiding (reverse, zipWith)
core/src/Streamly/Internal/Console/Stdio.hs+3−2
@@ -47,12 +47,13 @@ import System.IO (stdin, stdout, stderr)
4747import Prelude hiding (read)
4848
4949import Streamly.Internal.Data.Array.Type (Array(..))
50−import Streamly.Internal.Data.Stream (Stream)
50+import Streamly.Internal.Data.Stream.StreamD (Stream)
5151import Streamly.Internal.Data.Unfold (Unfold)
5252import Streamly.Internal.Data.Fold (Fold)
5353
5454import qualified Streamly.Internal.Data.Array as Array
55−import qualified Streamly.Internal.Data.Stream as Stream (intersperseMSuffix)
55+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
56+ (intersperseMSuffix)
5657import qualified Streamly.Internal.Data.Unfold as Unfold
5758import qualified Streamly.Internal.FileSystem.Handle as Handle
5859import qualified Streamly.Internal.Unicode.Stream as Unicode
core/src/Streamly/Internal/Data/Array.hs+8−10
@@ -139,7 +139,7 @@ import Streamly.Internal.Data.Array.Type
139139 (Array(..), length, asPtrUnsafe)
140140import Streamly.Internal.Data.Fold.Type (Fold(..))
141141import Streamly.Internal.Data.Producer.Type (Producer(..))
142−import Streamly.Internal.Data.Stream (Stream)
142+import Streamly.Internal.Data.Stream.StreamD (Stream)
143143import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..))
144144import Streamly.Internal.Data.Unfold.Type (Unfold(..))
145145import Streamly.Internal.System.IO (unsafeInlineIO)
@@ -151,9 +151,8 @@ import qualified Streamly.Internal.Data.Fold as FL
151151import qualified Streamly.Internal.Data.Producer.Type as Producer
152152import qualified Streamly.Internal.Data.Producer as Producer
153153import qualified Streamly.Internal.Data.Ring.Unboxed as RB
154−import qualified Streamly.Internal.Data.Stream.Common as P
155154import qualified Streamly.Internal.Data.Stream.StreamD as D
156−import qualified Streamly.Internal.Data.Stream.Type as Stream
155+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
157156import qualified Streamly.Internal.Data.Unfold as Unfold
158157
159158-------------------------------------------------------------------------------
@@ -169,7 +168,7 @@ import qualified Streamly.Internal.Data.Unfold as Unfold
169168fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
170169fromStreamN n m = do
171170 when (n < 0) $ error "writeN: negative write count specified"
172− A.fromStreamDN n $ Stream.toStreamD m
171+ A.fromStreamDN n m
173172
174173-- | Create an 'Array' from a stream. This is useful when we want to create a
175174-- single array from a stream of unknown size. 'writeN' is at least twice
@@ -182,7 +181,7 @@ fromStreamN n m = do
182181-- /Pre-release/
183182{-# INLINE fromStream #-}
184183fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)
185−fromStream m = P.fold A.write $ Stream.toStreamK m
184+fromStream = Stream.fold A.write
186185-- write m = A.fromStreamD $ D.fromStreamK m
187186
188187-------------------------------------------------------------------------------
@@ -373,8 +372,7 @@ getSliceUnsafe index len (Array contents start e) =
373372splitOn :: (Monad m, Unbox a) =>
374373 (a -> Bool) -> Array a -> Stream m (Array a)
375374splitOn predicate arr =
376− Stream.fromStreamD
377− $ fmap (\(i, len) -> getSliceUnsafe i len arr)
375+ fmap (\(i, len) -> getSliceUnsafe i len arr)
378376 $ D.sliceOnSuffix predicate (A.toStreamD arr)
379377
380378{-# INLINE genSlicesFromLen #-}
@@ -438,7 +436,7 @@ getIndex i arr =
438436{-# INLINE getIndices #-}
439437getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
440438getIndices m =
441− let unf = MA.getIndicesD (return . unsafeInlineIO) $ D.fromStreamK $ Stream.toStreamK m
439+ let unf = MA.getIndicesD (return . unsafeInlineIO) m
442440 in Unfold.lmap A.unsafeThaw unf
443441
444442-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
@@ -488,7 +486,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
488486streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
489487 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
490488streamTransform f arr =
491− P.fold (A.writeWith (length arr)) $ Stream.toStreamK $ f (A.read arr)
489+ Stream.fold (A.writeWith (length arr)) $ f (A.read arr)
492490
493491-------------------------------------------------------------------------------
494492-- Casts
@@ -553,7 +551,7 @@ asCStringUnsafe arr act = do
553551-- /Pre-release/
554552{-# INLINE fold #-}
555553fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
556−fold f arr = P.fold f (Stream.toStreamK (A.read arr))
554+fold f arr = Stream.fold f (A.read arr)
557555
558556-- | Fold an array using a stream fold operation.
559557--
core/src/Streamly/Internal/Data/Array/Generic.hs+7−7
@@ -55,7 +55,7 @@ import GHC.IO (unsafePerformIO)
5555import Text.Read (readPrec)
5656
5757import Streamly.Internal.Data.Fold.Type (Fold(..))
58−import Streamly.Internal.Data.Stream.Type (Stream)
58+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
5959import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
6060import Streamly.Internal.Data.Unfold.Type (Unfold(..))
6161import Streamly.Internal.System.IO (unsafeInlineIO)
@@ -65,8 +65,8 @@ import qualified Streamly.Internal.Data.Fold.Type as FL
6565import qualified Streamly.Internal.Data.Producer.Type as Producer
6666import qualified Streamly.Internal.Data.Producer as Producer
6767import qualified Streamly.Internal.Data.Ring as RB
68−import qualified Streamly.Internal.Data.Stream.StreamD as D
69−import qualified Streamly.Internal.Data.Stream.Type as Stream
68+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
69+import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
7070import qualified Text.ParserCombinators.ReadPrec as ReadPrec
7171
7272import Data.IORef
@@ -150,11 +150,11 @@ fromStreamD = D.fold write
150150fromStreamN :: MonadIO m => Int -> Stream m a -> m (Array a)
151151fromStreamN n m = do
152152 when (n < 0) $ error "fromStreamN: negative write count specified"
153− fromStreamDN n $ Stream.toStreamD m
153+ fromStreamDN n m
154154
155155{-# INLINE fromStream #-}
156156fromStream :: MonadIO m => Stream m a -> m (Array a)
157−fromStream = fromStreamD . Stream.toStreamD
157+fromStream = fromStreamD
158158
159159{-# INLINABLE fromListN #-}
160160fromListN :: Int -> [a] -> Array a
@@ -205,11 +205,11 @@ readRevStreamD arr@Array{..} =
205205
206206{-# INLINE_EARLY read #-}
207207read :: MonadIO m => Array a -> Stream m a
208−read = Stream.fromStreamD . readStreamD
208+read = readStreamD
209209
210210{-# INLINE_EARLY readRev #-}
211211readRev :: Monad m => Array a -> Stream m a
212−readRev = Stream.fromStreamD . readRevStreamD
212+readRev = readRevStreamD
213213
214214-------------------------------------------------------------------------------
215215-- Elimination - using Folds
core/src/Streamly/Internal/Data/Array/Generic/Mut/Type.hs+3−2
@@ -176,8 +176,9 @@ import Streamly.Internal.Data.Unfold.Type (Unfold(..))
176176
177177import qualified Streamly.Internal.Data.Fold.Type as FL
178178import qualified Streamly.Internal.Data.Producer as Producer
179−import qualified Streamly.Internal.Data.Stream.StreamD as D
180−import qualified Streamly.Internal.Data.Stream.StreamK as K
179+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
180+import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
181+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
181182
182183import Prelude hiding (read)
183184
core/src/Streamly/Internal/Data/Array/Mut.hs+3−6
@@ -34,13 +34,11 @@ where
3434
3535import Control.Monad.IO.Class (MonadIO(..))
3636import Streamly.Internal.Data.Unboxed (Unbox)
37−import Streamly.Internal.Data.Stream (Stream)
37+import Streamly.Internal.Data.Stream.StreamD (Stream)
3838import Streamly.Internal.Data.Unfold.Type (Unfold(..))
3939
4040import qualified Streamly.Internal.Data.Stream.StreamD as D
41−import qualified Streamly.Internal.Data.Stream.Type as Stream
4241import qualified Streamly.Internal.Data.Unfold as Unfold
43−-- import qualified Streamly.Internal.Data.Stream.Common as P
4442
4543import Prelude hiding (foldr, length, read, splitAt)
4644import Streamly.Internal.Data.Array.Mut.Type
@@ -53,8 +51,7 @@ import Streamly.Internal.Data.Array.Mut.Type
5351splitOn :: (MonadIO m, Unbox a) =>
5452 (a -> Bool) -> Array a -> Stream m (Array a)
5553splitOn predicate arr =
56− Stream.fromStreamD
57− $ fmap (\(i, len) -> getSliceUnsafe i len arr)
54+ fmap (\(i, len) -> getSliceUnsafe i len arr)
5855 $ D.sliceOnSuffix predicate (toStreamD arr)
5956
6057-- | Generate a stream of array slice descriptors ((index, len)) of specified
@@ -99,5 +96,5 @@ getSlicesFromLen from len =
9996-- /Pre-release/
10097{-# INLINE fromStream #-}
10198fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)
102−fromStream = fromStreamD . Stream.toStreamD
99+fromStream = fromStreamD
103100-- fromStream (Stream m) = P.fold write m
core/src/Streamly/Internal/Data/Array/Mut/Stream.hs+6−9
@@ -35,14 +35,13 @@ import Streamly.Internal.Data.Unboxed (Unbox, sizeOf)
3535import Streamly.Internal.Data.Array.Mut.Type (Array(..))
3636import Streamly.Internal.Data.Fold.Type (Fold(..))
3737import Streamly.Internal.Data.Parser (ParseError)
38−import Streamly.Internal.Data.Stream.Type (Stream)
38+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
3939import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
4040
4141import qualified Streamly.Internal.Data.Array.Mut.Type as MArray
4242import qualified Streamly.Internal.Data.Fold.Type as FL
4343import qualified Streamly.Internal.Data.Stream.StreamD as D
4444import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
45−import qualified Streamly.Internal.Data.Stream.Type as Stream
4645
4746-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
4847-- @n@ elements each.
@@ -55,7 +54,7 @@ import qualified Streamly.Internal.Data.Stream.Type as Stream
5554{-# INLINE arraysOf #-}
5655arraysOf :: (MonadIO m, Unbox a)
5756 => Int -> Stream m a -> Stream m (Array a)
58−arraysOf n = Stream.fromStreamD . MArray.arraysOf n . Stream.toStreamD
57+arraysOf = MArray.arraysOf
5958
6059-------------------------------------------------------------------------------
6160-- Compact
@@ -194,7 +193,7 @@ lpackArraysChunksOf n (Fold step1 initial1 extract1) =
194193{-# INLINE compact #-}
195194compact :: (MonadIO m, Unbox a)
196195 => Int -> Stream m (Array a) -> Stream m (Array a)
197−compact n = Stream.fromStreamD . packArraysChunksOf n . Stream.toStreamD
196+compact = packArraysChunksOf
198197
199198-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
200199-- maximum specified size. Note that if a single array is bigger than the
@@ -299,10 +298,9 @@ compactGEFold n = Fold step initial extract
299298-- maximum specified size in bytes.
300299--
301300-- /Internal/
302−compactLE :: (Monad m, MonadIO m, Unbox a) =>
301+compactLE :: (MonadIO m, Unbox a) =>
303302 Int -> Stream m (Array a) -> Stream m (Either ParseError (Array a))
304−compactLE n =
305− Stream.fromStreamD . D.parseMany (compactLEParserD n) . Stream.toStreamD
303+compactLE n = D.parseManyD (compactLEParserD n)
306304
307305-- | Like 'compactLE' but generates arrays of exactly equal to the size
308306-- specified except for the last array in the stream which could be shorter.
@@ -322,5 +320,4 @@ compactEQ _n _xs = undefined
322320compactGE ::
323321 (MonadIO m, Unbox a)
324322 => Int -> Stream m (Array a) -> Stream m (Array a)
325−compactGE n =
326− Stream.fromStreamD . D.foldMany (compactGEFold n) . Stream.toStreamD
323+compactGE n = D.foldMany (compactGEFold n)
core/src/Streamly/Internal/Data/Array/Mut/Type.hs+2−3
@@ -243,7 +243,7 @@ import GHC.Ptr (Ptr(..))
243243
244244import Streamly.Internal.Data.Fold.Type (Fold(..))
245245import Streamly.Internal.Data.Producer.Type (Producer (..))
246−import Streamly.Internal.Data.Stream.Type (Stream)
246+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
247247import Streamly.Internal.Data.SVar.Type (adaptState, defState)
248248import Streamly.Internal.Data.Unfold.Type (Unfold(..))
249249import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
@@ -252,7 +252,6 @@ import qualified Streamly.Internal.Data.Fold.Type as FL
252252import qualified Streamly.Internal.Data.Producer as Producer
253253import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
254254import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
255−import qualified Streamly.Internal.Data.Stream.Type as Stream
256255import qualified Streamly.Internal.Data.Unboxed as Unboxed
257256import qualified Prelude
258257
@@ -1036,7 +1035,7 @@ getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
10361035
10371036{-# INLINE getIndices #-}
10381037getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (Array a) a
1039−getIndices = getIndicesD liftIO . Stream.toStreamD
1038+getIndices = getIndicesD liftIO
10401039
10411040-------------------------------------------------------------------------------
10421041-- Subarrays
core/src/Streamly/Internal/Data/Array/Type.hs+12−6
@@ -90,7 +90,7 @@ import GHC.IO (unsafePerformIO)
9090import GHC.Ptr (Ptr(..))
9191import Streamly.Internal.Data.Array.Mut.Type (MutableByteArray)
9292import Streamly.Internal.Data.Fold.Type (Fold(..))
93−import Streamly.Internal.Data.Stream.Type (Stream)
93+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
9494import Streamly.Internal.Data.Unboxed (Unbox, peekWith, sizeOf)
9595import Streamly.Internal.Data.Unfold.Type (Unfold(..))
9696import Text.Read (readPrec)
@@ -101,7 +101,6 @@ import qualified GHC.Exts as Exts
101101import qualified Streamly.Internal.Data.Array.Mut.Type as MA
102102import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
103103import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
104−import qualified Streamly.Internal.Data.Stream.Type as Stream
105104import qualified Streamly.Internal.Data.Unboxed as Unboxed
106105import qualified Streamly.Internal.Data.Unfold.Type as Unfold
107106import qualified Text.ParserCombinators.ReadPrec as ReadPrec
@@ -113,6 +112,7 @@ import Streamly.Internal.System.IO (unsafeInlineIO, defaultChunkSize)
113112-- >>> :m
114113-- >>> :set -XMagicHash
115114-- >>> import Prelude hiding (length, foldr, read, unlines, splitAt)
115+-- >>> import Streamly.Data.Stream as Stream
116116-- >>> import Streamly.Internal.Data.Array as Array
117117
118118-------------------------------------------------------------------------------
@@ -265,8 +265,14 @@ bufferChunks :: (MonadIO m, Unbox a) =>
265265 D.Stream m a -> m (K.Stream m (Array a))
266266bufferChunks m = D.foldr K.cons K.nil $ arraysOf defaultChunkSize m
267267
268−-- | @arraysOf n stream@ groups the input stream into a stream of
269−-- arrays of size n.
268+-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
269+-- @n@ elements each.
270+--
271+-- Same as the following but may be more efficient:
272+--
273+-- >>> arraysOf n = Stream.foldMany (Array.writeN n)
274+--
275+-- /Pre-release/
270276{-# INLINE_NORMAL arraysOf #-}
271277arraysOf :: forall m a. (MonadIO m, Unbox a)
272278 => Int -> D.Stream m a -> D.Stream m (Array a)
@@ -360,7 +366,7 @@ toStreamKRev arr =
360366-- /Pre-release/
361367{-# INLINE_EARLY read #-}
362368read :: (Monad m, Unbox a) => Array a -> Stream m a
363−read = Stream.fromStreamD . toStreamD
369+read = toStreamD
364370
365371-- | Same as 'read'
366372--
@@ -377,7 +383,7 @@ toStream = read
377383-- /Pre-release/
378384{-# INLINE_EARLY readRev #-}
379385readRev :: (Monad m, Unbox a) => Array a -> Stream m a
380−readRev = Stream.fromStreamD . toStreamDRev
386+readRev = toStreamDRev
381387
382388-- | Same as 'readRev'
383389--
core/src/Streamly/Internal/Data/Fold.hs+14−15
@@ -160,7 +160,7 @@ module Streamly.Internal.Data.Fold
160160
161161 -- * Running A Fold
162162 , drive
163− , breakStream
163+ -- , breakStream
164164
165165 -- * Building Incrementally
166166 , extractM
@@ -325,7 +325,7 @@ import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
325325import Streamly.Internal.Data.Unboxed (Unbox, sizeOf)
326326import Streamly.Internal.Data.Unfold.Type (Unfold(..))
327327import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
328−import Streamly.Internal.Data.Stream.Type (Stream)
328+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
329329
330330import qualified Prelude
331331import qualified Streamly.Internal.Data.Array.Mut.Type as MA
@@ -334,8 +334,6 @@ import qualified Streamly.Internal.Data.Fold.Window as FoldW
334334import qualified Streamly.Internal.Data.Pipe.Type as Pipe
335335import qualified Streamly.Internal.Data.Ring.Unboxed as Ring
336336import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD
337−import qualified Streamly.Internal.Data.Stream.StreamK as K
338−import qualified Streamly.Internal.Data.Stream.Type as Stream
339337
340338import Prelude hiding
341339 ( filter, foldl1, drop, dropWhile, take, takeWhile, zipWith
@@ -366,7 +364,6 @@ import Streamly.Internal.Data.Fold.Tee
366364-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
367365-- >>> import qualified Streamly.Internal.Data.Fold.Window as FoldW
368366-- >>> import qualified Streamly.Internal.Data.Parser as Parser
369−-- >>> import qualified Streamly.Internal.Data.Stream.Type as Stream
370367-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
371368-- >>> import Prelude hiding (break, map, span, splitAt)
372369
@@ -388,8 +385,9 @@ import Streamly.Internal.Data.Fold.Tee
388385--
389386{-# INLINE drive #-}
390387drive :: Monad m => Stream m a -> Fold m a b -> m b
391−drive strm fl = StreamD.fold fl $ StreamD.fromStreamK $ Stream.toStreamK strm
388+drive = flip StreamD.fold
392389
390+{-
393391-- | Like 'drive' but also returns the remaining stream. The resulting stream
394392-- would be 'Stream.nil' if the stream finished before the fold.
395393--
@@ -399,13 +397,14 @@ drive strm fl = StreamD.fold fl $ StreamD.fromStreamK $ Stream.toStreamK strm
399397--
400398-- /CPS/
401399--
402−{-# INLINE breakStream #-}
403−breakStream :: Monad m => Stream m a -> Fold m a b -> m (b, Stream m a)
404−breakStream strm fl = fmap f $ K.foldBreak fl (Stream.toStreamK strm)
400+{-# INLINE breakStreamK #-}
401+breakStreamK :: Monad m => StreamK m a -> Fold m a b -> m (b, StreamK m a)
402+breakStreamK strm fl = fmap f $ K.foldBreak fl (Stream.toStreamK strm)
405403
406404 where
407405
408406 f (b, str) = (b, Stream.fromStreamK str)
407+-}
409408
410409-- | Append a stream to a fold to build the fold accumulator incrementally. We
411410-- can repeatedly call 'addStream' on the same fold to continue building the
@@ -2386,12 +2385,12 @@ chunksBetween _low _high _f1 _f2 = undefined
23862385-- /Warning!/ working on large streams accumulated as buffers in memory could
23872386-- be very inefficient, consider using "Streamly.Data.Array" instead.
23882387--
2389−-- >>> toStream = fmap Stream.fromStreamK Fold.toStreamK
2388+-- >>> toStream = fmap Stream.fromList Fold.toList
23902389--
23912390-- /Pre-release/
23922391{-# INLINE toStream #-}
2393−toStream :: Monad m => Fold m a (Stream n a)
2394−toStream = fmap Stream.fromStreamK toStreamK
2392+toStream :: (Monad m, Monad n) => Fold m a (Stream n a)
2393+toStream = fmap StreamD.fromList toList
23952394
23962395-- This is more efficient than 'toStream'. toStream is exactly the same as
23972396-- reversing the stream after toStreamRev.
@@ -2399,7 +2398,7 @@ toStream = fmap Stream.fromStreamK toStreamK
23992398-- | Buffers the input stream to a pure stream in the reverse order of the
24002399-- input.
24012400--
2402−-- >>> toStreamRev = fmap Stream.fromStreamK Fold.toStreamKRev
2401+-- >>> toStreamRev = fmap Stream.fromList Fold.toListRev
24032402--
24042403-- /Warning!/ working on large streams accumulated as buffers in memory could
24052404-- be very inefficient, consider using "Streamly.Data.Array" instead.
@@ -2408,8 +2407,8 @@ toStream = fmap Stream.fromStreamK toStreamK
24082407
24092408-- xn : ... : x2 : x1 : []
24102409{-# INLINE toStreamRev #-}
2411−toStreamRev :: Monad m => Fold m a (Stream n a)
2412−toStreamRev = fmap Stream.fromStreamK toStreamKRev
2410+toStreamRev :: (Monad m, Monad n) => Fold m a (Stream n a)
2411+toStreamRev = fmap StreamD.fromList toListRev
24132412
24142413-- XXX This does not fuse. It contains a recursive step function. We will need
24152414-- a Skip input constructor in the fold type to make it fuse.
core/src/Streamly/Internal/Data/Fold/Chunked.hs+4−14
@@ -6,6 +6,8 @@
66-- Stability : experimental
77-- Portability : GHC
88--
9+-- Use "Streamly.Data.Parser.Chunked" instead.
10+--
911-- Fold a stream of foreign arrays. @Fold m a b@ in this module works
1012-- on a stream of "Array a" and produces an output of type @b@.
1113--
@@ -20,12 +22,12 @@
2022-- >>> import qualified Streamly.Data.Fold as Fold
2123-- >>> import qualified Streamly.Internal.Data.Stream.Chunked as ArrayStream
2224-- >>> import qualified Streamly.Internal.Data.Fold.Chunked as ChunkFold
23−-- >>> import qualified Streamly.Internal.Data.Stream as Stream (arraysOf)
2425-- >>> import qualified Streamly.Data.Stream as Stream
26+-- >>> import qualified Streamly.Data.Stream.StreamK as StreamK
2527--
2628-- >>> f = ChunkFold.fromFold (Fold.take 7 Fold.toList)
2729-- >>> s = Stream.arraysOf 5 $ Stream.fromList "hello world"
28−-- >>> ArrayStream.runArrayFold f s
30+-- >>> ArrayStream.runArrayFold f (StreamK.fromStream s)
2931-- Right "hello w"
3032--
3133module Streamly.Internal.Data.Fold.Chunked
@@ -98,11 +100,7 @@ newtype ChunkFold m a b = ChunkFold (ParserD.Parser (Array a) m b)
98100--
99101-- /Pre-release/
100102{-# INLINE fromFold #-}
101−#ifdef DEVBUILD
102−fromFold :: forall m a b. (MonadIO m) =>
103−#else
104103fromFold :: forall m a b. (MonadIO m, Unbox a) =>
105−#endif
106104 Fold.Fold m a b -> ChunkFold m a b
107105fromFold (Fold.Fold fstep finitial fextract) =
108106 ChunkFold (ParserD.Parser step initial (fmap (Done 0) . fextract))
@@ -140,11 +138,7 @@ fromFold (Fold.Fold fstep finitial fextract) =
140138--
141139-- /Pre-release/
142140{-# INLINE fromParserD #-}
143−#ifdef DEVBUILD
144−fromParserD :: forall m a b. (MonadIO m) =>
145−#else
146141fromParserD :: forall m a b. (MonadIO m, Unbox a) =>
147−#endif
148142 ParserD.Parser a m b -> ChunkFold m a b
149143fromParserD (ParserD.Parser step1 initial1 extract1) =
150144 ChunkFold (ParserD.Parser step initial1 extract1)
@@ -186,11 +180,7 @@ fromParserD (ParserD.Parser step1 initial1 extract1) =
186180--
187181-- /Pre-release/
188182{-# INLINE fromParser #-}
189−#ifdef DEVBUILD
190−fromParser :: forall m a b. (MonadIO m) =>
191−#else
192183fromParser :: forall m a b. (MonadIO m, Unbox a) =>
193−#endif
194184 Parser.Parser a m b -> ChunkFold m a b
195185fromParser = fromParserD . ParserD.fromParserK
196186
core/src/Streamly/Internal/Data/Fold/Type.hs+1−1
@@ -445,7 +445,7 @@ import Prelude hiding (concatMap, filter, foldr, map, take)
445445-- >>> import Data.Maybe (fromJust, isJust)
446446-- >>> import Data.Monoid (Endo(..))
447447-- >>> import Streamly.Data.Fold (Fold)
448−-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
448+-- >>> import Streamly.Data.Stream (Stream)
449449-- >>> import qualified Data.Foldable as Foldable
450450-- >>> import qualified Streamly.Data.Fold as Fold
451451-- >>> import qualified Streamly.Internal.Data.Fold as Fold
core/src/Streamly/Internal/Data/Parser.hs+10−4
@@ -251,13 +251,19 @@ where
251251
252252import Streamly.Internal.Data.Fold.Type (Fold(..))
253253import Streamly.Internal.Data.Parser.ParserK.Type (Parser)
254−import Streamly.Internal.Data.Stream.Type (Stream)
255254
256255import qualified Data.Foldable as Foldable
257256import qualified Streamly.Internal.Data.Fold.Type as FL
258257import qualified Streamly.Internal.Data.Parser.ParserD as D
259258import qualified Streamly.Internal.Data.Parser.ParserK.Type as K
260−import qualified Streamly.Internal.Data.Stream.Type as Stream
259+
260+#ifdef USE_STREAMK
261+import Streamly.Internal.Data.StreamK (Stream)
262+import qualified Streamly.Internal.Data.StreamK as Stream
263+#else
264+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
265+import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
266+#endif
261267
262268import Prelude hiding
263269 ( any, all, dropWhile, take, takeWhile, sequence, concatMap, maybe, either
@@ -1007,7 +1013,7 @@ groupByRollingEither eq f1 = D.toParserK . D.groupByRollingEither eq f1
10071013--
10081014{-# INLINE streamEqBy #-}
10091015streamEqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Parser a m ()
1010−streamEqBy cmp = D.toParserK . D.streamEqBy cmp . Stream.toStreamD
1016+streamEqBy cmp = D.toParserK . D.streamEqBy cmp
10111017
10121018-- | Match the given sequence of elements using the given comparison function.
10131019-- Returns the original sequence if successful.
@@ -1259,7 +1265,7 @@ concatSequence ::
12591265 Monad m =>
12601266 Fold m b c -> Stream m (Parser a m b) -> Parser a m c
12611267concatSequence f p =
1262− let sp = fmap D.fromParserK $ Stream.toStreamD p
1268+ let sp = fmap D.fromParserK p
12631269 in D.toParserK $ D.sequence sp f
12641270
12651271-- | Map a 'Parser' returning function on the result of a 'Parser'.
core/src/Streamly/Internal/Data/Parser/Chunked.hs+9−10
@@ -42,7 +42,7 @@ import GHC.Types (SPEC(..))
4242import Streamly.Internal.Data.Array.Type (Array(..))
4343import Streamly.Internal.Data.Parser.Chunked.Type (ChunkParser (..))
4444import Streamly.Internal.Data.Parser.ParserD.Type (Initial(..), Step(..))
45−import Streamly.Internal.Data.Stream.Type (Stream)
45+import Streamly.Internal.Data.Stream.StreamK.Type (StreamK)
4646import Streamly.Internal.Data.SVar.Type (defState)
4747import Streamly.Internal.Data.Unboxed (peekWith, sizeOf, Unbox)
4848
@@ -51,7 +51,6 @@ import qualified Streamly.Internal.Data.Parser.Chunked.Type as K
5151import qualified Streamly.Internal.Data.Parser.ParserD as D
5252 hiding (fromParserK, toParserK)
5353import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
54−import qualified Streamly.Internal.Data.Stream.Type as Stream
5554import Streamly.Internal.Data.Parser.ParserD (ParseError(..))
5655
5756-------------------------------------------------------------------------------
@@ -97,11 +96,11 @@ parserDone (K.Failure n e) _ _ = return $ K.Error n e
9796parseBreak
9897 :: (Monad m, Unbox a)
9998 => ChunkParser a m b
100− -> Stream m (Array a)
101− -> m (Either ParseError b, Stream m (Array a))
99+ -> StreamK m (Array a)
100+ -> m (Either ParseError b, StreamK m (Array a))
102101parseBreak parser input = do
103102 let parserk = \arr -> K.runParser parser 0 0 arr parserDone
104− in go [] parserk (Stream.toStreamK input)
103+ in go [] parserk input
105104
106105 where
107106
@@ -127,13 +126,13 @@ parseBreak parser input = do
127126 let (s1, backBuf1) = backTrack n1 backBuf StreamK.nil
128127 in go backBuf1 cont1 s1
129128 K.Done 0 b ->
130− return (Right b, Stream.nil)
129+ return (Right b, StreamK.nil)
131130 K.Done n b -> do
132131 let n1 = negate n
133132 assertM(n1 >= 0 && n1 <= sum (map Array.length backBuf))
134133 let (s1, _) = backTrack n1 backBuf StreamK.nil
135− in return (Right b, Stream.fromStreamK s1)
136− K.Error _ err -> return (Left (ParseError err), Stream.nil)
134+ in return (Right b, s1)
135+ K.Error _ err -> return (Left (ParseError err), StreamK.nil)
137136
138137 seekErr n len =
139138 error $ "parseBreak: Partial: forward seek not implemented n = "
@@ -175,8 +174,8 @@ parseBreak parser input = do
175174 let n1 = len - n
176175 assertM(n1 <= sum (map Array.length (arr:backBuf)))
177176 let (s1, _) = backTrack n1 (arr:backBuf) stream
178− in return (Right b, Stream.fromStreamK s1)
179− K.Error _ err -> return (Left (ParseError err), Stream.nil)
177+ in return (Right b, s1)
178+ K.Error _ err -> return (Left (ParseError err), StreamK.nil)
180179
181180 go backBuf parserk stream = do
182181 let stop = goStop backBuf parserk
core/src/Streamly/Internal/Data/Ring/Unboxed.hs+1−1
@@ -84,7 +84,7 @@ import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
8484import GHC.Ptr (Ptr(..))
8585import Streamly.Internal.Data.Array.Mut.Type (Array)
8686import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..), lmap)
87−import Streamly.Internal.Data.Stream.Type (Stream)
87+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
8888import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
8989import Streamly.Internal.Data.Unfold.Type (Unfold(..))
9090import Streamly.Internal.System.IO (unsafeInlineIO)
core/src/Streamly/Internal/Data/Stream.hs+3−32
@@ -1,43 +1,14 @@
11-- |
22-- Module : Streamly.Internal.Data.Stream
3−-- Copyright : (c) 2017 Composewell Technologies
3+-- Copyright : (c) 2019 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
66-- Stability : experimental
77-- Portability : GHC
88--
99module Streamly.Internal.Data.Stream
10− ( module Streamly.Internal.Data.Stream.Type
11− , module Streamly.Internal.Data.Stream.Bottom
12− , module Streamly.Internal.Data.Stream.Eliminate
13− , module Streamly.Internal.Data.Stream.Exception
14− , module Streamly.Internal.Data.Stream.Expand
15− , module Streamly.Internal.Data.Stream.Generate
16− , module Streamly.Internal.Data.Stream.Lift
17− , module Streamly.Internal.Data.Stream.Reduce
18− , module Streamly.Internal.Data.Stream.Transform
19− , module Streamly.Internal.Data.Stream.Top
20− , module Streamly.Internal.Data.Stream.Cross
21− , module Streamly.Internal.Data.Stream.Zip
22−
23− -- modules having dependencies on libraries other than base
24− , module Streamly.Internal.Data.Stream.Transformer
25− , module Streamly.Internal.Data.Stream.Container
10+ ( module Streamly.Internal.Data.Stream.StreamD
2611 )
2712where
2813
29−import Streamly.Internal.Data.Stream.Bottom
30−import Streamly.Internal.Data.Stream.Cross
31−import Streamly.Internal.Data.Stream.Eliminate
32−import Streamly.Internal.Data.Stream.Exception
33−import Streamly.Internal.Data.Stream.Expand
34−import Streamly.Internal.Data.Stream.Generate
35−import Streamly.Internal.Data.Stream.Lift
36−import Streamly.Internal.Data.Stream.Reduce
37−import Streamly.Internal.Data.Stream.Top
38−import Streamly.Internal.Data.Stream.Transform
39−import Streamly.Internal.Data.Stream.Type
40−import Streamly.Internal.Data.Stream.Zip
41−
42−import Streamly.Internal.Data.Stream.Container
43−import Streamly.Internal.Data.Stream.Transformer
14+import Streamly.Internal.Data.Stream.StreamD
core/src/Streamly/Internal/Data/Stream/Bottom.hs+7−7
@@ -131,7 +131,7 @@ import Streamly.Internal.Data.Stream.Type
131131--
132132{-# INLINE timesWith #-}
133133timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)
134−timesWith g = fromStreamD $ D.times g
134+timesWith g = fromStreamD $ D.timesWith g
135135
136136-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
137137-- granularity @g@ specified in seconds. A low granularity clock is more
@@ -186,7 +186,7 @@ relTimesWith = fmap snd . timesWith
186186--
187187{-# INLINE foldAddLazy #-}
188188foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b
189−foldAddLazy f s = D.foldContinue f $ toStreamD s
189+foldAddLazy f s = D.foldAddLazy f $ toStreamD s
190190
191191-- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce
192192
@@ -385,7 +385,7 @@ map f = fromStreamD . D.map f . toStreamD
385385--
386386{-# INLINE postscan #-}
387387postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b
388−postscan fld = fromStreamD . D.postscanOnce fld . toStreamD
388+postscan fld = fromStreamD . D.postscan fld . toStreamD
389389
390390-- $smapM_Notes
391391--
@@ -531,8 +531,8 @@ intersperseM m = fromStreamD . D.intersperseM m . toStreamD
531531-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
532532--
533533{-# INLINE reverse #-}
534−reverse :: Monad m => Stream m a -> Stream m a
535−reverse s = fromStreamD $ D.reverse $ toStreamD s
534+reverse :: Stream m a -> Stream m a
535+reverse s = fromStreamK $ K.reverse $ toStreamK s
536536
537537-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
538538--
@@ -651,7 +651,7 @@ foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
651651{-# INLINE zipWithM #-}
652652zipWithM :: Monad m =>
653653 (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
654−zipWithM f m1 m2 = fromStreamD $ D.zipWithM f (toStreamD m1) (toStreamD m2)
654+zipWithM f m1 m2 = fromStreamK $ K.zipWithM f (toStreamK m1) (toStreamK m2)
655655
656656-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
657657-- elements @a@ and @b@ are then zipped using the supplied zip function and the
@@ -667,4 +667,4 @@ zipWithM f m1 m2 = fromStreamD $ D.zipWithM f (toStreamD m1) (toStreamD m2)
667667--
668668{-# INLINE zipWith #-}
669669zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
670−zipWith f m1 m2 = fromStreamD $ D.zipWith f (toStreamD m1) (toStreamD m2)
670+zipWith f m1 m2 = fromStreamK $ K.zipWith f (toStreamK m1) (toStreamK m2)
core/src/Streamly/Internal/Data/Stream/Chunked.hs+65−61
@@ -23,27 +23,42 @@ module Streamly.Internal.Data.Stream.Chunked
2323
2424 -- * Elimination
2525 -- ** Element Folds
26− , foldBreak
26+ -- The byte level foldBreak can work as efficiently as the chunk level. We
27+ -- can flatten the stream to byte stream and use that. But if we want the
28+ -- remaining stream to be a chunk stream then this could be handy. But it
29+ -- could also be implemented using parseBreak.
30+ , foldBreak -- StreamK.foldBreakChunks
2731 , foldBreakD
28− , parseBreak
32+ -- The byte level parseBreak cannot work efficiently. Because the stream
33+ -- will have to be a StreamK for backtracking, StreamK at byte level would
34+ -- not be efficient.
35+ , parseBreak -- StreamK.parseBreakChunks
2936 -- , parseBreakD
37+ -- , foldManyChunks
38+ -- , parseManyChunks
3039
3140 -- ** Array Folds
41+ -- XXX Use Parser.Chunked instead, need only chunkedParseBreak,
42+ -- foldBreak can be implemented using parseBreak. Use StreamK.
3243 , runArrayFold
3344 , runArrayFoldBreak
3445 -- , parseArr
35− , runArrayParserDBreak
36− , runArrayFoldMany
46+ , runArrayParserDBreak -- StreamK.chunkedParseBreak
47+ , runArrayFoldMany -- StreamK.chunkedParseMany
3748
3849 , toArray
3950
4051 -- * Compaction
41− , lpackArraysChunksOf
42− , compact
52+ -- We can use something like foldManyChunks, parseManyChunks with a take
53+ -- fold.
54+ , lpackArraysChunksOf -- Fold.compactChunks
55+ , compact -- rechunk, compactChunks
4356
4457 -- * Splitting
45− , splitOn
46− , splitOnSuffix
58+ -- We can use something like foldManyChunks, parseManyChunks with an
59+ -- appropriate splitting fold.
60+ , splitOn -- Stream.rechunkOn
61+ , splitOnSuffix -- Stream.rechunkOnSuffix
4762 )
4863where
4964
@@ -62,12 +77,11 @@ import GHC.Types (SPEC(..))
6277import Prelude hiding (null, last, (!!), read, concat, unlines)
6378
6479import Streamly.Data.Fold (Fold)
65−import Streamly.Data.Stream (Stream)
6680import Streamly.Internal.Data.Array.Type (Array(..))
6781import Streamly.Internal.Data.Fold.Chunked (ChunkFold(..))
6882import Streamly.Internal.Data.Parser (ParseError(..))
69−import Streamly.Internal.Data.Stream
70− (fromStreamD, fromStreamK, toStreamD, toStreamK)
83+import Streamly.Internal.Data.Stream.StreamD (Stream)
84+import Streamly.Internal.Data.Stream.StreamK (StreamK, fromStream, toStream)
7185import Streamly.Internal.Data.SVar.Type (adaptState, defState)
7286import Streamly.Internal.Data.Array.Mut.Type
7387 (allocBytesToElemCount)
@@ -83,15 +97,8 @@ import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))
8397import qualified Streamly.Internal.Data.Parser as PR
8498import qualified Streamly.Internal.Data.Parser.ParserD as PRD
8599 (Parser(..), Initial(..), fromParserK)
86−import qualified Streamly.Internal.Data.Stream as S
87100import qualified Streamly.Internal.Data.Stream.StreamD as D
88− ( fromList, nil, cons, map
89− , unfoldMany, append, splitInnerBy, splitInnerBySuffix, foldlM'
90− )
91−import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
92− (Step(Yield, Stop, Skip), Stream(Stream))
93−import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
94− (Stream, cons, nil, fromPure, foldStream)
101+import qualified Streamly.Internal.Data.Stream.StreamK as K
95102
96103-- XXX Since these are immutable arrays MonadIO constraint can be removed from
97104-- most places.
@@ -109,7 +116,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
109116{-# INLINE arraysOf #-}
110117arraysOf :: (MonadIO m, Unbox a)
111118 => Int -> Stream m a -> Stream m (Array a)
112−arraysOf n str = fromStreamD $ A.arraysOf n (toStreamD str)
119+arraysOf = A.arraysOf
113120
114121-------------------------------------------------------------------------------
115122-- Append
@@ -130,7 +137,7 @@ arraysOf n str = fromStreamD $ A.arraysOf n (toStreamD str)
130137concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
131138-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
132139-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
133−concat m = fromStreamD $ D.unfoldMany A.reader (toStreamD m)
140+concat = D.unfoldMany A.reader
134141
135142-- | Convert a stream of arrays into a stream of their elements reversing the
136143-- contents of each array before flattening.
@@ -141,7 +148,7 @@ concat m = fromStreamD $ D.unfoldMany A.reader (toStreamD m)
141148{-# INLINE concatRev #-}
142149concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
143150-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
144−concatRev m = fromStreamD $ D.unfoldMany A.readerRev (toStreamD m)
151+concatRev = D.unfoldMany A.readerRev
145152
146153-------------------------------------------------------------------------------
147154-- Intersperse and append
@@ -153,12 +160,12 @@ concatRev m = fromStreamD $ D.unfoldMany A.readerRev (toStreamD m)
153160-- /Pre-release/
154161{-# INLINE interpose #-}
155162interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
156−interpose x = S.interpose x A.reader
163+interpose x = D.interpose x A.reader
157164
158165{-# INLINE intercalateSuffix #-}
159166intercalateSuffix :: (Monad m, Unbox a)
160167 => Array a -> Stream m (Array a) -> Stream m a
161−intercalateSuffix = S.intercalateSuffix A.reader
168+intercalateSuffix = D.intercalateSuffix A.reader
162169
163170-- | Flatten a stream of arrays appending the given element after each
164171-- array.
@@ -168,12 +175,13 @@ intercalateSuffix = S.intercalateSuffix A.reader
168175interposeSuffix :: (Monad m, Unbox a)
169176 => a -> Stream m (Array a) -> Stream m a
170177-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
171−interposeSuffix x = S.interposeSuffix x A.reader
178+interposeSuffix x = D.interposeSuffix x A.reader
172179
173180data FlattenState s =
174181 OuterLoop s
175182 | InnerLoop s !MA.MutableByteArray !Int !Int
176183
184+-- XXX This is a special case of interposeSuffix, can be removed.
177185-- XXX Remove monadIO constraint
178186{-# INLINE_NORMAL unlines #-}
179187unlines :: forall m a. (MonadIO m, Unbox a)
@@ -203,6 +211,11 @@ unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
203211-- XXX These would not be needed once we implement compactLEFold, see
204212-- module Streamly.Internal.Data.Array.Mut.Stream
205213--
214+-- XXX Note that this thaws immutable arrays for appending, that may be
215+-- problematic if multiple users do the same thing, however, immutable arrays
216+-- would usually have no capacity to append, therefore, a copy will be forced
217+-- anyway. Confirm this. We can forcefully trim the array capacity before thaw
218+-- to ensure this.
206219{-# INLINE_NORMAL packArraysChunksOf #-}
207220packArraysChunksOf :: (MonadIO m, Unbox a)
208221 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
@@ -211,6 +224,8 @@ packArraysChunksOf n str =
211224
212225-- XXX instead of writing two different versions of this operation, we should
213226-- write it as a pipe.
227+--
228+-- XXX Confirm that immutable arrays won't be modified.
214229{-# INLINE_NORMAL lpackArraysChunksOf #-}
215230lpackArraysChunksOf :: (MonadIO m, Unbox a)
216231 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
@@ -224,7 +239,7 @@ lpackArraysChunksOf n fld =
224239{-# INLINE compact #-}
225240compact :: (MonadIO m, Unbox a)
226241 => Int -> Stream m (Array a) -> Stream m (Array a)
227−compact n xs = fromStreamD $ packArraysChunksOf n (toStreamD xs)
242+compact = packArraysChunksOf
228243
229244-------------------------------------------------------------------------------
230245-- Split
@@ -298,8 +313,7 @@ splitOn
298313 => Word8
299314 -> Stream m (Array Word8)
300315 -> Stream m (Array Word8)
301−splitOn byte s =
302− fromStreamD $ D.splitInnerBy (A.breakOn byte) A.splice $ toStreamD s
316+splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice
303317
304318{-# INLINE splitOnSuffix #-}
305319splitOnSuffix
@@ -308,8 +322,7 @@ splitOnSuffix
308322 -> Stream m (Array Word8)
309323 -> Stream m (Array Word8)
310324-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
311−splitOnSuffix byte s =
312− fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.splice $ toStreamD s
325+splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
313326
314327-------------------------------------------------------------------------------
315328-- Elimination - Running folds
@@ -395,13 +408,10 @@ foldBreakK (FL.Fold fstep initial extract) stream = do
395408foldBreak ::
396409 (MonadIO m, Unbox a)
397410 => Fold m a b
398− -> Stream m (A.Array a)
399− -> m (b, Stream m (A.Array a))
411+ -> StreamK m (A.Array a)
412+ -> m (b, StreamK m (A.Array a))
400413-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
401−foldBreak f =
402− fmap (fmap fromStreamK)
403− . foldBreakK f
404− . toStreamK
414+foldBreak = foldBreakK
405415-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
406416-- rule to rewrite runArrayFoldBreak to fold.
407417-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
@@ -457,10 +467,6 @@ splitAtArrayListRev n ls
457467-- Fold to a single Array
458468-------------------------------------------------------------------------------
459469
460−{-# INLINE foldlM' #-}
461−foldlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> m b
462−foldlM' step begin = D.foldlM' step begin . S.toStreamD
463−
464470-- XXX Both of these implementations of splicing seem to perform equally well.
465471-- We need to perform benchmarks over a range of sizes though.
466472
@@ -471,16 +477,16 @@ spliceArraysLenUnsafe :: (MonadIO m, Unbox a)
471477 => Int -> Stream m (MA.Array a) -> m (MA.Array a)
472478spliceArraysLenUnsafe len buffered = do
473479 arr <- liftIO $ MA.newPinned len
474− foldlM' MA.spliceUnsafe (return arr) buffered
480+ D.foldlM' MA.spliceUnsafe (return arr) buffered
475481
476482{-# INLINE _spliceArrays #-}
477483_spliceArrays :: (MonadIO m, Unbox a)
478484 => Stream m (Array a) -> m (Array a)
479485_spliceArrays s = do
480− buffered <- S.foldr S.cons S.nil s
481− len <- S.fold FL.sum (fmap Array.length buffered)
486+ buffered <- D.foldr K.cons K.nil s
487+ len <- K.fold FL.sum (fmap Array.length buffered)
482488 arr <- liftIO $ MA.newPinned len
483− final <- foldlM' writeArr (return arr) s
489+ final <- D.foldlM' writeArr (return arr) (toStream buffered)
484490 return $ A.unsafeFreeze final
485491
486492 where
@@ -491,9 +497,10 @@ _spliceArrays s = do
491497_spliceArraysBuffered :: (MonadIO m, Unbox a)
492498 => Stream m (Array a) -> m (Array a)
493499_spliceArraysBuffered s = do
494− buffered <- S.foldr S.cons S.nil s
495− len <- S.fold FL.sum (fmap Array.length buffered)
496− A.unsafeFreeze <$> spliceArraysLenUnsafe len (fmap A.unsafeThaw s)
500+ buffered <- D.foldr K.cons K.nil s
501+ len <- K.fold FL.sum (fmap Array.length buffered)
502+ A.unsafeFreeze <$>
503+ spliceArraysLenUnsafe len (fmap A.unsafeThaw (toStream buffered))
497504
498505{-# INLINE spliceArraysRealloced #-}
499506spliceArraysRealloced :: forall m a. (MonadIO m, Unbox a)
@@ -502,7 +509,7 @@ spliceArraysRealloced s = do
502509 let n = allocBytesToElemCount (undefined :: a) (4 * 1024)
503510 idst = liftIO $ MA.newPinned n
504511
505− arr <- foldlM' MA.spliceExp idst (fmap A.unsafeThaw s)
512+ arr <- D.foldlM' MA.spliceExp idst (fmap A.unsafeThaw s)
506513 liftIO $ A.unsafeFreeze <$> MA.rightSize arr
507514
508515-- XXX This should just be "fold A.write"
@@ -776,16 +783,13 @@ parseBreakK (PRD.Parser pstep initial extract) stream = do
776783parseBreak ::
777784 (MonadIO m, Unbox a)
778785 => PR.Parser a m b
779− -> Stream m (A.Array a)
780− -> m (Either ParseError b, Stream m (A.Array a))
786+ -> StreamK m (A.Array a)
787+ -> m (Either ParseError b, StreamK m (A.Array a))
781788{-
782789parseBreak p s =
783790 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
784791-}
785−parseBreak p =
786− fmap (fmap fromStreamK)
787− . parseBreakK (PRD.fromParserK p)
788− . toStreamK
792+parseBreak p = parseBreakK (PRD.fromParserK p)
789793
790794-------------------------------------------------------------------------------
791795-- Elimination - Running Array Folds and parsers
@@ -947,8 +951,8 @@ parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)
947951--
948952{-# INLINE runArrayFold #-}
949953runArrayFold :: (MonadIO m, Unbox a) =>
950− ChunkFold m a b -> Stream m (A.Array a) -> m (Either ParseError b)
951−runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStreamD s)
954+ ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b)
955+runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStream s)
952956
953957-- | Like 'fold' but also returns the remaining stream.
954958--
@@ -956,9 +960,9 @@ runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStreamD s)
956960--
957961{-# INLINE runArrayFoldBreak #-}
958962runArrayFoldBreak :: (MonadIO m, Unbox a) =>
959− ChunkFold m a b -> Stream m (A.Array a) -> m (Either ParseError b, Stream m (A.Array a))
963+ ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b, StreamK m (A.Array a))
960964runArrayFoldBreak (ChunkFold p) s =
961− second fromStreamD <$> runArrayParserDBreak p (toStreamD s)
965+ second fromStream <$> runArrayParserDBreak p (toStream s)
962966
963967{-# ANN type ParseChunksState Fuse #-}
964968data ParseChunksState x inpBuf st pst =
@@ -1205,6 +1209,6 @@ runArrayFoldManyD
12051209runArrayFoldMany
12061210 :: (Monad m, Unbox a)
12071211 => ChunkFold m a b
1208− -> Stream m (Array a)
1209− -> Stream m (Either ParseError b)
1210−runArrayFoldMany p m = fromStreamD $ runArrayFoldManyD p (toStreamD m)
1212+ -> StreamK m (Array a)
1213+ -> StreamK m (Either ParseError b)
1214+runArrayFoldMany p m = fromStream $ runArrayFoldManyD p (toStream m)
core/src/Streamly/Internal/Data/Stream/Cross.hs+1−1
@@ -121,7 +121,7 @@ instance Monad m => Monad (CrossStream m) where
121121 CrossStream
122122 (Stream.fromStreamK
123123 $ K.bindWith
124− K.serial
124+ K.append
125125 (Stream.toStreamK m)
126126 (Stream.toStreamK . unCrossStream . f))
127127
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+3−3
@@ -217,7 +217,7 @@ foldlS f z =
217217--
218218{-# INLINE_NORMAL parseD #-}
219219parseD :: Monad m => PRD.Parser a m b -> Stream m a -> m (Either ParseError b)
220−parseD p = D.parse p . toStreamD
220+parseD p = D.parseD p . toStreamD
221221
222222-- XXX Drive directly as parserK rather than converting to parserD first.
223223
@@ -248,7 +248,7 @@ parse = parseD . PRD.fromParserK
248248parseBreakD :: Monad m =>
249249 PRD.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
250250parseBreakD parser strm = do
251− (b, strmD) <- D.parseBreak parser (toStreamD strm)
251+ (b, strmD) <- D.parseBreakD parser (toStreamD strm)
252252 return $! (b, fromStreamD strmD)
253253
254254-- | Parse a stream using the supplied 'Parser'.
@@ -257,7 +257,7 @@ parseBreakD parser strm = do
257257--
258258{-# INLINE parseBreak #-}
259259parseBreak :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
260−parseBreak p strm = fmap f $ K.parseBreak (PRD.fromParserK p) (toStreamK strm)
260+parseBreak p strm = fmap f $ K.parseBreak p (toStreamK strm)
261261
262262 where
263263
core/src/Streamly/Internal/Data/Stream/Exception.hs+6−6
@@ -63,7 +63,7 @@ before action xs = fromStreamD $ D.before action $ toStreamD xs
6363--
6464{-# INLINE afterUnsafe #-}
6565afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a
66−afterUnsafe action xs = fromStreamD $ D.after_ action $ toStreamD xs
66+afterUnsafe action xs = fromStreamD $ D.afterUnsafe action $ toStreamD xs
6767
6868-- | Run the action @IO b@ whenever the stream is evaluated to completion, or
6969-- if it is garbage collected after a partial lazy evaluation.
@@ -75,7 +75,7 @@ afterUnsafe action xs = fromStreamD $ D.after_ action $ toStreamD xs
7575--
7676{-# INLINE afterIO #-}
7777afterIO :: MonadIO m => IO b -> Stream m a -> Stream m a
78−afterIO action xs = fromStreamD $ D.after action $ toStreamD xs
78+afterIO action xs = fromStreamD $ D.afterIO action $ toStreamD xs
7979
8080-- | Run the action @m b@ if the stream evaluation is aborted due to an
8181-- exception. The exception is not caught, simply rethrown.
@@ -98,7 +98,7 @@ onException action xs = fromStreamD $ D.onException action $ toStreamD xs
9898--
9999{-# INLINE finallyUnsafe #-}
100100finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a
101−finallyUnsafe action xs = fromStreamD $ D.finally_ action $ toStreamD xs
101+finallyUnsafe action xs = fromStreamD $ D.finallyUnsafe action $ toStreamD xs
102102
103103-- | Run the action @IO b@ whenever the stream stream stops normally, aborts
104104-- due to an exception or if it is garbage collected after a partial lazy
@@ -116,7 +116,7 @@ finallyUnsafe action xs = fromStreamD $ D.finally_ action $ toStreamD xs
116116{-# INLINE finallyIO #-}
117117finallyIO :: (MonadIO m, MonadCatch m) =>
118118 IO b -> Stream m a -> Stream m a
119−finallyIO action xs = fromStreamD $ D.finally action $ toStreamD xs
119+finallyIO action xs = fromStreamD $ D.finallyIO action $ toStreamD xs
120120
121121-- | Like 'bracket' but with following differences:
122122--
@@ -132,7 +132,7 @@ finallyIO action xs = fromStreamD $ D.finally action $ toStreamD xs
132132{-# INLINE bracketUnsafe #-}
133133bracketUnsafe :: MonadCatch m
134134 => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
135−bracketUnsafe bef aft bet = fromStreamD $ D.bracket_ bef aft (toStreamD . bet)
135+bracketUnsafe bef aft bet = fromStreamD $ D.bracketUnsafe bef aft (toStreamD . bet)
136136
137137-- | Run the alloc action @IO b@ with async exceptions disabled but keeping
138138-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
@@ -188,7 +188,7 @@ bracketIO3 :: (MonadIO m, MonadCatch m)
188188 -> (b -> Stream m a)
189189 -> Stream m a
190190bracketIO3 bef aft gc exc bet = fromStreamD $
191− D.bracket' bef aft exc gc (toStreamD . bet)
191+ D.bracketIO3 bef aft exc gc (toStreamD . bet)
192192
193193-- | Like 'handle' but the exception handler is also provided with the stream
194194-- that generated the exception as input. The exception handler can thus
core/src/Streamly/Internal/Data/Stream/Expand.hs+9−9
@@ -281,7 +281,7 @@ interleaveMin2 s1 s2 =
281281{-# INLINE interleaveFstSuffix2 #-}
282282interleaveFstSuffix2 :: Monad m => Stream m b -> Stream m b -> Stream m b
283283interleaveFstSuffix2 m1 m2 =
284− fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
284+ fromStreamD $ D.interleaveFstSuffix (toStreamD m1) (toStreamD m2)
285285
286286-- | Interleaves the outputs of two streams, yielding elements from each stream
287287-- alternately, starting from the first stream and ending at the first stream.
@@ -305,7 +305,7 @@ interleaveFstSuffix2 m1 m2 =
305305{-# INLINE interleaveFst2 #-}
306306interleaveFst2 :: Monad m => Stream m b -> Stream m b -> Stream m b
307307interleaveFst2 m1 m2 =
308− fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
308+ fromStreamD $ D.interleaveFst (toStreamD m1) (toStreamD m2)
309309
310310------------------------------------------------------------------------------
311311-- Scheduling
@@ -449,7 +449,7 @@ unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
449449{-# INLINE unfoldInterleave #-}
450450unfoldInterleave ::Monad m => Unfold m a b -> Stream m a -> Stream m b
451451unfoldInterleave u m =
452− fromStreamD $ D.unfoldManyInterleave u (toStreamD m)
452+ fromStreamD $ D.unfoldInterleave u (toStreamD m)
453453
454454-- | 'unfoldInterleave' switches to the next stream whenever a value from a
455455-- stream is yielded, it does not switch on a 'Skip'. So if a stream keeps
@@ -460,7 +460,7 @@ unfoldInterleave u m =
460460{-# INLINE unfoldRoundRobin #-}
461461unfoldRoundRobin ::Monad m => Unfold m a b -> Stream m a -> Stream m b
462462unfoldRoundRobin u m =
463− fromStreamD $ D.unfoldManyRoundRobin u (toStreamD m)
463+ fromStreamD $ D.unfoldRoundRobin u (toStreamD m)
464464
465465------------------------------------------------------------------------------
466466-- Combine N Streams - interpose
@@ -478,7 +478,7 @@ unfoldRoundRobin u m =
478478interpose :: Monad m
479479 => c -> Unfold m b c -> Stream m b -> Stream m c
480480interpose x unf str =
481− fromStreamD $ D.interpose (return x) unf (toStreamD str)
481+ fromStreamD $ D.interpose x unf (toStreamD str)
482482
483483-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
484484
@@ -492,7 +492,7 @@ interpose x unf str =
492492interposeSuffix :: Monad m
493493 => c -> Unfold m b c -> Stream m b -> Stream m c
494494interposeSuffix x unf str =
495− fromStreamD $ D.interposeSuffix (return x) unf (toStreamD str)
495+ fromStreamD $ D.interposeSuffix x unf (toStreamD str)
496496
497497------------------------------------------------------------------------------
498498-- Combine N Streams - intercalate
@@ -563,8 +563,8 @@ gintercalateSuffix unf1 str1 unf2 str2 =
563563{-# INLINE intercalateSuffix #-}
564564intercalateSuffix :: Monad m
565565 => Unfold m b c -> b -> Stream m b -> Stream m c
566−intercalateSuffix unf seed str = fromStreamD $ D.unfoldMany unf
567− $ D.intersperseMSuffix (return seed) (toStreamD str)
566+intercalateSuffix unf seed =
567+ fromStreamD . D.intercalateSuffix unf seed . toStreamD
568568
569569------------------------------------------------------------------------------
570570-- Combine N Streams - concatMap
@@ -633,7 +633,7 @@ mergeMapWith ::
633633 -> Stream m b
634634mergeMapWith par f m =
635635 fromStreamK
636− $ K.concatPairsWith
636+ $ K.mergeMapWith
637637 (\s1 s2 -> toStreamK $ fromStreamK s1 `par` fromStreamK s2)
638638 (toStreamK . f)
639639 (toStreamK m)
core/src/Streamly/Internal/Data/Stream/Reduce.hs+3−3
@@ -355,7 +355,7 @@ parseMany
355355 -> Stream m a
356356 -> Stream m (Either ParseError b)
357357parseMany p m =
358− fromStreamD $ D.parseMany (ParserD.fromParserK p) (toStreamD m)
358+ fromStreamD $ D.parseManyD (ParserD.fromParserK p) (toStreamD m)
359359
360360-- | Same as parseMany but for StreamD streams.
361361--
@@ -368,7 +368,7 @@ parseManyD
368368 -> Stream m a
369369 -> Stream m (Either ParseError b)
370370parseManyD p m =
371− fromStreamD $ D.parseMany p (toStreamD m)
371+ fromStreamD $ D.parseManyD p (toStreamD m)
372372
373373-- | Apply a stream of parsers to an input stream and emit the results in the
374374-- output stream.
@@ -424,7 +424,7 @@ parseIterate
424424 -> Stream m a
425425 -> Stream m (Either ParseError b)
426426parseIterate f i m = fromStreamD $
427− D.parseIterate (ParserD.fromParserK . f) i (toStreamD m)
427+ D.parseIterateD (ParserD.fromParserK . f) i (toStreamD m)
428428
429429------------------------------------------------------------------------------
430430-- Chunking
core/src/Streamly/Internal/Data/Stream/StreamD.hs+4
@@ -25,6 +25,8 @@ module Streamly.Internal.Data.Stream.StreamD
2525 , module Streamly.Internal.Data.Stream.StreamD.Transformer
2626 , module Streamly.Internal.Data.Stream.StreamD.Nesting
2727 , module Streamly.Internal.Data.Stream.StreamD.Transform
28+ , module Streamly.Internal.Data.Stream.StreamD.Top
29+ , module Streamly.Internal.Data.Stream.StreamD.Container
2830 )
2931where
3032
@@ -36,3 +38,5 @@ import Streamly.Internal.Data.Stream.StreamD.Lift
3638import Streamly.Internal.Data.Stream.StreamD.Transformer
3739import Streamly.Internal.Data.Stream.StreamD.Nesting
3840import Streamly.Internal.Data.Stream.StreamD.Transform
41+import Streamly.Internal.Data.Stream.StreamD.Top
42+import Streamly.Internal.Data.Stream.StreamD.Container
core/src/Streamly/Internal/Data/Stream/Container.hs → core/src/Streamly/Internal/Data/Stream/StreamD/Container.hsrenamed+29−28
@@ -1,5 +1,5 @@
11-- |
2−-- Module : Streamly.Internal.Data.Stream.Container
2+-- Module : Streamly.Internal.Data.Stream.StreamD.Container
33-- Copyright : (c) 2019 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
@@ -8,9 +8,7 @@
88--
99-- Stream operations that require transformers or containers like Set or Map.
1010
11−-- Rename this to Stream.Container?
12−
13−module Streamly.Internal.Data.Stream.Container
11+module Streamly.Internal.Data.Stream.StreamD.Container
1412 (
1513 nub
1614
@@ -32,20 +30,19 @@ import Control.Monad.Trans.State.Strict (get, put)
3230import Data.Function ((&))
3331import Data.Maybe (isJust)
3432import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
35−import Streamly.Internal.Data.Stream.Type (Stream)
36−import Streamly.Internal.Data.Stream.Cross (CrossStream(..))
33+import Streamly.Internal.Data.Stream.StreamD.Type
34+ (Stream(..), mkCross, unCross)
3735
3836import qualified Data.Map.Strict as Map
3937import qualified Data.Set as Set
4038import qualified Streamly.Data.Fold as Fold
4139import qualified Streamly.Internal.Data.Array.Generic as Array
4240import qualified Streamly.Internal.Data.Array.Mut.Type as MA
43−import qualified Streamly.Internal.Data.Stream.Bottom as Stream
44−import qualified Streamly.Internal.Data.Stream.Expand as Stream
45−import qualified Streamly.Internal.Data.Stream.Generate as Stream
46−import qualified Streamly.Internal.Data.Stream.Transform as Stream
47−import qualified Streamly.Internal.Data.Stream.StreamD as D
48−import qualified Streamly.Internal.Data.Stream.Transformer as Stream
41+import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
42+import qualified Streamly.Internal.Data.Stream.StreamD.Nesting as Stream
43+import qualified Streamly.Internal.Data.Stream.StreamD.Generate as Stream
44+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as Stream
45+import qualified Streamly.Internal.Data.Stream.StreamD.Transformer as Stream
4946
5047-- $setup
5148-- >>> :m
@@ -55,8 +52,8 @@ import qualified Streamly.Internal.Data.Stream.Transformer as Stream
5552-- stream. If we want to limit the memory we can just use "take" to limit the
5653-- uniq elements in the stream.
5754{-# INLINE_NORMAL nub #-}
58−nub :: (Monad m, Ord a) => D.Stream m a -> D.Stream m a
59−nub (D.Stream step1 state1) = D.Stream step (Set.empty, state1)
55+nub :: (Monad m, Ord a) => Stream m a -> Stream m a
56+nub (Stream step1 state1) = Stream step (Set.empty, state1)
6057
6158 where
6259
@@ -115,6 +112,8 @@ joinInner s1 s2 =
115112-- Ordering returning function. The time complexity would then become (m x log
116113-- n).
117114
115+-- XXX Check performance of StreamD vs StreamK
116+
118117-- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s
119118-- that are not equal to any @b@ emit @(a, Nothing)@.
120119--
@@ -133,25 +132,25 @@ joinInner s1 s2 =
133132{-# INLINE joinLeftGeneric #-}
134133joinLeftGeneric :: Monad m =>
135134 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
136−joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCrossStream $ do
137− a <- CrossStream (Stream.liftInner s1)
135+joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
136+ a <- mkCross (Stream.liftInner s1)
138137 -- XXX should we use StreamD monad here?
139138 -- XXX Is there a better way to perform some action at the end of a loop
140139 -- iteration?
141− CrossStream (Stream.fromEffect $ put False)
140+ mkCross (Stream.fromEffect $ put False)
142141 let final = Stream.concatEffect $ do
143142 r <- get
144143 if r
145144 then pure Stream.nil
146145 else pure (Stream.fromPure Nothing)
147− b <- CrossStream (fmap Just (Stream.liftInner s2) <> final)
146+ b <- mkCross (fmap Just (Stream.liftInner s2) `Stream.append` final)
148147 case b of
149148 Just b1 ->
150149 if a `eq` b1
151150 then do
152− CrossStream (Stream.fromEffect $ put True)
151+ mkCross (Stream.fromEffect $ put True)
153152 return (a, Just b1)
154− else CrossStream Stream.nil
153+ else mkCross Stream.nil
155154 Nothing -> return (a, Nothing)
156155
157156-- XXX rename to joinLeftOrd?
@@ -180,6 +179,8 @@ joinLeft s1 s2 =
180179
181180-- XXX We can do this concurrently.
182181
182+-- XXX Check performance of StreamD vs StreamK
183+
183184-- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those
184185-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
185186-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
@@ -206,7 +207,7 @@ joinOuterGeneric eq s1 s =
206207 Stream.fold
207208 (MA.writeN len)
208209 (Stream.fromList (Prelude.replicate len False))
209− return $ go inputArr foundArr <> leftOver inputArr foundArr
210+ return $ go inputArr foundArr `Stream.append` leftOver inputArr foundArr
210211
211212 where
212213
@@ -222,14 +223,14 @@ joinOuterGeneric eq s1 s =
222223 ) stream1 stream2
223224 ) & Stream.catMaybes
224225
225− evalState = Stream.evalStateT (return False) . unCrossStream
226+ evalState = Stream.evalStateT (return False) . unCross
226227
227228 go inputArr foundArr = evalState $ do
228− a <- CrossStream (Stream.liftInner s1)
229+ a <- mkCross (Stream.liftInner s1)
229230 -- XXX should we use StreamD monad here?
230231 -- XXX Is there a better way to perform some action at the end of a loop
231232 -- iteration?
232− CrossStream (Stream.fromEffect $ put False)
233+ mkCross (Stream.fromEffect $ put False)
233234 let final = Stream.concatEffect $ do
234235 r <- get
235236 if r
@@ -237,17 +238,17 @@ joinOuterGeneric eq s1 s =
237238 else pure (Stream.fromPure Nothing)
238239 (i, b) <-
239240 let stream = Array.read inputArr
240− in CrossStream
241− (Stream.indexed $ fmap Just (Stream.liftInner stream) <> final)
241+ in mkCross
242+ (Stream.indexed $ fmap Just (Stream.liftInner stream) `Stream.append` final)
242243
243244 case b of
244245 Just b1 ->
245246 if a `eq` b1
246247 then do
247− CrossStream (Stream.fromEffect $ put True)
248+ mkCross (Stream.fromEffect $ put True)
248249 MA.putIndex i foundArr True
249250 return (Just a, Just b1)
250− else CrossStream Stream.nil
251+ else mkCross Stream.nil
251252 Nothing -> return (Just a, Nothing)
252253
253254-- Put the b's that have been paired, in another hash or mutate the hash to set
core/src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs+165−12
@@ -17,7 +17,9 @@ module Streamly.Internal.Data.Stream.StreamD.Eliminate
1717
1818 -- -- * Running a 'Parser'
1919 , parse
20+ , parseD
2021 , parseBreak
22+ , parseBreakD
2123
2224 -- * Stream Deconstruction
2325 , uncons
@@ -69,33 +71,50 @@ module Streamly.Internal.Data.Stream.StreamD.Eliminate
6971 -- ** Substreams
7072 -- | These should probably be expressed using parsers.
7173 , isPrefixOf
74+ , isInfixOf
75+ , isSuffixOf
76+ , isSuffixOfUnbox
7277 , isSubsequenceOf
7378 , stripPrefix
79+ , stripSuffix
80+ , stripSuffixUnbox
7481 )
7582where
7683
7784#include "inline.hs"
7885
7986import Control.Exception (assert)
87+import Control.Monad.IO.Class (MonadIO(..))
88+import Foreign.Storable (Storable)
8089import GHC.Exts (SpecConstrAnnotation(..))
8190import GHC.Types (SPEC(..))
8291import Streamly.Internal.Data.Parser (ParseError(..))
8392import Streamly.Internal.Data.SVar.Type (defState)
93+import Streamly.Internal.Data.Unboxed (Unbox)
8494
8595import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
86−#ifdef USE_FOLDS_EVERYWHERE
96+
97+import qualified Streamly.Internal.Data.Array.Type as Array
8798import qualified Streamly.Internal.Data.Fold as Fold
88−#endif
8999import qualified Streamly.Internal.Data.Parser as PR
90100import qualified Streamly.Internal.Data.Parser.ParserD as PRD
91101import qualified Streamly.Internal.Data.Stream.StreamD.Generate as StreamD
92102import qualified Streamly.Internal.Data.Stream.StreamD.Nesting as Nesting
103+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as StreamD
93104
94105import Prelude hiding
95106 ( all, any, elem, foldr, foldr1, head, last, lookup, mapM, mapM_
96107 , maximum, minimum, notElem, null, splitAt, tail, (!!))
97108import Streamly.Internal.Data.Stream.StreamD.Type
98109
110+-- $setup
111+-- >>> :m
112+-- >>> import Streamly.Internal.Data.Stream (Stream)
113+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
114+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
115+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
116+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
117+
99118------------------------------------------------------------------------------
100119-- Elimination by Folds
101120------------------------------------------------------------------------------
@@ -138,24 +157,40 @@ splitAt n ls
138157newtype List a = List {getList :: [a]}
139158
140159-- | Run a 'Parse' over a stream.
141−{-# INLINE_NORMAL parse #-}
142−parse
160+{-# INLINE_NORMAL parseD #-}
161+parseD
143162 :: Monad m
144163 => PRD.Parser a m b
145164 -> Stream m a
146165 -> m (Either ParseError b)
147−parse parser strm = do
148− (b, _) <- parseBreak parser strm
166+parseD parser strm = do
167+ (b, _) <- parseBreakD parser strm
149168 return b
150169
170+-- | Parse a stream using the supplied 'Parser'.
171+--
172+-- Parsers (See "Streamly.Internal.Data.Parser") are more powerful folds that
173+-- add backtracking and error functionality to terminating folds. Unlike folds,
174+-- parsers may not always result in a valid output, they may result in an
175+-- error. For example:
176+--
177+-- >>> Stream.parse (Parser.takeEQ 1 Fold.drain) Stream.nil
178+-- Left (ParseError "takeEQ: Expecting exactly 1 elements, input terminated on 0")
179+--
180+-- Note: @parse p@ is not the same as @head . parseMany p@ on an empty stream.
181+--
182+{-# INLINE [3] parse #-}
183+parse :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b)
184+parse = parseD . PRD.fromParserK
185+
151186-- | Run a 'Parse' over a stream and return rest of the Stream.
152−{-# INLINE_NORMAL parseBreak #-}
153−parseBreak
187+{-# INLINE_NORMAL parseBreakD #-}
188+parseBreakD
154189 :: Monad m
155190 => PRD.Parser a m b
156191 -> Stream m a
157192 -> m (Either ParseError b, Stream m a)
158−parseBreak (PRD.Parser pstep initial extract) stream@(Stream step state) = do
193+parseBreakD (PRD.Parser pstep initial extract) stream@(Stream step state) = do
159194 res <- initial
160195 case res of
161196 PRD.IPartial s -> go SPEC state (List []) s
@@ -308,6 +343,12 @@ parseBreak (PRD.Parser pstep initial extract) stream@(Stream step state) = do
308343 PR.Error err ->
309344 return (Left (ParseError err), StreamD.nil)
310345
346+-- | Parse a stream using the supplied 'Parser'.
347+--
348+{-# INLINE parseBreak #-}
349+parseBreak :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
350+parseBreak p = parseBreakD (PRD.fromParserK p)
351+
311352------------------------------------------------------------------------------
312353-- Specialized Folds
313354------------------------------------------------------------------------------
@@ -601,8 +642,14 @@ mapM_ m = drain . mapM m
601642-- Multi-stream folds
602643------------------------------------------------------------------------------
603644
645+-- | Returns 'True' if the first stream is the same as or a prefix of the
646+-- second. A stream is a prefix of itself.
647+--
648+-- >>> Stream.isPrefixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char)
649+-- True
650+--
604651{-# INLINE_NORMAL isPrefixOf #-}
605−isPrefixOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool
652+isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
606653isPrefixOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb
607654
608655 where
@@ -624,8 +671,15 @@ isPrefixOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb
624671 Skip sb' -> go SPEC (Just' x) sa sb'
625672 Stop -> return False
626673
674+-- | Returns 'True' if all the elements of the first stream occur, in order, in
675+-- the second stream. The elements do not have to occur consecutively. A stream
676+-- is a subsequence of itself.
677+--
678+-- >>> Stream.isSubsequenceOf (Stream.fromList "hlo") (Stream.fromList "hello" :: Stream IO Char)
679+-- True
680+--
627681{-# INLINE_NORMAL isSubsequenceOf #-}
628−isSubsequenceOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool
682+isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
629683isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb
630684
631685 where
@@ -647,9 +701,16 @@ isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb
647701 Skip sb' -> go SPEC (Just' x) sa sb'
648702 Stop -> return False
649703
704+-- | @stripPrefix prefix input@ strips the @prefix@ stream from the @input@
705+-- stream if it is a prefix of input. Returns 'Nothing' if the input does not
706+-- start with the given prefix, stripped input otherwise. Returns @Just nil@
707+-- when the prefix is the same as the input stream.
708+--
709+-- Space: @O(1)@
710+--
650711{-# INLINE_NORMAL stripPrefix #-}
651712stripPrefix
652− :: (Eq a, Monad m)
713+ :: (Monad m, Eq a)
653714 => Stream m a -> Stream m a -> m (Maybe (Stream m a))
654715stripPrefix (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb
655716
@@ -671,3 +732,95 @@ stripPrefix (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb
671732 else return Nothing
672733 Skip sb' -> go SPEC (Just' x) sa sb'
673734 Stop -> return Nothing
735+
736+-- | Returns 'True' if the first stream is an infix of the second. A stream is
737+-- considered an infix of itself.
738+--
739+-- >>> s = Stream.fromList "hello" :: Stream IO Char
740+-- >>> Stream.isInfixOf s s
741+-- True
742+--
743+-- Space: @O(n)@ worst case where @n@ is the length of the infix.
744+--
745+-- /Pre-release/
746+--
747+-- /Requires 'Storable' constraint/
748+--
749+{-# INLINE isInfixOf #-}
750+isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a, Unbox a)
751+ => Stream m a -> Stream m a -> m Bool
752+isInfixOf infx stream = do
753+ arr <- fold Array.write infx
754+ -- XXX can use breakOnSeq instead (when available)
755+ r <- null $ StreamD.drop 1 $ Nesting.splitOnSeq arr Fold.drain stream
756+ return (not r)
757+
758+-- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf
759+-- may use the suffix stream many times. To run in optimal memory we do not
760+-- want to buffer the suffix stream in memory therefore we need an ability to
761+-- clone (or consume it multiple times) the suffix stream without any side
762+-- effects so that multiple potential suffix matches can proceed in parallel
763+-- without buffering the suffix stream. For example, we may create the suffix
764+-- stream from a file handle, however, if we evaluate the stream multiple
765+-- times, once for each match, we will need a different file handle each time
766+-- which may exhaust the file descriptors. Instead, we want to share the same
767+-- underlying file descriptor, use pread on it to generate the stream and clone
768+-- the stream for each match. Therefore the suffix stream should be built in
769+-- such a way that it can be consumed multiple times without any problems.
770+
771+-- XXX Can be implemented with better space/time complexity.
772+-- Space: @O(n)@ worst case where @n@ is the length of the suffix.
773+
774+-- | Returns 'True' if the first stream is a suffix of the second. A stream is
775+-- considered a suffix of itself.
776+--
777+-- >>> Stream.isSuffixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char)
778+-- True
779+--
780+-- Space: @O(n)@, buffers entire input stream and the suffix.
781+--
782+-- /Pre-release/
783+--
784+-- /Suboptimal/ - Help wanted.
785+--
786+{-# INLINE isSuffixOf #-}
787+isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
788+isSuffixOf suffix stream =
789+ StreamD.reverse suffix `isPrefixOf` StreamD.reverse stream
790+
791+-- | Much faster than 'isSuffixOf'.
792+{-# INLINE isSuffixOfUnbox #-}
793+isSuffixOfUnbox :: (MonadIO m, Eq a, Unbox a) =>
794+ Stream m a -> Stream m a -> m Bool
795+isSuffixOfUnbox suffix stream =
796+ StreamD.reverseUnbox suffix `isPrefixOf` StreamD.reverseUnbox stream
797+
798+-- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does
799+-- not end with the given suffix. Returns @Just nil@ when the suffix is the
800+-- same as the stream.
801+--
802+-- It may be more efficient to convert the stream to an Array and use
803+-- stripSuffix on that especially if the elements have a Storable or Prim
804+-- instance.
805+--
806+-- See also "Streamly.Internal.Data.Stream.Reduce.dropSuffix".
807+--
808+-- Space: @O(n)@, buffers the entire input stream as well as the suffix
809+--
810+-- /Pre-release/
811+{-# INLINE stripSuffix #-}
812+stripSuffix
813+ :: (Monad m, Eq a)
814+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
815+stripSuffix m1 m2 =
816+ fmap StreamD.reverse
817+ <$> stripPrefix (StreamD.reverse m1) (StreamD.reverse m2)
818+
819+-- | Much faster than 'stripSuffix'.
820+{-# INLINE stripSuffixUnbox #-}
821+stripSuffixUnbox
822+ :: (MonadIO m, Eq a, Unbox a)
823+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
824+stripSuffixUnbox m1 m2 =
825+ fmap StreamD.reverseUnbox
826+ <$> stripPrefix (StreamD.reverseUnbox m1) (StreamD.reverseUnbox m2)
core/src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+147−35
@@ -11,13 +11,14 @@ module Streamly.Internal.Data.Stream.StreamD.Exception
1111 gbracket_
1212 , gbracket
1313 , before
14− , after_
15− , after
16− , bracket_
17− , bracket'
14+ , afterUnsafe
15+ , afterIO
16+ , bracketUnsafe
17+ , bracketIO3
18+ , bracketIO
1819 , onException
19− , finally_
20− , finally
20+ , finallyUnsafe
21+ , finallyIO
2122 , ghandle
2223 , handle
2324 )
@@ -36,6 +37,10 @@ import qualified Control.Monad.Catch as MC
3637
3738import Streamly.Internal.Data.Stream.StreamD.Type
3839
40+-- $setup
41+-- >>> :m
42+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
43+
3944data GbracketState s1 s2 v
4045 = GBracketInit
4146 | GBracketNormal s1 v
@@ -169,7 +174,12 @@ gbracket bef aft onExc onGC ftry action =
169174 Skip s -> return $ Skip (GBracketIOException (Stream step1 s))
170175 Stop -> return Stop
171176
172−-- | See 'Streamly.Internal.Data.Stream.before'.
177+-- | Run the action @m b@ before the stream yields its first element.
178+--
179+-- Same as the following but more efficient due to fusion:
180+--
181+-- >>> before action xs = Stream.nilM action <> xs
182+-- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)
173183--
174184{-# INLINE_NORMAL before #-}
175185before :: Monad m => m b -> Stream m a -> Stream m a
@@ -187,11 +197,22 @@ before action (Stream step state) = Stream step' Nothing
187197 Skip s -> return $ Skip (Just s)
188198 Stop -> return Stop
189199
190−-- | See 'Streamly.Internal.Data.Stream.after_'.
200+-- | Like 'after', with following differences:
201+--
202+-- * action @m b@ won't run if the stream is garbage collected
203+-- after partial evaluation.
204+-- * Monad @m@ does not require any other constraints.
205+-- * has slightly better performance than 'after'.
206+--
207+-- Same as the following, but with stream fusion:
208+--
209+-- >>> afterUnsafe action xs = xs <> Stream.nilM action
191210--
192−{-# INLINE_NORMAL after_ #-}
193−after_ :: Monad m => m b -> Stream m a -> Stream m a
194−after_ action (Stream step state) = Stream step' state
211+-- /Pre-release/
212+--
213+{-# INLINE_NORMAL afterUnsafe #-}
214+afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a
215+afterUnsafe action (Stream step state) = Stream step' state
195216
196217 where
197218
@@ -203,12 +224,18 @@ after_ action (Stream step state) = Stream step' state
203224 Skip s -> return $ Skip s
204225 Stop -> action >> return Stop
205226
206−-- | See 'Streamly.Internal.Data.Stream.after'.
227+-- | Run the action @IO b@ whenever the stream is evaluated to completion, or
228+-- if it is garbage collected after a partial lazy evaluation.
229+--
230+-- The semantics of the action @IO b@ are similar to the semantics of cleanup
231+-- action in 'bracketIO'.
207232--
208−{-# INLINE_NORMAL after #-}
209−after :: MonadIO m
233+-- /See also 'afterUnsafe'/
234+--
235+{-# INLINE_NORMAL afterIO #-}
236+afterIO :: MonadIO m
210237 => IO b -> Stream m a -> Stream m a
211−after action (Stream step state) = Stream step' Nothing
238+afterIO action (Stream step state) = Stream step' Nothing
212239
213240 where
214241
@@ -227,8 +254,11 @@ after action (Stream step state) = Stream step' Nothing
227254
228255-- XXX For high performance error checks in busy streams we may need another
229256-- Error constructor in step.
257+
258+-- | Run the action @m b@ if the stream evaluation is aborted due to an
259+-- exception. The exception is not caught, simply rethrown.
230260--
231−-- | See 'Streamly.Internal.Data.Stream.onException'.
261+-- /Inhibits stream fusion/
232262--
233263{-# INLINE_NORMAL onException #-}
234264onException :: MonadCatch m => m b -> Stream m a -> Stream m a
@@ -254,29 +284,55 @@ _onException action (Stream step state) = Stream step' state
254284 Skip s -> return $ Skip s
255285 Stop -> return Stop
256286
257−-- | See 'Streamly.Internal.Data.Stream.bracket_'.
287+-- | Like 'bracket' but with following differences:
258288--
259−{-# INLINE_NORMAL bracket_ #-}
260−bracket_ :: MonadCatch m
289+-- * alloc action @m b@ runs with async exceptions enabled
290+-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
291+-- after partial evaluation.
292+-- * has slightly better performance than 'bracketIO'.
293+--
294+-- /Inhibits stream fusion/
295+--
296+-- /Pre-release/
297+--
298+{-# INLINE_NORMAL bracketUnsafe #-}
299+bracketUnsafe :: MonadCatch m
261300 => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
262−bracket_ bef aft =
301+bracketUnsafe bef aft =
263302 gbracket_
264303 bef
265304 aft
266305 (\a (e :: SomeException) _ -> nilM (aft a >> MC.throwM e))
267306 (inline MC.try)
268307
269−-- | See 'Streamly.Internal.Data.Stream.bracket'.
308+-- For a use case of this see the "streamly-process" package. It needs to kill
309+-- the process in case of exception or garbage collection, but waits for the
310+-- process to terminate in normal cases.
311+
312+-- | Like 'bracketIO' but can use 3 separate cleanup actions depending on the
313+-- mode of termination:
270314--
271−{-# INLINE_NORMAL bracket' #-}
272−bracket' :: (MonadIO m, MonadCatch m) =>
315+-- 1. When the stream stops normally
316+-- 2. When the stream is garbage collected
317+-- 3. When the stream encounters an exception
318+--
319+-- @bracketIO3 before onStop onGC onException action@ runs @action@ using the
320+-- result of @before@. If the stream stops, @onStop@ action is executed, if the
321+-- stream is abandoned @onGC@ is executed, if the stream encounters an
322+-- exception @onException@ is executed.
323+--
324+-- /Inhibits stream fusion/
325+--
326+-- /Pre-release/
327+{-# INLINE_NORMAL bracketIO3 #-}
328+bracketIO3 :: (MonadIO m, MonadCatch m) =>
273329 IO b
274330 -> (b -> IO c)
275331 -> (b -> IO d)
276332 -> (b -> IO e)
277333 -> (b -> Stream m a)
278334 -> Stream m a
279−bracket' bef aft onExc onGC =
335+bracketIO3 bef aft onExc onGC =
280336 gbracket
281337 bef
282338 aft
@@ -284,6 +340,32 @@ bracket' bef aft onExc onGC =
284340 onGC
285341 (inline MC.try)
286342
343+-- | Run the alloc action @IO b@ with async exceptions disabled but keeping
344+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
345+-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
346+--
347+-- @b@ is usually a resource under the IO monad, e.g. a file handle, that
348+-- requires a cleanup after use. The cleanup action @b -> IO c@, runs whenever
349+-- the stream ends normally, due to a sync or async exception or if it gets
350+-- garbage collected after a partial lazy evaluation.
351+--
352+-- 'bracketIO' only guarantees that the cleanup action runs, and it runs with
353+-- async exceptions enabled. The action must ensure that it can successfully
354+-- cleanup the resource in the face of sync or async exceptions.
355+--
356+-- When the stream ends normally or on a sync exception, cleanup action runs
357+-- immediately in the current thread context, whereas in other cases it runs in
358+-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
359+--
360+-- /See also: 'bracketUnsafe'/
361+--
362+-- /Inhibits stream fusion/
363+--
364+{-# INLINE bracketIO #-}
365+bracketIO :: (MonadIO m, MonadCatch m)
366+ => IO b -> (b -> IO c) -> (b -> Stream m a) -> Stream m a
367+bracketIO bef aft = bracketIO3 bef aft aft aft
368+
287369data BracketState s v = BracketInit | BracketRun s v
288370
289371-- | Alternate (custom) implementation of 'bracket'.
@@ -310,22 +392,49 @@ _bracket bef aft bet = Stream step' BracketInit
310392 Skip s -> return $ Skip (BracketRun (Stream step s) v)
311393 Stop -> aft v >> return Stop
312394
313−-- | See 'Streamly.Internal.Data.Stream.finally_'.
395+-- | Like 'finally' with following differences:
396+--
397+-- * action @m b@ won't run if the stream is garbage collected
398+-- after partial evaluation.
399+-- * has slightly better performance than 'finallyIO'.
314400--
315−{-# INLINE finally_ #-}
316−finally_ :: MonadCatch m => m b -> Stream m a -> Stream m a
317−finally_ action xs = bracket_ (return ()) (const action) (const xs)
401+-- /Inhibits stream fusion/
402+--
403+-- /Pre-release/
404+--
405+{-# INLINE finallyUnsafe #-}
406+finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a
407+finallyUnsafe action xs = bracketUnsafe (return ()) (const action) (const xs)
318408
319−-- | See 'Streamly.Internal.Data.Stream.finally'.
409+-- | Run the action @IO b@ whenever the stream stream stops normally, aborts
410+-- due to an exception or if it is garbage collected after a partial lazy
411+-- evaluation.
412+--
413+-- The semantics of running the action @IO b@ are similar to the cleanup action
414+-- semantics described in 'bracketIO'.
415+--
416+-- >>> finallyIO release = Stream.bracketIO (return ()) (const release)
320417--
321−-- finally action xs = after action $ onException action xs
418+-- /See also 'finallyUnsafe'/
322419--
323−{-# INLINE finally #-}
324−finally :: (MonadIO m, MonadCatch m) => IO b -> Stream m a -> Stream m a
325−finally action xs = bracket' (return ()) act act act (const xs)
420+-- /Inhibits stream fusion/
421+--
422+{-# INLINE finallyIO #-}
423+finallyIO :: (MonadIO m, MonadCatch m) => IO b -> Stream m a -> Stream m a
424+finallyIO action xs = bracketIO3 (return ()) act act act (const xs)
326425 where act _ = action
327426
328−-- | See 'Streamly.Internal.Data.Stream.ghandle'.
427+-- | Like 'handle' but the exception handler is also provided with the stream
428+-- that generated the exception as input. The exception handler can thus
429+-- re-evaluate the stream to retry the action that failed. The exception
430+-- handler can again call 'ghandle' on it to retry the action multiple times.
431+--
432+-- This is highly experimental. In a stream of actions we can map the stream
433+-- with a retry combinator to retry each action on failure.
434+--
435+-- /Inhibits stream fusion/
436+--
437+-- /Pre-release/
329438--
330439{-# INLINE_NORMAL ghandle #-}
331440ghandle :: (MonadCatch m, Exception e)
@@ -333,7 +442,10 @@ ghandle :: (MonadCatch m, Exception e)
333442ghandle f stream =
334443 gbracket_ (return ()) return (const f) (inline MC.try) (const stream)
335444
336−-- | See 'Streamly.Internal.Data.Stream.handle'.
445+-- | When evaluating a stream if an exception occurs, stream evaluation aborts
446+-- and the specified exception handler is run with the exception as argument.
447+--
448+-- /Inhibits stream fusion/
337449--
338450{-# INLINE_NORMAL handle #-}
339451handle :: (MonadCatch m, Exception e)
core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+771−42
@@ -7,17 +7,6 @@
77-- Stability : experimental
88-- Portability : GHC
99--
10−-- Prefer unfolds ("Streamly.Internal.Data.Unfold") over the combinators in
11−-- this module. They are more powerful and efficient as they can be transformed
12−-- and composed on the input side efficiently and they can fuse in nested
13−-- operations (e.g. unfoldMany). All the combinators in this module can be
14−-- expressed using unfolds with the same efficiency.
15−--
16−-- Operations in this module that are not in "Streamly.Internal.Data.Unfold":
17−-- generate, times, fromPrimIORef.
18−--
19−-- We should plan to replace this module with "Streamly.Internal.Data.Unfold"
20−-- in future.
2110
2211-- A few combinators in this module have been adapted from the vector package
2312-- (c) Roman Leshchinskiy. See the notes in specific combinators.
@@ -46,20 +35,50 @@ module Streamly.Internal.Data.Stream.StreamD.Generate
4635 , replicateM
4736
4837 -- * Enumeration
49− , enumerateFromStepIntegral
38+ -- ** Enumerating 'Num' Types
39+ , enumerateFromStepNum
40+ , enumerateFromNum
41+ , enumerateFromThenNum
42+
43+ -- ** Enumerating 'Bounded' 'Enum' Types
44+ , enumerate
45+ , enumerateTo
46+ , enumerateFromBounded
47+
48+ -- ** Enumerating 'Enum' Types not larger than 'Int'
49+ , enumerateFromToSmall
50+ , enumerateFromThenToSmall
51+ , enumerateFromThenSmallBounded
52+
53+ -- ** Enumerating 'Bounded' 'Integral' Types
5054 , enumerateFromIntegral
5155 , enumerateFromThenIntegral
56+
57+ -- ** Enumerating 'Integral' Types
5258 , enumerateFromToIntegral
5359 , enumerateFromThenToIntegral
5460
55− , enumerateFromStepNum
56− , enumerateFromNum
57− , enumerateFromThenNum
61+ -- ** Enumerating unbounded 'Integral' Types
62+ , enumerateFromStepIntegral
63+
64+ -- ** Enumerating 'Fractional' Types
65+ , enumerateFromFractional
5866 , enumerateFromToFractional
67+ , enumerateFromThenFractional
5968 , enumerateFromThenToFractional
6069
70+ -- ** Enumerable Type Class
71+ , Enumerable(..)
72+
6173 -- * Time Enumeration
6274 , times
75+ , timesWith
76+ , absTimes
77+ , absTimesWith
78+ , relTimes
79+ , relTimesWith
80+ , durations
81+ , timeout
6382
6483 -- * From Generators
6584 -- | Generate a monadic stream from a seed.
@@ -75,12 +94,15 @@ module Streamly.Internal.Data.Stream.StreamD.Generate
7594 -- * From Containers
7695 -- | Transform an input structure into a stream.
7796
78− -- Note: Direct style stream does not support @fromFoldable@.
7997 , fromList
8098 , fromListM
99+ , fromFoldable
100+ , fromFoldableM
81101
82102 -- * From Pointers
83103 , fromPtr
104+ , fromPtrN
105+ , fromByteStr#
84106
85107 -- * Conversions
86108 , fromStreamK
@@ -92,53 +114,108 @@ where
92114#include "ArrayMacros.h"
93115
94116import Control.Monad.IO.Class (MonadIO(..))
117+import Data.Functor.Identity (Identity(..))
95118import Foreign.Ptr (Ptr, plusPtr)
96119import Foreign.Storable (Storable (peek), sizeOf)
120+import GHC.Exts (Addr#, Ptr (Ptr))
97121import Streamly.Internal.Data.Time.Clock
98122 (Clock(Monotonic), asyncClock, readClock)
99123import Streamly.Internal.Data.Time.Units
100− (toAbsTime, AbsTime, toRelTime64, RelTime64)
124+ (toAbsTime, AbsTime, toRelTime64, RelTime64, addToAbsTime64)
101125
102126#ifdef USE_UNFOLDS_EVERYWHERE
103127import qualified Streamly.Internal.Data.Unfold as Unfold
104128import qualified Streamly.Internal.Data.Unfold.Enumeration as Unfold
105129#endif
106130
107−import Prelude hiding (iterate, repeat, replicate, takeWhile)
131+import Data.Fixed
132+import Data.Int
133+import Data.Ratio
134+import Data.Word
135+import Numeric.Natural
136+import Prelude hiding (iterate, repeat, replicate, take, takeWhile)
108137import Streamly.Internal.Data.Stream.StreamD.Type
109138
139+-- $setup
140+-- >>> :m
141+-- >>> import Control.Concurrent (threadDelay)
142+-- >>> import Data.Function (fix, (&))
143+-- >>> import Data.Semigroup (cycle1)
144+-- >>> import qualified Streamly.Data.Fold as Fold
145+-- >>> import qualified Streamly.Data.Unfold as Unfold
146+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
147+-- >>> import GHC.Exts (Ptr (Ptr))
148+
110149------------------------------------------------------------------------------
111150-- Primitives
112151------------------------------------------------------------------------------
113152
114−-- | An empty 'Stream'.
153+-- XXX implement in terms of nilM?
154+
155+-- | A stream that terminates without producing any output or side effect.
156+--
157+-- >>> Stream.fold Fold.toList Stream.nil
158+-- []
159+--
115160{-# INLINE_NORMAL nil #-}
116−nil :: Monad m => Stream m a
117−nil = Stream (\_ _ -> return Stop) ()
161+nil :: Applicative m => Stream m a
162+nil = Stream (\_ _ -> pure Stop) ()
118163
119164-- XXX implement in terms of consM?
120165-- cons x = consM (return x)
166+
167+-- | Fuse a pure value at the head of an existing stream::
168+--
169+-- >>> s = 1 `Stream.cons` Stream.fromList [2,3]
170+-- >>> Stream.fold Fold.toList s
171+-- [1,2,3]
172+--
173+-- This function should not be used to dynamically construct a stream. If a
174+-- stream is constructed by successive use of this function it would take
175+-- O(n^2) time to consume the stream.
176+--
177+-- This function should only be used to statically fuse an element with a
178+-- stream. Do not use this recursively or where it cannot be inlined.
179+--
180+-- See "Streamly.Data.Stream.StreamK" for a 'cons' that can be used to
181+-- construct a stream recursively.
182+--
183+-- Definition:
184+--
185+-- >>> cons x xs = return x `Stream.consM` xs
121186--
122−-- | Can fuse but has O(n^2) complexity.
123187{-# INLINE_NORMAL cons #-}
124−cons :: Monad m => a -> Stream m a -> Stream m a
188+cons :: Applicative m => a -> Stream m a -> Stream m a
125189cons x (Stream step state) = Stream step1 Nothing
126190 where
127191 {-# INLINE_LATE step1 #-}
128− step1 _ Nothing = return $ Yield x (Just state)
192+ step1 _ Nothing = pure $ Yield x (Just state)
129193 step1 gst (Just st) = do
130− r <- step gst st
131− return $
132− case r of
194+ (\case
133195 Yield a s -> Yield a (Just s)
134196 Skip s -> Skip (Just s)
135− Stop -> Stop
197+ Stop -> Stop) <$> step gst st
136198
137199------------------------------------------------------------------------------
138200-- Unfolding
139201------------------------------------------------------------------------------
140202
141203-- Adapted from vector package
204+
205+-- | Build a stream by unfolding a /monadic/ step function starting from a
206+-- seed. The step function returns the next element in the stream and the next
207+-- seed value. When it is done it returns 'Nothing' and the stream ends. For
208+-- example,
209+--
210+-- >>> :{
211+-- let f b =
212+-- if b > 2
213+-- then return Nothing
214+-- else return (Just (b, b + 1))
215+-- in Stream.fold Fold.toList $ Stream.unfoldrM f 0
216+-- :}
217+-- [0,1,2]
218+--
142219{-# INLINE_NORMAL unfoldrM #-}
143220unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a
144221#ifdef USE_UNFOLDS_EVERYWHERE
@@ -154,6 +231,28 @@ unfoldrM next = Stream step
154231 Nothing -> Stop
155232#endif
156233
234+-- |
235+-- >>> :{
236+-- unfoldr step s =
237+-- case step s of
238+-- Nothing -> Stream.nil
239+-- Just (a, b) -> a `Stream.cons` unfoldr step b
240+-- :}
241+--
242+-- Build a stream by unfolding a /pure/ step function @step@ starting from a
243+-- seed @s@. The step function returns the next element in the stream and the
244+-- next seed value. When it is done it returns 'Nothing' and the stream ends.
245+-- For example,
246+--
247+-- >>> :{
248+-- let f b =
249+-- if b > 2
250+-- then Nothing
251+-- else Just (b, b + 1)
252+-- in Stream.fold Fold.toList $ Stream.unfoldr f 0
253+-- :}
254+-- [0,1,2]
255+--
157256{-# INLINE_LATE unfoldr #-}
158257unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a
159258unfoldr f = unfoldrM (return . f)
@@ -162,6 +261,20 @@ unfoldr f = unfoldrM (return . f)
162261-- From values
163262------------------------------------------------------------------------------
164263
264+-- |
265+-- >>> repeatM = Stream.sequence . Stream.repeat
266+-- >>> repeatM = fix . Stream.consM
267+-- >>> repeatM = cycle1 . Stream.fromEffect
268+--
269+-- Generate a stream by repeatedly executing a monadic action forever.
270+--
271+-- >>> :{
272+-- repeatAction =
273+-- Stream.repeatM (threadDelay 1000000 >> print 1)
274+-- & Stream.take 10
275+-- & Stream.fold Fold.drain
276+-- :}
277+--
165278{-# INLINE_NORMAL repeatM #-}
166279repeatM :: Monad m => m a -> Stream m a
167280#ifdef USE_UNFOLDS_EVERYWHERE
@@ -170,6 +283,11 @@ repeatM = unfold Unfold.repeatM
170283repeatM x = Stream (\_ _ -> x >>= \r -> return $ Yield r ()) ()
171284#endif
172285
286+-- |
287+-- Generate an infinite stream by repeating a pure value.
288+--
289+-- >>> repeat x = Stream.repeatM (pure x)
290+--
173291{-# INLINE_NORMAL repeat #-}
174292repeat :: Monad m => a -> Stream m a
175293#ifdef USE_UNFOLDS_EVERYWHERE
@@ -179,8 +297,13 @@ repeat x = Stream (\_ _ -> return $ Yield x ()) ()
179297#endif
180298
181299-- Adapted from the vector package
300+
301+-- |
302+-- >>> replicateM n = Stream.sequence . Stream.replicate n
303+--
304+-- Generate a stream by performing a monadic action @n@ times.
182305{-# INLINE_NORMAL replicateM #-}
183−replicateM :: forall m a. Monad m => Int -> m a -> Stream m a
306+replicateM :: Monad m => Int -> m a -> Stream m a
184307#ifdef USE_UNFOLDS_EVERYWHERE
185308replicateM n p = unfold Unfold.replicateM (n, p)
186309#else
@@ -194,6 +317,12 @@ replicateM n p = Stream step n
194317 return $ Yield x (i - 1)
195318#endif
196319
320+-- |
321+-- >>> replicate n = Stream.take n . Stream.repeat
322+-- >>> replicate n x = Stream.replicateM n (pure x)
323+--
324+-- Generate a stream of length @n@ by repeating a value @n@ times.
325+--
197326{-# INLINE_NORMAL replicate #-}
198327replicate :: Monad m => Int -> a -> Stream m a
199328replicate n x = replicateM n (return x)
@@ -291,8 +420,19 @@ enumerateFromThenToIntegralDn from next to = Stream step EnumInit
291420#endif
292421
293422-- XXX This can perhaps be simplified and written in terms of
294−-- enumeratFromStepIntegral as we have done in unfolds. But anyway we should be
295−-- replacing the stream generation module with unfolds.
423+-- enumeratFromStepIntegral as we have done in unfolds.
424+
425+-- | Enumerate an 'Integral' type in steps up to a given limit.
426+-- @enumerateFromThenToIntegral from then to@ generates a finite stream whose
427+-- first element is @from@, the second element is @then@ and the successive
428+-- elements are in increments of @then - from@ up to @to@.
429+--
430+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 2 6
431+-- [0,2,4,6]
432+--
433+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)
434+-- [0,-2,-4,-6]
435+--
296436{-# INLINE_NORMAL enumerateFromThenToIntegral #-}
297437enumerateFromThenToIntegral
298438 :: (Monad m, Integral a)
@@ -306,6 +446,17 @@ enumerateFromThenToIntegral from next to
306446 | otherwise = enumerateFromThenToIntegralDn from next to
307447#endif
308448
449+-- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from
450+-- then@ generates a stream whose first element is @from@, the second element
451+-- is @then@ and the successive elements are in increments of @then - from@.
452+-- The stream is bounded by the size of the 'Integral' type.
453+--
454+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2
455+-- [0,2,4,6]
456+--
457+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)
458+-- [0,-2,-4,-6]
459+--
309460{-# INLINE_NORMAL enumerateFromThenIntegral #-}
310461enumerateFromThenIntegral
311462 :: (Monad m, Integral a, Bounded a)
@@ -320,8 +471,18 @@ enumerateFromThenIntegral from next =
320471 else enumerateFromThenToIntegralDn from next minBound
321472#endif
322473
323−-- | Can be used to enumerate unbounded integrals. This does not check for
324−-- overflow or underflow for bounded integrals.
474+-- | @enumerateFromStepIntegral from step@ generates an infinite stream whose
475+-- first element is @from@ and the successive elements are in increments of
476+-- @step@.
477+--
478+-- CAUTION: This function is not safe for finite integral types. It does not
479+-- check for overflow, underflow or bounds.
480+--
481+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2
482+-- [0,2,4,6]
483+--
484+-- >>> Stream.fold Fold.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)
485+-- [0,-2,-4]
325486--
326487{-# INLINE_NORMAL enumerateFromStepIntegral #-}
327488enumerateFromStepIntegral :: (Integral a, Monad m) => a -> a -> Stream m a
@@ -336,13 +497,26 @@ enumerateFromStepIntegral from stride =
336497 step _ !x = return $ Yield x $! (x + stride)
337498#endif
338499
339−-- | Enumerate upwards from @from@ to @to@. We are assuming that "to" is
340−-- constrained by the type to be within max/min bounds.
500+-- | Enumerate an 'Integral' type up to a given limit.
501+-- @enumerateFromToIntegral from to@ generates a finite stream whose first
502+-- element is @from@ and successive elements are in increments of @1@ up to
503+-- @to@.
504+--
505+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToIntegral 0 4
506+-- [0,1,2,3,4]
507+--
341508{-# INLINE enumerateFromToIntegral #-}
342509enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a
343510enumerateFromToIntegral from to =
344511 takeWhile (<= to) $ enumerateFromStepIntegral from 1
345512
513+-- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a
514+-- stream whose first element is @from@ and the successive elements are in
515+-- increments of @1@. The stream is bounded by the size of the 'Integral' type.
516+--
517+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)
518+-- [0,1,2,3]
519+--
346520{-# INLINE enumerateFromIntegral #-}
347521enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => a -> Stream m a
348522enumerateFromIntegral from = enumerateFromToIntegral from maxBound
@@ -351,9 +525,67 @@ enumerateFromIntegral from = enumerateFromToIntegral from maxBound
351525-- Enumeration of Fractionals
352526------------------------------------------------------------------------------
353527
354−-- | We cannot write a general function for Num. The only way to write code
528+-- We cannot write a general function for Num. The only way to write code
355529-- portable between the two is to use a 'Real' constraint and convert between
356530-- Fractional and Integral using fromRational which is horribly slow.
531+
532+-- Even though the underlying implementation of enumerateFromFractional and
533+-- enumerateFromThenFractional works for any 'Num' we have restricted these to
534+-- 'Fractional' because these do not perform any bounds check, in contrast to
535+-- integral versions and are therefore not equivalent substitutes for those.
536+
537+-- | Numerically stable enumeration from a 'Fractional' number in steps of size
538+-- @1@. @enumerateFromFractional from@ generates a stream whose first element
539+-- is @from@ and the successive elements are in increments of @1@. No overflow
540+-- or underflow checks are performed.
541+--
542+-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
543+--
544+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1
545+-- [1.1,2.1,3.1,4.1]
546+--
547+{-# INLINE enumerateFromFractional #-}
548+enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a
549+enumerateFromFractional = enumerateFromNum
550+
551+-- | Numerically stable enumeration from a 'Fractional' number in steps.
552+-- @enumerateFromThenFractional from then@ generates a stream whose first
553+-- element is @from@, the second element is @then@ and the successive elements
554+-- are in increments of @then - from@. No overflow or underflow checks are
555+-- performed.
556+--
557+-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
558+-- example:
559+--
560+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1
561+-- [1.1,2.1,3.1,4.1]
562+--
563+-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)
564+-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
565+--
566+{-# INLINE enumerateFromThenFractional #-}
567+enumerateFromThenFractional
568+ :: (Monad m, Fractional a)
569+ => a -> a -> Stream m a
570+enumerateFromThenFractional = enumerateFromThenNum
571+
572+-- | Numerically stable enumeration from a 'Fractional' number to a given
573+-- limit. @enumerateFromToFractional from to@ generates a finite stream whose
574+-- first element is @from@ and successive elements are in increments of @1@ up
575+-- to @to@.
576+--
577+-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
578+-- example:
579+--
580+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4
581+-- [1.1,2.1,3.1,4.1]
582+--
583+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4.6
584+-- [1.1,2.1,3.1,4.1,5.1]
585+--
586+-- Notice that the last element is equal to the specified @to@ value after
587+-- rounding to the nearest integer.
588+--
357589{-# INLINE_NORMAL enumerateFromToFractional #-}
358590enumerateFromToFractional
359591 :: (Monad m, Fractional a, Ord a)
@@ -361,24 +593,320 @@ enumerateFromToFractional
361593enumerateFromToFractional from to =
362594 takeWhile (<= to + 1 / 2) $ enumerateFromStepNum from 1
363595
596+-- | Numerically stable enumeration from a 'Fractional' number in steps up to a
597+-- given limit. @enumerateFromThenToFractional from then to@ generates a
598+-- finite stream whose first element is @from@, the second element is @then@
599+-- and the successive elements are in increments of @then - from@ up to @to@.
600+--
601+-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
602+-- example:
603+--
604+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 2 6
605+-- [0.1,2.0,3.9,5.799999999999999]
606+--
607+-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)
608+-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
609+--
364610{-# INLINE_NORMAL enumerateFromThenToFractional #-}
365611enumerateFromThenToFractional
366612 :: (Monad m, Fractional a, Ord a)
367613 => a -> a -> a -> Stream m a
368614enumerateFromThenToFractional from next to =
369− takeWhile predicate $ enumerateFromThenNum from next
615+ takeWhile predicate $ enumerateFromThenFractional from next
370616 where
371617 mid = (next - from) / 2
372618 predicate | next >= from = (<= to + mid)
373619 | otherwise = (>= to + mid)
374620
621+-------------------------------------------------------------------------------
622+-- Enumeration of Enum types not larger than Int
623+-------------------------------------------------------------------------------
624+--
625+-- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.
626+--
627+{-# INLINE enumerateFromToSmall #-}
628+enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a
629+enumerateFromToSmall from to =
630+ fmap toEnum
631+ $ enumerateFromToIntegral (fromEnum from) (fromEnum to)
632+
633+-- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.
634+--
635+{-# INLINE enumerateFromThenToSmall #-}
636+enumerateFromThenToSmall :: (Monad m, Enum a)
637+ => a -> a -> a -> Stream m a
638+enumerateFromThenToSmall from next to =
639+ fmap toEnum
640+ $ enumerateFromThenToIntegral
641+ (fromEnum from) (fromEnum next) (fromEnum to)
642+
643+-- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.
644+--
645+-- Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a
646+-- type is bounded but does not have a 'Bounded' instance then we can go on
647+-- enumerating it beyond the legal values of the type, resulting in the failure
648+-- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'
649+-- instance for this function to be safely used.
650+--
651+{-# INLINE enumerateFromThenSmallBounded #-}
652+enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a)
653+ => a -> a -> Stream m a
654+enumerateFromThenSmallBounded from next =
655+ if fromEnum next >= fromEnum from
656+ then enumerateFromThenTo from next maxBound
657+ else enumerateFromThenTo from next minBound
658+
659+-------------------------------------------------------------------------------
660+-- Enumerable type class
661+-------------------------------------------------------------------------------
662+--
663+-- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream
664+-- enumerations like this:
665+--
666+-- {-# RULES "fromList enumFrom" [1]
667+-- forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}
668+--
669+-- But this does not work because enumFrom is a class method and GHC rewrites
670+-- it quickly, so we do not get a chance to have our rule fired.
671+
672+-- | Types that can be enumerated as a stream. The operations in this type
673+-- class are equivalent to those in the 'Enum' type class, except that these
674+-- generate a stream instead of a list. Use the functions in
675+-- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.
676+--
677+class Enum a => Enumerable a where
678+ -- | @enumerateFrom from@ generates a stream starting with the element
679+ -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
680+ -- generating an infinite stream when the type is not 'Bounded'.
681+ --
682+ -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)
683+ -- [0,1,2,3]
684+ --
685+ -- For 'Fractional' types, enumeration is numerically stable. However, no
686+ -- overflow or underflow checks are performed.
687+ --
688+ -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1
689+ -- [1.1,2.1,3.1,4.1]
690+ --
691+ enumerateFrom :: (Monad m) => a -> Stream m a
692+
693+ -- | Generate a finite stream starting with the element @from@, enumerating
694+ -- the type up to the value @to@. If @to@ is smaller than @from@ then an
695+ -- empty stream is returned.
696+ --
697+ -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 0 4
698+ -- [0,1,2,3,4]
699+ --
700+ -- For 'Fractional' types, the last element is equal to the specified @to@
701+ -- value after rounding to the nearest integral value.
702+ --
703+ -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4
704+ -- [1.1,2.1,3.1,4.1]
705+ --
706+ -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4.6
707+ -- [1.1,2.1,3.1,4.1,5.1]
708+ --
709+ enumerateFromTo :: (Monad m) => a -> a -> Stream m a
710+
711+ -- | @enumerateFromThen from then@ generates a stream whose first element
712+ -- is @from@, the second element is @then@ and the successive elements are
713+ -- in increments of @then - from@. Enumeration can occur downwards or
714+ -- upwards depending on whether @then@ comes before or after @from@. For
715+ -- 'Bounded' types the stream ends when 'maxBound' is reached, for
716+ -- unbounded types it keeps enumerating infinitely.
717+ --
718+ -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2
719+ -- [0,2,4,6]
720+ --
721+ -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)
722+ -- [0,-2,-4,-6]
723+ --
724+ enumerateFromThen :: (Monad m) => a -> a -> Stream m a
725+
726+ -- | @enumerateFromThenTo from then to@ generates a finite stream whose
727+ -- first element is @from@, the second element is @then@ and the successive
728+ -- elements are in increments of @then - from@ up to @to@. Enumeration can
729+ -- occur downwards or upwards depending on whether @then@ comes before or
730+ -- after @from@.
731+ --
732+ -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 2 6
733+ -- [0,2,4,6]
734+ --
735+ -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)
736+ -- [0,-2,-4,-6]
737+ --
738+ enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a
739+
740+-- MAYBE: Sometimes it is more convenient to know the count rather then the
741+-- ending or starting element. For those cases we can define the folllowing
742+-- APIs. All of these will work only for bounded types if we represent the
743+-- count by Int.
744+--
745+-- enumerateN
746+-- enumerateFromN
747+-- enumerateToN
748+-- enumerateFromStep
749+-- enumerateFromStepN
750+
751+-------------------------------------------------------------------------------
752+-- Convenient functions for bounded types
753+-------------------------------------------------------------------------------
754+--
755+-- |
756+-- > enumerate = enumerateFrom minBound
757+--
758+-- Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'
759+--
760+{-# INLINE enumerate #-}
761+enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a
762+enumerate = enumerateFrom minBound
763+
764+-- |
765+-- >>> enumerateTo = Stream.enumerateFromTo minBound
766+--
767+-- Enumerate a 'Bounded' type from its 'minBound' to specified value.
768+--
769+{-# INLINE enumerateTo #-}
770+enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a
771+enumerateTo = enumerateFromTo minBound
772+
773+-- |
774+-- >>> enumerateFromBounded from = Stream.enumerateFromTo from maxBound
775+--
776+-- 'enumerateFrom' for 'Bounded' 'Enum' types.
777+--
778+{-# INLINE enumerateFromBounded #-}
779+enumerateFromBounded :: (Monad m, Enumerable a, Bounded a)
780+ => a -> Stream m a
781+enumerateFromBounded from = enumerateFromTo from maxBound
782+
783+-------------------------------------------------------------------------------
784+-- Enumerable Instances
785+-------------------------------------------------------------------------------
786+--
787+-- For Enum types smaller than or equal to Int size.
788+#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE) \
789+instance Enumerable SMALL_TYPE where { \
790+ {-# INLINE enumerateFrom #-}; \
791+ enumerateFrom = enumerateFromBounded; \
792+ {-# INLINE enumerateFromThen #-}; \
793+ enumerateFromThen = enumerateFromThenSmallBounded; \
794+ {-# INLINE enumerateFromTo #-}; \
795+ enumerateFromTo = enumerateFromToSmall; \
796+ {-# INLINE enumerateFromThenTo #-}; \
797+ enumerateFromThenTo = enumerateFromThenToSmall }
798+
799+ENUMERABLE_BOUNDED_SMALL(())
800+ENUMERABLE_BOUNDED_SMALL(Bool)
801+ENUMERABLE_BOUNDED_SMALL(Ordering)
802+ENUMERABLE_BOUNDED_SMALL(Char)
803+
804+-- For bounded Integral Enum types, may be larger than Int.
805+#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE) \
806+instance Enumerable INTEGRAL_TYPE where { \
807+ {-# INLINE enumerateFrom #-}; \
808+ enumerateFrom = enumerateFromIntegral; \
809+ {-# INLINE enumerateFromThen #-}; \
810+ enumerateFromThen = enumerateFromThenIntegral; \
811+ {-# INLINE enumerateFromTo #-}; \
812+ enumerateFromTo = enumerateFromToIntegral; \
813+ {-# INLINE enumerateFromThenTo #-}; \
814+ enumerateFromThenTo = enumerateFromThenToIntegral }
815+
816+ENUMERABLE_BOUNDED_INTEGRAL(Int)
817+ENUMERABLE_BOUNDED_INTEGRAL(Int8)
818+ENUMERABLE_BOUNDED_INTEGRAL(Int16)
819+ENUMERABLE_BOUNDED_INTEGRAL(Int32)
820+ENUMERABLE_BOUNDED_INTEGRAL(Int64)
821+ENUMERABLE_BOUNDED_INTEGRAL(Word)
822+ENUMERABLE_BOUNDED_INTEGRAL(Word8)
823+ENUMERABLE_BOUNDED_INTEGRAL(Word16)
824+ENUMERABLE_BOUNDED_INTEGRAL(Word32)
825+ENUMERABLE_BOUNDED_INTEGRAL(Word64)
826+
827+-- For unbounded Integral Enum types.
828+#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE) \
829+instance Enumerable INTEGRAL_TYPE where { \
830+ {-# INLINE enumerateFrom #-}; \
831+ enumerateFrom from = enumerateFromStepIntegral from 1; \
832+ {-# INLINE enumerateFromThen #-}; \
833+ enumerateFromThen from next = \
834+ enumerateFromStepIntegral from (next - from); \
835+ {-# INLINE enumerateFromTo #-}; \
836+ enumerateFromTo = enumerateFromToIntegral; \
837+ {-# INLINE enumerateFromThenTo #-}; \
838+ enumerateFromThenTo = enumerateFromThenToIntegral }
839+
840+ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)
841+ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)
842+
843+#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT) \
844+instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where { \
845+ {-# INLINE enumerateFrom #-}; \
846+ enumerateFrom = enumerateFromFractional; \
847+ {-# INLINE enumerateFromThen #-}; \
848+ enumerateFromThen = enumerateFromThenFractional; \
849+ {-# INLINE enumerateFromTo #-}; \
850+ enumerateFromTo = enumerateFromToFractional; \
851+ {-# INLINE enumerateFromThenTo #-}; \
852+ enumerateFromThenTo = enumerateFromThenToFractional }
853+
854+ENUMERABLE_FRACTIONAL(Float,)
855+ENUMERABLE_FRACTIONAL(Double,)
856+ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)
857+ENUMERABLE_FRACTIONAL((Ratio a),Integral a)
858+
859+instance Enumerable a => Enumerable (Identity a) where
860+ {-# INLINE enumerateFrom #-}
861+ enumerateFrom (Identity from) =
862+ fmap Identity $ enumerateFrom from
863+ {-# INLINE enumerateFromThen #-}
864+ enumerateFromThen (Identity from) (Identity next) =
865+ fmap Identity $ enumerateFromThen from next
866+ {-# INLINE enumerateFromTo #-}
867+ enumerateFromTo (Identity from) (Identity to) =
868+ fmap Identity $ enumerateFromTo from to
869+ {-# INLINE enumerateFromThenTo #-}
870+ enumerateFromThenTo (Identity from) (Identity next) (Identity to) =
871+ fmap Identity
872+ $ enumerateFromThenTo from next to
873+
874+-- TODO
875+{-
876+instance Enumerable a => Enumerable (Last a)
877+instance Enumerable a => Enumerable (First a)
878+instance Enumerable a => Enumerable (Max a)
879+instance Enumerable a => Enumerable (Min a)
880+instance Enumerable a => Enumerable (Const a b)
881+instance Enumerable (f a) => Enumerable (Alt f a)
882+instance Enumerable (f a) => Enumerable (Ap f a)
883+-}
375884------------------------------------------------------------------------------
376885-- Time Enumeration
377886------------------------------------------------------------------------------
378887
379−{-# INLINE_NORMAL times #-}
380−times :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)
381−times g = Stream step Nothing
888+-- | @timesWith g@ returns a stream of time value tuples. The first component
889+-- of the tuple is an absolute time reference (epoch) denoting the start of the
890+-- stream and the second component is a time relative to the reference.
891+--
892+-- The argument @g@ specifies the granularity of the relative time in seconds.
893+-- A lower granularity clock gives higher precision but is more expensive in
894+-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
895+--
896+-- >>> import Control.Concurrent (threadDelay)
897+-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)
898+-- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01
899+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
900+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
901+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
902+--
903+-- Note: This API is not safe on 32-bit machines.
904+--
905+-- /Pre-release/
906+--
907+{-# INLINE_NORMAL timesWith #-}
908+timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)
909+timesWith g = Stream step Nothing
382910
383911 where
384912
@@ -395,6 +923,125 @@ times g = Stream step Nothing
395923 -- floating precision time
396924 return $ Yield (toAbsTime t0, toRelTime64 (a - t0)) s
397925
926+-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
927+-- granularity @g@ specified in seconds. A low granularity clock is more
928+-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
929+-- as 1 ms.
930+--
931+-- >>> f = Fold.drainMapM print
932+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01
933+-- AbsTime (TimeSpec {sec = ..., nsec = ...})
934+-- AbsTime (TimeSpec {sec = ..., nsec = ...})
935+-- AbsTime (TimeSpec {sec = ..., nsec = ...})
936+--
937+-- Note: This API is not safe on 32-bit machines.
938+--
939+-- /Pre-release/
940+--
941+{-# INLINE absTimesWith #-}
942+absTimesWith :: MonadIO m => Double -> Stream m AbsTime
943+absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
944+
945+-- | @relTimesWith g@ returns a stream of relative time values starting from 0,
946+-- using a clock of granularity @g@ specified in seconds. A low granularity
947+-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
948+-- ms is treated as 1 ms.
949+--
950+-- >>> f = Fold.drainMapM print
951+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
952+-- RelTime64 (NanoSecond64 ...)
953+-- RelTime64 (NanoSecond64 ...)
954+-- RelTime64 (NanoSecond64 ...)
955+--
956+-- Note: This API is not safe on 32-bit machines.
957+--
958+-- /Pre-release/
959+--
960+{-# INLINE relTimesWith #-}
961+relTimesWith :: MonadIO m => Double -> Stream m RelTime64
962+relTimesWith = fmap snd . timesWith
963+
964+-- | @times@ returns a stream of time value tuples with clock of 10 ms
965+-- granularity. The first component of the tuple is an absolute time reference
966+-- (epoch) denoting the start of the stream and the second component is a time
967+-- relative to the reference.
968+--
969+-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)
970+-- >>> Stream.fold f $ Stream.take 3 $ Stream.times
971+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
972+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
973+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
974+--
975+-- Note: This API is not safe on 32-bit machines.
976+--
977+-- /Pre-release/
978+--
979+{-# INLINE times #-}
980+times :: MonadIO m => Stream m (AbsTime, RelTime64)
981+times = timesWith 0.01
982+
983+-- | @absTimes@ returns a stream of absolute timestamps using a clock of 10 ms
984+-- granularity.
985+--
986+-- >>> f = Fold.drainMapM print
987+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes
988+-- AbsTime (TimeSpec {sec = ..., nsec = ...})
989+-- AbsTime (TimeSpec {sec = ..., nsec = ...})
990+-- AbsTime (TimeSpec {sec = ..., nsec = ...})
991+--
992+-- Note: This API is not safe on 32-bit machines.
993+--
994+-- /Pre-release/
995+--
996+{-# INLINE absTimes #-}
997+absTimes :: MonadIO m => Stream m AbsTime
998+absTimes = fmap (uncurry addToAbsTime64) times
999+
1000+-- | @relTimes@ returns a stream of relative time values starting from 0,
1001+-- using a clock of granularity 10 ms.
1002+--
1003+-- >>> f = Fold.drainMapM print
1004+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes
1005+-- RelTime64 (NanoSecond64 ...)
1006+-- RelTime64 (NanoSecond64 ...)
1007+-- RelTime64 (NanoSecond64 ...)
1008+--
1009+-- Note: This API is not safe on 32-bit machines.
1010+--
1011+-- /Pre-release/
1012+--
1013+{-# INLINE relTimes #-}
1014+relTimes :: MonadIO m => Stream m RelTime64
1015+relTimes = fmap snd times
1016+
1017+-- | @durations g@ returns a stream of relative time values measuring the time
1018+-- elapsed since the immediate predecessor element of the stream was generated.
1019+-- The first element of the stream is always 0. @durations@ uses a clock of
1020+-- granularity @g@ specified in seconds. A low granularity clock is more
1021+-- expensive in terms of CPU usage. The minimum granularity is 1 millisecond.
1022+-- Durations lower than 1 ms will be 0.
1023+--
1024+-- Note: This API is not safe on 32-bit machines.
1025+--
1026+-- /Unimplemented/
1027+--
1028+{-# INLINE durations #-}
1029+durations :: -- Monad m =>
1030+ Double -> t m RelTime64
1031+durations = undefined
1032+
1033+-- | Generate a singleton event at or after the specified absolute time. Note
1034+-- that this is different from a threadDelay, a threadDelay starts from the
1035+-- time when the action is evaluated, whereas if we use AbsTime based timeout
1036+-- it will immediately expire if the action is evaluated too late.
1037+--
1038+-- /Unimplemented/
1039+--
1040+{-# INLINE timeout #-}
1041+timeout :: -- Monad m =>
1042+ AbsTime -> t m ()
1043+timeout = undefined
1044+
3981045-------------------------------------------------------------------------------
3991046-- From Generators
4001047-------------------------------------------------------------------------------
@@ -435,6 +1082,22 @@ generate n gen = generateM n (return . gen)
4351082-- Iteration
4361083-------------------------------------------------------------------------------
4371084
1085+-- |
1086+-- >>> iterateM f m = m >>= \a -> return a `Stream.consM` iterateM f (f a)
1087+--
1088+-- Generate an infinite stream with the first element generated by the action
1089+-- @m@ and each successive element derived by applying the monadic function
1090+-- @f@ on the previous element.
1091+--
1092+-- >>> :{
1093+-- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)
1094+-- & Stream.take 3
1095+-- & Stream.fold Fold.toList
1096+-- :}
1097+-- 0
1098+-- 1
1099+-- [0,1,2]
1100+--
4381101{-# INLINE_NORMAL iterateM #-}
4391102iterateM :: Monad m => (a -> m a) -> m a -> Stream m a
4401103#ifdef USE_UNFOLDS_EVERYWHERE
@@ -443,6 +1106,16 @@ iterateM step = unfold (Unfold.iterateM step)
4431106iterateM step = Stream (\_ st -> st >>= \(!x) -> return $ Yield x (step x))
4441107#endif
4451108
1109+-- |
1110+-- >>> iterate f x = x `Stream.cons` iterate f x
1111+--
1112+-- Generate an infinite stream with @x@ as the first element and each
1113+-- successive element derived by applying the function @f@ on the previous
1114+-- element.
1115+--
1116+-- >>> Stream.fold Fold.toList $ Stream.take 5 $ Stream.iterate (+1) 1
1117+-- [1,2,3,4,5]
1118+--
4461119{-# INLINE_NORMAL iterate #-}
4471120iterate :: Monad m => (a -> a) -> a -> Stream m a
4481121iterate step st = iterateM (return . step) (return st)
@@ -464,12 +1137,39 @@ fromListM = Stream step
4641137 step _ [] = return Stop
4651138#endif
4661139
1140+-- |
1141+-- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil
1142+--
1143+-- Construct a stream from a 'Foldable' containing pure values:
1144+--
1145+-- /WARNING: O(n^2), suitable only for a small number of
1146+-- elements in the stream/
1147+--
1148+{-# INLINE fromFoldable #-}
1149+fromFoldable :: (Monad m, Foldable f) => f a -> Stream m a
1150+fromFoldable = Prelude.foldr cons nil
1151+
1152+-- |
1153+-- >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil
1154+--
1155+-- Construct a stream from a 'Foldable' containing pure values:
1156+--
1157+-- /WARNING: O(n^2), suitable only for a small number of
1158+-- elements in the stream/
1159+--
1160+{-# INLINE fromFoldableM #-}
1161+fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a
1162+fromFoldableM = Prelude.foldr consM nil
1163+
4671164-------------------------------------------------------------------------------
4681165-- From pointers
4691166-------------------------------------------------------------------------------
4701167
471−-- | Read an infinite stream from a pointer, advancing the pointer as needed.
472−-- The caller is responsible to end the stream safely.
1168+-- | Keep reading 'Storable' elements from 'Ptr' onwards.
1169+--
1170+-- /Unsafe:/ The caller is responsible for safe addressing.
1171+--
1172+-- /Pre-release/
4731173{-# INLINE fromPtr #-}
4741174fromPtr :: forall m a. (MonadIO m, Storable a) => Ptr a -> Stream m a
4751175fromPtr = Stream step
@@ -480,3 +1180,32 @@ fromPtr = Stream step
4801180 step _ p = do
4811181 x <- liftIO $ peek p
4821182 return $ Yield x (PTR_NEXT(p, a))
1183+
1184+-- | Take @n@ 'Storable' elements starting from 'Ptr' onwards.
1185+--
1186+-- >>> fromPtrN n = Stream.take n . Stream.fromPtr
1187+--
1188+-- /Unsafe:/ The caller is responsible for safe addressing.
1189+--
1190+-- /Pre-release/
1191+{-# INLINE fromPtrN #-}
1192+fromPtrN :: (MonadIO m, Storable a) => Int -> Ptr a -> Stream m a
1193+fromPtrN n = take n . fromPtr
1194+
1195+-- | Read bytes from an 'Addr#' until a 0 byte is encountered, the 0 byte is
1196+-- not included in the stream.
1197+--
1198+-- >>> fromByteStr# addr = Stream.takeWhile (/= 0) $ Stream.fromPtr $ Ptr addr
1199+--
1200+-- /Unsafe:/ The caller is responsible for safe addressing.
1201+--
1202+-- Note that this is completely safe when reading from Haskell string
1203+-- literals because they are guaranteed to be NULL terminated:
1204+--
1205+-- >>> Stream.fold Fold.toList $ Stream.fromByteStr# "\1\2\3\0"#
1206+-- [1,2,3]
1207+--
1208+{-# INLINE fromByteStr# #-}
1209+fromByteStr# :: MonadIO m => Addr# -> Stream m Word8
1210+fromByteStr# addr =
1211+ takeWhile (/= 0) $ fromPtr $ Ptr addr
core/src/Streamly/Internal/Data/Stream/StreamD/Lift.hs+36−8
@@ -11,8 +11,8 @@
1111module Streamly.Internal.Data.Stream.StreamD.Lift
1212 (
1313 -- * Generalize Inner Monad
14− hoist
15− , generally -- XXX generalize
14+ morphInner
15+ , generalizeInner
1616
1717 -- * Transform Inner Monad
1818 , liftInnerWith
@@ -28,13 +28,26 @@ import Streamly.Internal.Data.SVar.Type (adaptState)
2828
2929import Streamly.Internal.Data.Stream.StreamD.Type
3030
31+-- $setup
32+-- >>> :m
33+-- >>> import Data.Functor.Identity (runIdentity)
34+-- >>> import Streamly.Internal.Data.Stream as Stream
35+
3136-------------------------------------------------------------------------------
3237-- Generalize Inner Monad
3338-------------------------------------------------------------------------------
3439
35−{-# INLINE_NORMAL hoist #-}
36−hoist :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a
37−hoist f (Stream step state) = Stream step' state
40+-- | Transform the inner monad of a stream using a natural transformation.
41+--
42+-- Example, generalize the inner monad from Identity to any other:
43+--
44+-- >>> generalizeInner = Stream.morphInner (return . runIdentity)
45+--
46+-- Also known as hoist.
47+--
48+{-# INLINE_NORMAL morphInner #-}
49+morphInner :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a
50+morphInner f (Stream step state) = Stream step' state
3851 where
3952 {-# INLINE_LATE step' #-}
4053 step' gst st = do
@@ -44,14 +57,23 @@ hoist f (Stream step state) = Stream step' state
4457 Skip s -> Skip s
4558 Stop -> Stop
4659
47−{-# INLINE generally #-}
48−generally :: Monad m => Stream Identity a -> Stream m a
49−generally = hoist (return . runIdentity)
60+-- | Generalize the inner monad of the stream from 'Identity' to any monad.
61+--
62+-- Definition:
63+--
64+-- >>> generalizeInner = Stream.morphInner (return . runIdentity)
65+--
66+{-# INLINE generalizeInner #-}
67+generalizeInner :: Monad m => Stream Identity a -> Stream m a
68+generalizeInner = morphInner (return . runIdentity)
5069
5170-------------------------------------------------------------------------------
5271-- Transform Inner Monad
5372-------------------------------------------------------------------------------
5473
74+-- | Lift the inner monad @m@ of a stream @Stream m a@ to @t m@ using the
75+-- supplied lift function.
76+--
5577{-# INLINE_NORMAL liftInnerWith #-}
5678liftInnerWith :: (Monad (t m)) =>
5779 (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a
@@ -67,6 +89,8 @@ liftInnerWith lift (Stream step state) = Stream step1 state
6789 Skip s -> Skip s
6890 Stop -> Stop
6991
92+-- | Evaluate the inner monad of a stream using the supplied runner function.
93+--
7094{-# INLINE_NORMAL runInnerWith #-}
7195runInnerWith :: Monad m =>
7296 (forall b. t m b -> m b) -> Stream (t m) a -> Stream m a
@@ -82,6 +106,10 @@ runInnerWith run (Stream step state) = Stream step1 state
82106 Skip s -> Skip s
83107 Stop -> Stop
84108
109+-- | Evaluate the inner monad of a stream using the supplied stateful runner
110+-- function and the initial state. The state returned by an invocation of the
111+-- runner is supplied as input state to the next invocation.
112+--
85113{-# INLINE_NORMAL runInnerWithState #-}
86114runInnerWithState :: Monad m =>
87115 (forall b. s -> t m b -> m (b, s))
core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+546−89
@@ -41,17 +41,17 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
4141
4242 -- *** Interleaving
4343 -- | Interleave elements from two streams alternately. A special case of
44− -- unfoldManyInterleave.
44+ -- unfoldInterleave.
4545 , InterleaveState(..)
4646 , interleave
4747 , interleaveMin
48− , interleaveSuffix
49− , interleaveInfix
48+ , interleaveFst
49+ , interleaveFstSuffix
5050
5151 -- *** Scheduling
5252 -- | Execute streams alternately irrespective of whether they generate
5353 -- elements or not. Note 'interleave' would execute a stream until it
54− -- yields an element. A special case of unfoldManyRoundRobin.
54+ -- yields an element. A special case of unfoldRoundRobin.
5555 , roundRobin -- interleaveFair?/ParallelFair
5656
5757 -- *** Zipping
@@ -63,6 +63,8 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
6363 -- | Interleave elements from two streams based on a condition.
6464 , mergeBy
6565 , mergeByM
66+ , mergeMinBy
67+ , mergeFstBy
6668
6769 -- ** Combine N Streams
6870 -- | Functions generally ending in these shapes:
@@ -85,20 +87,24 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
8587 -- gintercalate.
8688 , unfoldMany
8789 , ConcatUnfoldInterleaveState (..)
88− , unfoldManyInterleave
89− , unfoldManyRoundRobin
90+ , unfoldInterleave
91+ , unfoldRoundRobin
9092
9193 -- *** Interpose
9294 -- | Like unfoldMany but intersperses an effect between the streams. A
9395 -- special case of gintercalate.
9496 , interpose
97+ , interposeM
9598 , interposeSuffix
99+ , interposeSuffixM
96100
97101 -- *** Intercalate
98102 -- | Like unfoldMany but intersperses streams from another source between
99103 -- the streams from the first source.
100104 , gintercalate
101105 , gintercalateSuffix
106+ , intercalate
107+ , intercalateSuffix
102108
103109 -- * Eliminate
104110 -- | Folding and Parsing chunks of streams to eliminate nested streams.
@@ -113,6 +119,7 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
113119 -- | Apply folds on a stream.
114120 , foldMany
115121 , refoldMany
122+ , foldSequence
116123 , foldIterateM
117124 , refoldIterateM
118125
@@ -123,7 +130,11 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
123130 -- splitting the stream and then folds each such split to single value in
124131 -- the output stream.
125132 , parseMany
133+ , parseManyD
134+ , parseSequence
135+ , parseManyTill
126136 , parseIterate
137+ , parseIterateD
127138
128139 -- ** Grouping
129140 -- | Group segments of a stream and fold. Special case of parsing.
@@ -138,11 +149,21 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
138149 , splitOnSuffixSeq
139150 , sliceOnSuffix
140151
152+ -- XXX Implement these as folds or parsers instead.
153+ , splitOnSuffixSeqAny
154+ , splitOnPrefix
155+ , splitOnAny
156+
141157 -- * Transform (Nested Containers)
142158 -- | Opposite to compact in ArrayStream
143159 , splitInnerBy
144160 , splitInnerBySuffix
145161 , intersectBySorted
162+
163+ -- * Reduce By Streams
164+ , dropPrefix
165+ , dropInfix
166+ , dropSuffix
146167 )
147168where
148169
@@ -152,7 +173,6 @@ where
152173import Control.Exception (assert)
153174import Control.Monad.IO.Class (MonadIO(..))
154175import Data.Bits (shiftR, shiftL, (.|.), (.&.))
155−import Data.Functor.Identity ( Identity )
156176import Data.Proxy (Proxy(..))
157177import Data.Word (Word32)
158178import Foreign.Storable (Storable, peek)
@@ -175,19 +195,49 @@ import qualified Streamly.Internal.Data.Parser as PR
175195import qualified Streamly.Internal.Data.Parser.ParserD as PRD
176196import qualified Streamly.Internal.Data.Ring.Unboxed as RB
177197
198+import Streamly.Internal.Data.Stream.StreamD.Transform
199+ (intersperse, intersperseMSuffix)
178200import Streamly.Internal.Data.Stream.StreamD.Type
179201
180202import Prelude hiding (concatMap, mapM, zipWith)
181203
204+-- $setup
205+-- >>> :m
206+-- >>> import Data.Either (either)
207+-- >>> import Data.IORef
208+-- >>> import Streamly.Internal.Data.Stream (Stream)
209+-- >>> import Prelude hiding (zipWith, concatMap, concat)
210+-- >>> import qualified Streamly.Data.Array as Array
211+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
212+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
213+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
214+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
215+-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
216+--
217+
182218------------------------------------------------------------------------------
183219-- Appending
184220------------------------------------------------------------------------------
185221
186222data AppendState s1 s2 = AppendFirst s1 | AppendSecond s2
187223
188−-- Note that this could be much faster compared to the CPS stream. However, as
189−-- the number of streams being composed increases this may become expensive.
190−-- Need to see where the breaking point is between the two.
224+-- | Fuses two streams sequentially, yielding all elements from the first
225+-- stream, and then all elements from the second stream.
226+--
227+-- >>> s1 = Stream.fromList [1,2]
228+-- >>> s2 = Stream.fromList [3,4]
229+-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
230+-- [1,2,3,4]
231+--
232+-- This function should not be used to dynamically construct a stream. If a
233+-- stream is constructed by successive use of this function it would take
234+-- quadratic time complexity to consume the stream.
235+--
236+-- This function should only be used to statically fuse a stream with another
237+-- stream. Do not use this recursively or where it cannot be inlined.
238+--
239+-- See "Streamly.Data.Stream.StreamK" for an 'append' that can be used to
240+-- construct a stream recursively.
191241--
192242{-# INLINE_NORMAL append #-}
193243append :: Monad m => Stream m a -> Stream m a -> Stream m a
@@ -218,6 +268,15 @@ append (Stream step1 state1) (Stream step2 state2) =
218268data InterleaveState s1 s2 = InterleaveFirst s1 s2 | InterleaveSecond s1 s2
219269 | InterleaveSecondOnly s2 | InterleaveFirstOnly s1
220270
271+-- | Interleaves two streams, yielding one element from each stream
272+-- alternately. When one stream stops the rest of the other stream is used in
273+-- the output stream.
274+--
275+-- When joining many streams in a left associative manner earlier streams will
276+-- get exponential priority than the ones joining later. Because of exponential
277+-- weighting it can be used with 'concatMapWith' even on a large number of
278+-- streams.
279+--
221280{-# INLINE_NORMAL interleave #-}
222281interleave :: Monad m => Stream m a -> Stream m a -> Stream m a
223282interleave (Stream step1 state1) (Stream step2 state2) =
@@ -254,6 +313,9 @@ interleave (Stream step1 state1) (Stream step2 state2) =
254313 Skip s -> Skip (InterleaveSecondOnly s)
255314 Stop -> Stop
256315
316+-- | Like `interleave` but stops interleaving as soon as any of the two streams
317+-- stops.
318+--
257319{-# INLINE_NORMAL interleaveMin #-}
258320interleaveMin :: Monad m => Stream m a -> Stream m a -> Stream m a
259321interleaveMin (Stream step1 state1) (Stream step2 state2) =
@@ -279,9 +341,28 @@ interleaveMin (Stream step1 state1) (Stream step2 state2) =
279341 step _ (InterleaveFirstOnly _) = undefined
280342 step _ (InterleaveSecondOnly _) = undefined
281343
282−{-# INLINE_NORMAL interleaveSuffix #-}
283−interleaveSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a
284−interleaveSuffix (Stream step1 state1) (Stream step2 state2) =
344+-- | Interleaves the outputs of two streams, yielding elements from each stream
345+-- alternately, starting from the first stream. As soon as the first stream
346+-- finishes, the output stops, discarding the remaining part of the second
347+-- stream. In this case, the last element in the resulting stream would be from
348+-- the second stream. If the second stream finishes early then the first stream
349+-- still continues to yield elements until it finishes.
350+--
351+-- >>> :set -XOverloadedStrings
352+-- >>> import Data.Functor.Identity (Identity)
353+-- >>> Stream.interleaveFstSuffix "abc" ",,,," :: Stream Identity Char
354+-- fromList "a,b,c,"
355+-- >>> Stream.interleaveFstSuffix "abc" "," :: Stream Identity Char
356+-- fromList "a,bc"
357+--
358+-- 'interleaveFstSuffix' is a dual of 'interleaveFst'.
359+--
360+-- Do not use dynamically.
361+--
362+-- /Pre-release/
363+{-# INLINE_NORMAL interleaveFstSuffix #-}
364+interleaveFstSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a
365+interleaveFstSuffix (Stream step1 state1) (Stream step2 state2) =
285366 Stream step (InterleaveFirst state1 state2)
286367
287368 where
@@ -317,9 +398,28 @@ data InterleaveInfixState s1 s2 a
317398 | InterleaveInfixFirstYield s1 s2 a
318399 | InterleaveInfixFirstOnly s1
319400
320−{-# INLINE_NORMAL interleaveInfix #-}
321−interleaveInfix :: Monad m => Stream m a -> Stream m a -> Stream m a
322−interleaveInfix (Stream step1 state1) (Stream step2 state2) =
401+-- | Interleaves the outputs of two streams, yielding elements from each stream
402+-- alternately, starting from the first stream and ending at the first stream.
403+-- If the second stream is longer than the first, elements from the second
404+-- stream are infixed with elements from the first stream. If the first stream
405+-- is longer then it continues yielding elements even after the second stream
406+-- has finished.
407+--
408+-- >>> :set -XOverloadedStrings
409+-- >>> import Data.Functor.Identity (Identity)
410+-- >>> Stream.interleaveFst "abc" ",,,," :: Stream Identity Char
411+-- fromList "a,b,c"
412+-- >>> Stream.interleaveFst "abc" "," :: Stream Identity Char
413+-- fromList "a,bc"
414+--
415+-- 'interleaveFst' is a dual of 'interleaveFstSuffix'.
416+--
417+-- Do not use dynamically.
418+--
419+-- /Pre-release/
420+{-# INLINE_NORMAL interleaveFst #-}
421+interleaveFst :: Monad m => Stream m a -> Stream m a -> Stream m a
422+interleaveFst (Stream step1 state1) (Stream step2 state2) =
323423 Stream step (InterleaveInfixFirst state1 state2)
324424
325425 where
@@ -360,6 +460,18 @@ interleaveInfix (Stream step1 state1) (Stream step2 state2) =
360460-- Scheduling
361461------------------------------------------------------------------------------
362462
463+-- | Schedule the execution of two streams in a fair round-robin manner,
464+-- executing each stream once, alternately. Execution of a stream may not
465+-- necessarily result in an output, a stream may choose to @Skip@ producing an
466+-- element until later giving the other stream a chance to run. Therefore, this
467+-- combinator fairly interleaves the execution of two streams rather than
468+-- fairly interleaving the output of the two streams. This can be useful in
469+-- co-operative multitasking without using explicit threads. This can be used
470+-- as an alternative to `async`.
471+--
472+-- Do not use dynamically.
473+--
474+-- /Pre-release/
363475{-# INLINE_NORMAL roundRobin #-}
364476roundRobin :: Monad m => Stream m a -> Stream m a -> Stream m a
365477roundRobin (Stream step1 state1) (Stream step2 state2) =
@@ -396,44 +508,38 @@ roundRobin (Stream step1 state1) (Stream step2 state2) =
396508 Skip s -> Skip (InterleaveFirstOnly s)
397509 Stop -> Stop
398510
399−------------------------------------------------------------------------------
400−-- Zipping
401−------------------------------------------------------------------------------
402−
403−{-# INLINE_NORMAL zipWithM #-}
404−zipWithM :: Monad m
405− => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
406−zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)
407− where
408− {-# INLINE_LATE step #-}
409− step gst (sa, sb, Nothing) = do
410− r <- stepa (adaptState gst) sa
411− return $
412− case r of
413− Yield x sa' -> Skip (sa', sb, Just x)
414− Skip sa' -> Skip (sa', sb, Nothing)
415− Stop -> Stop
416−
417− step gst (sa, sb, Just x) = do
418− r <- stepb (adaptState gst) sb
419− case r of
420− Yield y sb' -> do
421− z <- f x y
422− return $ Yield z (sa, sb', Nothing)
423− Skip sb' -> return $ Skip (sa, sb', Just x)
424− Stop -> return Stop
425−
426−{-# RULES "zipWithM xs xs"
427− forall f xs. zipWithM @Identity f xs xs = mapM (\x -> f x x) xs #-}
428−
429−{-# INLINE zipWith #-}
430−zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
431−zipWith f = zipWithM (\a b -> return (f a b))
432−
433511------------------------------------------------------------------------------
434512-- Merging
435513------------------------------------------------------------------------------
436514
515+-- | Like 'mergeBy' but with a monadic comparison function.
516+--
517+-- Merge two streams randomly:
518+--
519+-- @
520+-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
521+-- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])
522+-- [2,1,2,2,2,1,1,1]
523+-- @
524+--
525+-- Merge two streams in a proportion of 2:1:
526+--
527+-- >>> :{
528+-- do
529+-- let s1 = Stream.fromList [1,1,1,1,1,1]
530+-- s2 = Stream.fromList [2,2,2]
531+-- let proportionately m n = do
532+-- ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]
533+-- return $ \_ _ -> do
534+-- r <- readIORef ref
535+-- writeIORef ref $ Prelude.tail r
536+-- return $ Prelude.head r
537+-- f <- proportionately 2 1
538+-- xs <- Stream.fold Fold.toList $ Stream.mergeByM f s1 s2
539+-- print xs
540+-- :}
541+-- [1,1,2,1,1,2,1,1,2]
542+--
437543{-# INLINE_NORMAL mergeByM #-}
438544mergeByM
439545 :: (Monad m)
@@ -474,12 +580,43 @@ mergeByM cmp (Stream stepa ta) (Stream stepb tb) =
474580
475581 step _ (Nothing, Nothing, Nothing, Nothing) = return Stop
476582
583+-- | Merge two streams using a comparison function. The head elements of both
584+-- the streams are compared and the smaller of the two elements is emitted, if
585+-- both elements are equal then the element from the first stream is used
586+-- first.
587+--
588+-- If the streams are sorted in ascending order, the resulting stream would
589+-- also remain sorted in ascending order.
590+--
591+-- >>> s1 = Stream.fromList [1,3,5]
592+-- >>> s2 = Stream.fromList [2,4,6,8]
593+-- >>> Stream.fold Fold.toList $ Stream.mergeBy compare s1 s2
594+-- [1,2,3,4,5,6,8]
595+--
477596{-# INLINE mergeBy #-}
478597mergeBy
479598 :: (Monad m)
480599 => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
481600mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
482601
602+-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
603+--
604+-- /Unimplemented/
605+{-# INLINABLE mergeMinBy #-}
606+mergeMinBy :: -- Monad m =>
607+ (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
608+mergeMinBy _f _m1 _m2 = undefined
609+ -- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)
610+
611+-- | Like 'mergeByM' but stops merging as soon as the first stream stops.
612+--
613+-- /Unimplemented/
614+{-# INLINABLE mergeFstBy #-}
615+mergeFstBy :: -- Monad m =>
616+ (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
617+mergeFstBy _f _m1 _m2 = undefined
618+ -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
619+
483620-------------------------------------------------------------------------------
484621-- Intersection of sorted streams
485622-------------------------------------------------------------------------------
@@ -551,17 +688,28 @@ data ConcatUnfoldInterleaveState o i =
551688-- Maybe we can configure the behavior.
552689--
553690-- XXX Instead of using "concatPairsWith wSerial" we can implement an N-way
554−-- interleaving CPS combinator which behaves like unfoldManyInterleave. Instead
691+-- interleaving CPS combinator which behaves like unfoldInterleave. Instead
555692-- of pairing up the streams we just need to go yielding one element from each
556693-- stream and storing the remaining streams and then keep doing rounds through
557694-- those in a round robin fashion. This would be much like wAsync.
695+
696+-- | This does not pair streams like mergeMapWith, instead, it goes through
697+-- each stream one by one and yields one element from each stream. After it
698+-- goes to the last stream it reverses the traversal to come back to the first
699+-- stream yielding elements from each stream on its way back to the first
700+-- stream and so on.
558701--
559−-- See 'Streamly.Internal.Data.Stream.unfoldInterleave' documentation for more
560−-- details.
702+-- >>> lists = Stream.fromList [[1,1],[2,2],[3,3],[4,4],[5,5]]
703+-- >>> interleaved = Stream.unfoldInterleave Unfold.fromList lists
704+-- >>> Stream.fold Fold.toList interleaved
705+-- [1,2,3,4,5,5,4,3,2,1]
561706--
562−{-# INLINE_NORMAL unfoldManyInterleave #-}
563−unfoldManyInterleave :: Monad m => Unfold m a b -> Stream m a -> Stream m b
564−unfoldManyInterleave (Unfold istep inject) (Stream ostep ost) =
707+-- Note that this is order of magnitude more efficient than "mergeMapWith
708+-- interleave" because of fusion.
709+--
710+{-# INLINE_NORMAL unfoldInterleave #-}
711+unfoldInterleave :: Monad m => Unfold m a b -> Stream m a -> Stream m b
712+unfoldInterleave (Unfold istep inject) (Stream ostep ost) =
565713 Stream step (ConcatUnfoldInterleaveOuter ost [])
566714
567715 where
@@ -612,11 +760,17 @@ unfoldManyInterleave (Unfold istep inject) (Stream ostep ost) =
612760--
613761-- This could be inefficient if the tasks are too small.
614762--
615−-- Compared to unfoldManyInterleave this one switches streams on Skips.
763+-- Compared to unfoldInterleave this one switches streams on Skips.
764+
765+-- | 'unfoldInterleave' switches to the next stream whenever a value from a
766+-- stream is yielded, it does not switch on a 'Skip'. So if a stream keeps
767+-- skipping for long time other streams won't get a chance to run.
768+-- 'unfoldRoundRobin' switches on Skip as well. So it basically schedules each
769+-- stream fairly irrespective of whether it produces a value or not.
616770--
617−{-# INLINE_NORMAL unfoldManyRoundRobin #-}
618−unfoldManyRoundRobin :: Monad m => Unfold m a b -> Stream m a -> Stream m b
619−unfoldManyRoundRobin (Unfold istep inject) (Stream ostep ost) =
771+{-# INLINE_NORMAL unfoldRoundRobin #-}
772+unfoldRoundRobin :: Monad m => Unfold m a b -> Stream m a -> Stream m b
773+unfoldRoundRobin (Unfold istep inject) (Stream ostep ost) =
620774 Stream step (ConcatUnfoldInterleaveOuter ost [])
621775 where
622776 {-# INLINE_LATE step #-}
@@ -677,11 +831,11 @@ data InterposeSuffixState s1 i1 =
677831-- effect only if at least one element has been yielded by the unfolding.
678832-- However, that becomes a bit complicated, so we have chosen the former
679833-- behvaior for now.
680−{-# INLINE_NORMAL interposeSuffix #-}
681−interposeSuffix
834+{-# INLINE_NORMAL interposeSuffixM #-}
835+interposeSuffixM
682836 :: Monad m
683837 => m c -> Unfold m b c -> Stream m b -> Stream m c
684−interposeSuffix
838+interposeSuffixM
685839 action
686840 (Unfold istep1 inject1) (Stream step1 state1) =
687841 Stream step (InterposeSuffixFirst state1)
@@ -719,6 +873,19 @@ interposeSuffix
719873 r <- action
720874 return $ Yield r (InterposeSuffixFirst s1)
721875
876+-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
877+
878+-- | Unfold the elements of a stream, append the given element after each
879+-- unfolded stream and then concat them into a single stream.
880+--
881+-- >>> unlines = Stream.interposeSuffix '\n'
882+--
883+-- /Pre-release/
884+{-# INLINE interposeSuffix #-}
885+interposeSuffix :: Monad m
886+ => c -> Unfold m b c -> Stream m b -> Stream m c
887+interposeSuffix x = interposeSuffixM (return x)
888+
722889{-# ANN type InterposeState Fuse #-}
723890data InterposeState s1 i1 a =
724891 InterposeFirst s1
@@ -732,9 +899,9 @@ data InterposeState s1 i1 a =
732899
733900-- Note that this only interposes the pure values, we may run many effects to
734901-- generate those values as some effects may not generate anything (Skip).
735−{-# INLINE_NORMAL interpose #-}
736−interpose :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c
737−interpose
902+{-# INLINE_NORMAL interposeM #-}
903+interposeM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c
904+interposeM
738905 action
739906 (Unfold istep1 inject1) (Stream step1 state1) =
740907 Stream step (InterposeFirst state1)
@@ -801,6 +968,19 @@ interpose
801968 return $ Yield v (InterposeFirstInner s1 i1)
802969 -}
803970
971+-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
972+
973+-- | Unfold the elements of a stream, intersperse the given element between the
974+-- unfolded streams and then concat them into a single stream.
975+--
976+-- >>> unwords = Stream.interpose ' '
977+--
978+-- /Pre-release/
979+{-# INLINE interpose #-}
980+interpose :: Monad m
981+ => c -> Unfold m b c -> Stream m b -> Stream m c
982+interpose x = interposeM (return x)
983+
804984------------------------------------------------------------------------------
805985-- Combine N Streams - intercalate
806986------------------------------------------------------------------------------
@@ -815,16 +995,9 @@ data ICUState s1 s2 i1 i2 =
815995 | ICUFirstOnlyInner s1 i1
816996 | ICUSecondOnlyInner s2 i2
817997
818−-- | Interleave streams (full streams, not the elements) unfolded from two
819−-- input streams and concat. Stop when the first stream stops. If the second
820−-- stream ends before the first one then first stream still keeps running alone
821−-- without any interleaving with the second stream.
822−--
823−-- [a1, a2, ... an] [b1, b2 ...]
824−-- => [streamA1, streamA2, ... streamAn] [streamB1, streamB2, ...]
825−-- => [streamA1, streamB1, streamA2...StreamAn, streamBn]
826−-- => [a11, a12, ...a1j, b11, b12, ...b1k, a21, a22, ...]
998+-- | 'interleaveFstSuffix' followed by unfold and concat.
827999--
1000+-- /Pre-release/
8281001{-# INLINE_NORMAL gintercalateSuffix #-}
8291002gintercalateSuffix
8301003 :: Monad m
@@ -901,16 +1074,17 @@ data ICALState s1 s2 i1 i2 a =
9011074 -- -- | ICALSecondInner s1 s2 i1 i2 a
9021075 -- -- | ICALFirstResume s1 s2 i1 i2 a
9031076
904−-- | Interleave streams (full streams, not the elements) unfolded from two
905−-- input streams and concat. Stop when the first stream stops. If the second
906−-- stream ends before the first one then first stream still keeps running alone
907−-- without any interleaving with the second stream.
1077+-- XXX we can swap the order of arguments to gintercalate so that the
1078+-- definition of unfoldMany becomes simpler? The first stream should be
1079+-- infixed inside the second one. However, if we change the order in
1080+-- "interleave" as well similarly, then that will make it a bit unintuitive.
9081081--
909−-- [a1, a2, ... an] [b1, b2 ...]
910−-- => [streamA1, streamA2, ... streamAn] [streamB1, streamB2, ...]
911−-- => [streamA1, streamB1, streamA2...StreamAn, streamBn]
912−-- => [a11, a12, ...a1j, b11, b12, ...b1k, a21, a22, ...]
1082+-- > unfoldMany unf str =
1083+-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
1084+
1085+-- | 'interleaveFst' followed by unfold and concat.
9131086--
1087+-- /Pre-release/
9141088{-# INLINE_NORMAL gintercalate #-}
9151089gintercalate
9161090 :: Monad m
@@ -1017,10 +1191,57 @@ gintercalate
10171191 return $ Yield x (ICALFirstInner s1 s2 i1 i2)
10181192 -}
10191193
1194+-- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)
1195+
1196+-- | 'intersperseMSuffix' followed by unfold and concat.
1197+--
1198+-- >>> intercalateSuffix u a = Stream.unfoldMany u . Stream.intersperseMSuffix a
1199+-- >>> intersperseMSuffix = Stream.intercalateSuffix Unfold.identity
1200+-- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"
1201+--
1202+-- >>> input = Stream.fromList ["abc", "def", "ghi"]
1203+-- >>> Stream.fold Fold.toList $ Stream.intercalateSuffix Unfold.fromList "\n" input
1204+-- "abc\ndef\nghi\n"
1205+--
1206+{-# INLINE intercalateSuffix #-}
1207+intercalateSuffix :: Monad m
1208+ => Unfold m b c -> b -> Stream m b -> Stream m c
1209+intercalateSuffix unf seed = unfoldMany unf . intersperseMSuffix (return seed)
1210+
1211+-- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)
1212+
1213+-- | 'intersperse' followed by unfold and concat.
1214+--
1215+-- >>> intercalate u a = Stream.unfoldMany u . Stream.intersperse a
1216+-- >>> intersperse = Stream.intercalate Unfold.identity
1217+-- >>> unwords = Stream.intercalate Unfold.fromList " "
1218+--
1219+-- >>> input = Stream.fromList ["abc", "def", "ghi"]
1220+-- >>> Stream.fold Fold.toList $ Stream.intercalate Unfold.fromList " " input
1221+-- "abc def ghi"
1222+--
1223+{-# INLINE intercalate #-}
1224+intercalate :: Monad m
1225+ => Unfold m b c -> b -> Stream m b -> Stream m c
1226+intercalate unf seed str = unfoldMany unf $ intersperse seed str
1227+
10201228------------------------------------------------------------------------------
10211229-- Folding
10221230------------------------------------------------------------------------------
10231231
1232+-- | Apply a stream of folds to an input stream and emit the results in the
1233+-- output stream.
1234+--
1235+-- /Unimplemented/
1236+--
1237+{-# INLINE foldSequence #-}
1238+foldSequence
1239+ :: -- Monad m =>
1240+ Stream m (Fold m a b)
1241+ -> Stream m a
1242+ -> Stream m b
1243+foldSequence _f _m = undefined
1244+
10241245{-# ANN type FIterState Fuse #-}
10251246data FIterState s f m a b
10261247 = FIterInit s f
@@ -1028,6 +1249,21 @@ data FIterState s f m a b
10281249 | FIterYield b (FIterState s f m a b)
10291250 | FIterStop
10301251
1252+-- | Iterate a fold generator on a stream. The initial value @b@ is used to
1253+-- generate the first fold, the fold is applied on the stream and the result of
1254+-- the fold is used to generate the next fold and so on.
1255+--
1256+-- >>> import Data.Monoid (Sum(..))
1257+-- >>> f x = return (Fold.take 2 (Fold.sconcat x))
1258+-- >>> s = fmap Sum $ Stream.fromList [1..10]
1259+-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s
1260+-- [3,10,21,36,55,55]
1261+--
1262+-- This is the streaming equivalent of monad like sequenced application of
1263+-- folds where next fold is dependent on the previous fold.
1264+--
1265+-- /Pre-release/
1266+--
10311267{-# INLINE_NORMAL foldIterateM #-}
10321268foldIterateM ::
10331269 Monad m => (b -> m (FL.Fold m a b)) -> m b -> Stream m a -> Stream m b
@@ -1150,13 +1386,13 @@ data ParseChunksState x inpBuf st pst =
11501386-- XXX return the remaining stream as part of the error.
11511387-- XXX This is in fact parseMany1 (a la foldMany1). Do we need a parseMany as
11521388-- well?
1153−{-# INLINE_NORMAL parseMany #-}
1154−parseMany
1389+{-# INLINE_NORMAL parseManyD #-}
1390+parseManyD
11551391 :: Monad m
11561392 => PRD.Parser a m b
11571393 -> Stream m a
11581394 -> Stream m (Either ParseError b)
1159−parseMany (PRD.Parser pstep initial extract) (Stream step state) =
1395+parseManyD (PRD.Parser pstep initial extract) (Stream step state) =
11601396 Stream stepOuter (ParseChunksInit [] state)
11611397
11621398 where
@@ -1362,6 +1598,62 @@ parseMany (PRD.Parser pstep initial extract) (Stream step state) =
13621598
13631599 stepOuter _ (ParseChunksYield a next) = return $ Yield a next
13641600
1601+-- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the
1602+-- output stream.
1603+--
1604+-- Example:
1605+--
1606+-- >>> s = Stream.fromList [1..10]
1607+-- >>> parser = Parser.takeBetween 0 2 Fold.sum
1608+-- >>> Stream.fold Fold.toList $ Stream.parseMany parser s
1609+-- [Right 3,Right 7,Right 11,Right 15,Right 19]
1610+--
1611+-- This is the streaming equivalent of the 'Streamly.Data.Parser.many' parse
1612+-- combinator.
1613+--
1614+-- Known Issues: When the parser fails there is no way to get the remaining
1615+-- stream.
1616+--
1617+{-# INLINE parseMany #-}
1618+parseMany
1619+ :: Monad m
1620+ => PR.Parser a m b
1621+ -> Stream m a
1622+ -> Stream m (Either ParseError b)
1623+parseMany p = parseManyD (PRD.fromParserK p)
1624+
1625+-- | Apply a stream of parsers to an input stream and emit the results in the
1626+-- output stream.
1627+--
1628+-- /Unimplemented/
1629+--
1630+{-# INLINE parseSequence #-}
1631+parseSequence
1632+ :: -- Monad m =>
1633+ Stream m (PR.Parser a m b)
1634+ -> Stream m a
1635+ -> Stream m b
1636+parseSequence _f _m = undefined
1637+
1638+-- XXX Change the parser arguments' order
1639+
1640+-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
1641+-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
1642+-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
1643+-- output of @test@ is discarded and the output of @collect@ is emitted in the
1644+-- output stream. The parser fails if @collect@ fails.
1645+--
1646+-- /Unimplemented/
1647+--
1648+{-# INLINE parseManyTill #-}
1649+parseManyTill ::
1650+ -- MonadThrow m =>
1651+ PR.Parser a m b
1652+ -> PR.Parser a m x
1653+ -> Stream m a
1654+ -> Stream m b
1655+parseManyTill = undefined
1656+
13651657{-# ANN type ConcatParseState Fuse #-}
13661658data ConcatParseState c b inpBuf st p m a =
13671659 ConcatParseInit inpBuf st p
@@ -1378,14 +1670,14 @@ data ConcatParseState c b inpBuf st p m a =
13781670 | ConcatParseYield c (ConcatParseState c b inpBuf st p m a)
13791671
13801672-- XXX Review the changes
1381−{-# INLINE_NORMAL parseIterate #-}
1382−parseIterate
1673+{-# INLINE_NORMAL parseIterateD #-}
1674+parseIterateD
13831675 :: Monad m
13841676 => (b -> PRD.Parser a m b)
13851677 -> b
13861678 -> Stream m a
13871679 -> Stream m (Either ParseError b)
1388−parseIterate func seed (Stream step state) =
1680+parseIterateD func seed (Stream step state) =
13891681 Stream stepOuter (ConcatParseInit [] state (func seed))
13901682
13911683 where
@@ -1577,6 +1869,29 @@ parseIterate func seed (Stream step state) =
15771869
15781870 stepOuter _ (ConcatParseYield a next) = return $ Yield a next
15791871
1872+-- | Iterate a parser generating function on a stream. The initial value @b@ is
1873+-- used to generate the first parser, the parser is applied on the stream and
1874+-- the result is used to generate the next parser and so on.
1875+--
1876+-- >>> import Data.Monoid (Sum(..))
1877+-- >>> s = Stream.fromList [1..10]
1878+-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.catRights $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) (Sum 0) $ fmap Sum s
1879+-- [3,10,21,36,55,55]
1880+--
1881+-- This is the streaming equivalent of monad like sequenced application of
1882+-- parsers where next parser is dependent on the previous parser.
1883+--
1884+-- /Pre-release/
1885+--
1886+{-# INLINE parseIterate #-}
1887+parseIterate
1888+ :: Monad m
1889+ => (b -> PR.Parser a m b)
1890+ -> b
1891+ -> Stream m a
1892+ -> Stream m (Either ParseError b)
1893+parseIterate f = parseIterateD (PRD.fromParserK . f)
1894+
15801895------------------------------------------------------------------------------
15811896-- Grouping
15821897------------------------------------------------------------------------------
@@ -2551,6 +2866,109 @@ splitOnSuffixSeq withSep patArr (Fold fstep initial done) (Stream step state) =
25512866 let jump c = SplitOnSuffixSeqKRDone (n - 1) c rb rh1
25522867 yieldProceed jump b
25532868
2869+-- Implement this as a fold or a parser instead.
2870+-- This can be implemented easily using Rabin Karp
2871+-- | Split post any one of the given patterns.
2872+--
2873+-- /Unimplemented/
2874+{-# INLINE splitOnSuffixSeqAny #-}
2875+splitOnSuffixSeqAny :: -- (Monad m, Unboxed a, Integral a) =>
2876+ [Array a] -> Fold m a b -> Stream m a -> Stream m b
2877+splitOnSuffixSeqAny _subseq _f _m = undefined
2878+ -- D.fromStreamD $ D.splitPostAny f subseq (D.toStreamD m)
2879+
2880+-- | Split on a prefixed separator element, dropping the separator. The
2881+-- supplied 'Fold' is applied on the split segments.
2882+--
2883+-- @
2884+-- > splitOnPrefix' p xs = Stream.toList $ Stream.splitOnPrefix p (Fold.toList) (Stream.fromList xs)
2885+-- > splitOnPrefix' (== '.') ".a.b"
2886+-- ["a","b"]
2887+-- @
2888+--
2889+-- An empty stream results in an empty output stream:
2890+-- @
2891+-- > splitOnPrefix' (== '.') ""
2892+-- []
2893+-- @
2894+--
2895+-- An empty segment consisting of only a prefix is folded to the default output
2896+-- of the fold:
2897+--
2898+-- @
2899+-- > splitOnPrefix' (== '.') "."
2900+-- [""]
2901+--
2902+-- > splitOnPrefix' (== '.') ".a.b."
2903+-- ["a","b",""]
2904+--
2905+-- > splitOnPrefix' (== '.') ".a..b"
2906+-- ["a","","b"]
2907+--
2908+-- @
2909+--
2910+-- A prefix is optional at the beginning of the stream:
2911+--
2912+-- @
2913+-- > splitOnPrefix' (== '.') "a"
2914+-- ["a"]
2915+--
2916+-- > splitOnPrefix' (== '.') "a.b"
2917+-- ["a","b"]
2918+-- @
2919+--
2920+-- 'splitOnPrefix' is an inverse of 'intercalatePrefix' with a single element:
2921+--
2922+-- > Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList . Stream.splitOnPrefix (== '.') Fold.toList === id
2923+--
2924+-- Assuming the input stream does not contain the separator:
2925+--
2926+-- > Stream.splitOnPrefix (== '.') Fold.toList . Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList === id
2927+--
2928+-- /Unimplemented/
2929+{-# INLINE splitOnPrefix #-}
2930+splitOnPrefix :: -- (IsStream t, MonadCatch m) =>
2931+ (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
2932+splitOnPrefix _predicate _f = undefined
2933+ -- parseMany (Parser.sliceBeginBy predicate f)
2934+
2935+-- Int list examples for splitOn:
2936+--
2937+-- >>> splitList [] [1,2,3,3,4]
2938+-- > [[1],[2],[3],[3],[4]]
2939+--
2940+-- >>> splitList [5] [1,2,3,3,4]
2941+-- > [[1,2,3,3,4]]
2942+--
2943+-- >>> splitList [1] [1,2,3,3,4]
2944+-- > [[],[2,3,3,4]]
2945+--
2946+-- >>> splitList [4] [1,2,3,3,4]
2947+-- > [[1,2,3,3],[]]
2948+--
2949+-- >>> splitList [2] [1,2,3,3,4]
2950+-- > [[1],[3,3,4]]
2951+--
2952+-- >>> splitList [3] [1,2,3,3,4]
2953+-- > [[1,2],[],[4]]
2954+--
2955+-- >>> splitList [3,3] [1,2,3,3,4]
2956+-- > [[1,2],[4]]
2957+--
2958+-- >>> splitList [1,2,3,3,4] [1,2,3,3,4]
2959+-- > [[],[]]
2960+
2961+-- This can be implemented easily using Rabin Karp
2962+-- | Split on any one of the given patterns.
2963+--
2964+-- /Unimplemented/
2965+--
2966+{-# INLINE splitOnAny #-}
2967+splitOnAny :: -- (Monad m, Unboxed a, Integral a) =>
2968+ [Array a] -> Fold m a b -> Stream m a -> Stream m b
2969+splitOnAny _subseq _f _m =
2970+ undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.toStreamD m)
2971+
25542972------------------------------------------------------------------------------
25552973-- Nested Container Transformation
25562974------------------------------------------------------------------------------
@@ -2663,3 +3081,42 @@ splitInnerBySuffix splitter joiner (Stream step1 state1) =
26633081
26643082 step _ (SplitYielding x next) = return $ Yield x next
26653083 step _ SplitFinishing = return Stop
3084+
3085+------------------------------------------------------------------------------
3086+-- Trimming
3087+------------------------------------------------------------------------------
3088+
3089+-- | Drop prefix from the input stream if present.
3090+--
3091+-- Space: @O(1)@
3092+--
3093+-- /Unimplemented/
3094+{-# INLINE dropPrefix #-}
3095+dropPrefix ::
3096+ -- (Monad m, Eq a) =>
3097+ Stream m a -> Stream m a -> Stream m a
3098+dropPrefix = error "Not implemented yet!"
3099+
3100+-- | Drop all matching infix from the input stream if present. Infix stream
3101+-- may be consumed multiple times.
3102+--
3103+-- Space: @O(n)@ where n is the length of the infix.
3104+--
3105+-- /Unimplemented/
3106+{-# INLINE dropInfix #-}
3107+dropInfix ::
3108+ -- (Monad m, Eq a) =>
3109+ Stream m a -> Stream m a -> Stream m a
3110+dropInfix = error "Not implemented yet!"
3111+
3112+-- | Drop suffix from the input stream if present. Suffix stream may be
3113+-- consumed multiple times.
3114+--
3115+-- Space: @O(n)@ where n is the length of the suffix.
3116+--
3117+-- /Unimplemented/
3118+{-# INLINE dropSuffix #-}
3119+dropSuffix ::
3120+ -- (Monad m, Eq a) =>
3121+ Stream m a -> Stream m a -> Stream m a
3122+dropSuffix = error "Not implemented yet!"
core/src/Streamly/Internal/Data/Stream/Top.hs → core/src/Streamly/Internal/Data/Stream/StreamD/Top.hsrenamed+8−45
@@ -1,5 +1,5 @@
11-- |
2−-- Module : Streamly.Internal.Data.Stream.Top
2+-- Module : Streamly.Internal.Data.Stream.StreamD.Top
33-- Copyright : (c) 2020 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
@@ -8,16 +8,13 @@
88--
99-- Top level module that can depend on all other lower level Stream modules.
1010
11−module Streamly.Internal.Data.Stream.Top
11+module Streamly.Internal.Data.Stream.StreamD.Top
1212 (
1313 -- * Transformation
1414 -- ** Sampling
1515 -- | Value agnostic filtering.
1616 strideFromThen
1717
18− -- ** Reordering
19− , sortBy
20−
2118 -- * Nesting
2219 -- ** Set like operations
2320 -- | These are not exactly set operations because streams are not
@@ -51,18 +48,13 @@ import Control.Monad.IO.Class (MonadIO(..))
5148import Data.IORef (newIORef, readIORef, modifyIORef')
5249import Streamly.Internal.Data.Fold.Type (Fold)
5350import Streamly.Internal.Data.Stream.Common ()
54−import Streamly.Internal.Data.Stream.Type
55− (Stream, fromStreamD, toStreamD, cross)
51+import Streamly.Internal.Data.Stream.StreamD.Type (Stream, cross)
5652
5753import qualified Data.List as List
5854import qualified Streamly.Internal.Data.Fold as Fold
59−import qualified Streamly.Internal.Data.Parser as Parser
60−import qualified Streamly.Internal.Data.Stream.Eliminate as Stream
61−import qualified Streamly.Internal.Data.Stream.Generate as Stream
62−import qualified Streamly.Internal.Data.Stream.Expand as Stream
63−import qualified Streamly.Internal.Data.Stream.Reduce as Stream
64−import qualified Streamly.Internal.Data.Stream.Transform as Stream
65−import qualified Streamly.Internal.Data.Stream.StreamD as StreamD
55+import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream
56+import qualified Streamly.Internal.Data.Stream.StreamD.Nesting as Stream
57+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as Stream
6658
6759import Prelude hiding (filter, zipWith, concatMap, concat)
6860
@@ -91,32 +83,6 @@ strideFromThen offset stride =
9183 Stream.with Stream.indexed Stream.filter
9284 (\(i, _) -> i >= offset && (i - offset) `mod` stride == 0)
9385
94−------------------------------------------------------------------------------
95−-- Reordering
96−------------------------------------------------------------------------------
97−--
98−-- Note: this is not the fastest possible implementation as of now.
99−--
100−-- We could possibly choose different algorithms depending on whether the
101−-- input stream is almost sorted (ascending/descending) or random. We could
102−-- serialize the stream to an array and use quicksort.
103−
104−-- | Sort the input stream using a supplied comparison function.
105−--
106−-- /O(n) space/
107−--
108−{-# INLINE sortBy #-}
109−sortBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a
110−-- sortBy f = Stream.concatPairsWith (Stream.mergeBy f) Stream.fromPure
111−sortBy cmp =
112− let p =
113− Parser.groupByRollingEither
114− (\x -> (< GT) . cmp x)
115− Fold.toStreamRev
116− Fold.toStream
117− in Stream.mergeMapWith (Stream.mergeBy cmp) id
118− . Stream.catRights . Stream.parseMany (fmap (either id id) p)
119−
12086------------------------------------------------------------------------------
12187-- SQL Joins
12288------------------------------------------------------------------------------
@@ -290,10 +256,7 @@ filterInStreamGenericBy eq =
290256{-# INLINE filterInStreamAscBy #-}
291257filterInStreamAscBy :: Monad m =>
292258 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
293−filterInStreamAscBy eq s1 s2 =
294− fromStreamD
295− $ StreamD.intersectBySorted eq (toStreamD s2)
296− $ toStreamD s1
259+filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
297260
298261-- | Delete all elements of the first stream from the seconds stream. If an
299262-- element occurs multiple times in the first stream as many occurrences of it
@@ -380,7 +343,7 @@ unionWithStreamGenericBy eq s1 s2 =
380343 $ do
381344 xs1 <- liftIO $ readIORef ref
382345 return $ Stream.fromList xs1
383− return $ Stream.mapM f s2 <> s3
346+ return $ Stream.mapM f s2 `Stream.append` s3
384347
385348-- | A more efficient 'unionWithStreamGenericBy' for sorted streams.
386349--
core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+784−53
@@ -27,14 +27,16 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
2727 -- * Mapping Effects
2828 , tap
2929 , tapOffsetEvery
30+ , trace
31+ , trace_
3032
3133 -- * Folding
3234 , foldrS
3335 , foldlS
3436
3537 -- * Scanning By 'Fold'
36− , postscanOnce -- XXX rename to postscan
37− , scanOnce -- XXX rename to scan
38+ , postscan
39+ , scan
3840 , scanMany
3941
4042 -- * Scanning
@@ -65,44 +67,67 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
6567
6668 -- * Filtering
6769 -- | Produce a subset of the stream.
70+ , with
6871 , scanMaybe
6972 , filter
7073 , filterM
7174 , deleteBy
75+ , uniqBy
7276 , uniq
77+ , prune
78+ , repeated
7379
7480 -- * Trimming
7581 -- | Produce a subset of the stream trimmed at ends.
7682 , take
7783 , takeWhile
7884 , takeWhileM
85+ , takeWhileLast
86+ , takeWhileAround
7987 , drop
8088 , dropWhile
8189 , dropWhileM
90+ , dropLast
91+ , dropWhileLast
92+ , dropWhileAround
8293
8394 -- * Inserting Elements
8495 -- | Produce a superset of the stream.
8596 , insertBy
8697 , intersperse
8798 , intersperseM
99+ , intersperseMWith
88100 , intersperseMSuffix
89101 , intersperseMSuffixWith
90102
91103 -- * Inserting Side Effects
92104 , intersperseM_
93105 , intersperseMSuffix_
106+ , intersperseMPrefix_
107+
108+ , delay
109+ , delayPre
110+ , delayPost
94111
95112 -- * Reordering
96113 -- | Produce strictly the same set but reordered.
97114 , reverse
98− -- , reverse'
115+ , reverseUnbox
116+ , reassembleBy
99117
100118 -- * Position Indexing
101119 , indexed
102120 , indexedR
103121
122+ -- * Time Indexing
123+ , timestampWith
124+ , timestamped
125+ , timeIndexWith
126+ , timeIndexed
127+
104128 -- * Searching
105129 , findIndices
130+ , elemIndices
106131 , slicesBy
107132
108133 -- * Rolling map
@@ -115,32 +140,73 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
115140 , mapMaybe
116141 , mapMaybeM
117142 , catMaybes
143+
144+ -- * Either Streams
145+ , catLefts
146+ , catRights
147+ , catEithers
118148 )
119149where
120150
121151#include "inline.hs"
122152
153+import Control.Concurrent (threadDelay)
123154import Control.Monad (void)
155+import Control.Monad.IO.Class (MonadIO (liftIO))
156+import Data.Either (fromLeft, isLeft, isRight, fromRight)
157+import Data.Functor ((<&>))
124158import Data.Maybe (fromJust, isJust)
125159import Fusion.Plugin.Types (Fuse(..))
126160
127161import Streamly.Internal.Data.Fold.Type (Fold(..))
128162import Streamly.Internal.Data.Pipe.Type (Pipe(..), PipeState(..))
129163import Streamly.Internal.Data.SVar.Type (adaptState)
164+import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64)
165+import Streamly.Internal.Data.Unboxed (Unbox)
166+import Streamly.Internal.System.IO (defaultChunkSize)
130167
131−import qualified Streamly.Internal.Data.Fold.Type as FL
168+-- import qualified Data.List as List
169+import qualified Streamly.Internal.Data.Array.Type as A
170+import qualified Streamly.Internal.Data.Fold as FL
132171import qualified Streamly.Internal.Data.Pipe.Type as Pipe
172+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
133173
134174import Prelude hiding
135175 ( drop, dropWhile, filter, map, mapM, reverse
136− , scanl, scanl1, sequence, take, takeWhile)
176+ , scanl, scanl1, sequence, take, takeWhile, zipWith)
137177
178+import Streamly.Internal.Data.Stream.StreamD.Generate
179+ (absTimesWith, relTimesWith)
138180import Streamly.Internal.Data.Stream.StreamD.Type
139181
182+--
183+-- $setup
184+-- >>> :m
185+-- >>> import Control.Concurrent (threadDelay)
186+-- >>> import Control.Monad (void)
187+-- >>> import Control.Monad.IO.Class (MonadIO (liftIO))
188+-- >>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
189+-- >>> import Data.Function ((&))
190+-- >>> import Data.Maybe (fromJust, isJust)
191+-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
192+-- >>> import Streamly.Internal.Data.Stream (Stream)
193+-- >>> import qualified Streamly.Data.Fold as Fold
194+-- >>> import qualified Streamly.Data.Unfold as Unfold
195+-- >>> import qualified Streamly.Internal.Data.Fold as Fold (filtering)
196+-- >>> import qualified Streamly.Internal.Data.Fold.Window as Window
197+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
198+-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
199+--
200+-- >>> hSetBuffering stdout LineBuffering
201+
140202------------------------------------------------------------------------------
141203-- Piping
142204------------------------------------------------------------------------------
143205
206+-- | Use a 'Pipe' to transform a stream.
207+--
208+-- /Pre-release/
209+--
144210{-# INLINE_NORMAL transform #-}
145211transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b
146212transform (Pipe pstep1 pstep2 pstate) (Stream step state) =
@@ -197,6 +263,16 @@ foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin))
197263-- Transformation by Mapping
198264------------------------------------------------------------------------------
199265
266+-- |
267+-- >>> sequence = Stream.mapM id
268+--
269+-- Replace the elements of a stream of monadic actions with the outputs of
270+-- those actions.
271+--
272+-- >>> s = Stream.fromList [putStr "a", putStr "b", putStrLn "c"]
273+-- >>> Stream.fold Fold.drain $ Stream.sequence s
274+-- abc
275+--
200276{-# INLINE_NORMAL sequence #-}
201277sequence :: Monad m => Stream m (m a) -> Stream m a
202278sequence (Stream step state) = Stream step' state
@@ -217,6 +293,25 @@ data TapState fs st a
217293 = TapInit | Tapping !fs st | TapDone st
218294
219295-- XXX Multiple yield points
296+
297+-- | Tap the data flowing through a stream into a 'Fold'. For example, you may
298+-- add a tap to log the contents flowing through the stream. The fold is used
299+-- only for effects, its result is discarded.
300+--
301+-- @
302+-- Fold m a b
303+-- |
304+-- -----stream m a ---------------stream m a-----
305+--
306+-- @
307+--
308+-- >>> s = Stream.enumerateFromTo 1 2
309+-- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainMapM print) s
310+-- 1
311+-- 2
312+--
313+-- Compare with 'trace'.
314+--
220315{-# INLINE tap #-}
221316tap :: Monad m => Fold m a b -> Stream m a -> Stream m a
222317tap (Fold fstep initial extract) (Stream step state) = Stream step' TapInit
@@ -301,15 +396,62 @@ tapOffsetEvery offset n (Fold fstep initial extract) (Stream step state) =
301396 Skip s -> Skip (TapOffDone s)
302397 Stop -> Stop
303398
399+-- | Apply a monadic function to each element flowing through the stream and
400+-- discard the results.
401+--
402+-- >>> s = Stream.enumerateFromTo 1 2
403+-- >>> Stream.fold Fold.drain $ Stream.trace print s
404+-- 1
405+-- 2
406+--
407+-- Compare with 'tap'.
408+--
409+{-# INLINE trace #-}
410+trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a
411+trace f = mapM (\x -> void (f x) >> return x)
412+
413+-- | Perform a side effect before yielding each element of the stream and
414+-- discard the results.
415+--
416+-- >>> s = Stream.enumerateFromTo 1 2
417+-- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s
418+-- "got here"
419+-- "got here"
420+--
421+-- Same as 'intersperseMPrefix_' but always serial.
422+--
423+-- See also: 'trace'
424+--
425+-- /Pre-release/
426+{-# INLINE trace_ #-}
427+trace_ :: Monad m => m b -> Stream m a -> Stream m a
428+trace_ eff = mapM (\x -> eff >> return x)
429+
304430------------------------------------------------------------------------------
305431-- Scanning with a Fold
306432------------------------------------------------------------------------------
307433
308434data ScanState s f = ScanInit s | ScanDo s !f | ScanDone
309435
310−{-# INLINE_NORMAL postscanOnce #-}
311−postscanOnce :: Monad m => FL.Fold m a b -> Stream m a -> Stream m b
312−postscanOnce (FL.Fold fstep initial extract) (Stream sstep state) =
436+-- | Postscan a stream using the given monadic fold.
437+--
438+-- The following example extracts the input stream up to a point where the
439+-- running average of elements is no more than 10:
440+--
441+-- >>> import Data.Maybe (fromJust)
442+-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
443+-- >>> s = Stream.enumerateFromTo 1.0 100.0
444+-- >>> :{
445+-- Stream.fold Fold.toList
446+-- $ fmap (fromJust . fst)
447+-- $ Stream.takeWhile (\(_,x) -> x <= 10)
448+-- $ Stream.postscan (Fold.tee Fold.latest avg) s
449+-- :}
450+-- [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]
451+--
452+{-# INLINE_NORMAL postscan #-}
453+postscan :: Monad m => FL.Fold m a b -> Stream m a -> Stream m b
454+postscan (FL.Fold fstep initial extract) (Stream sstep state) =
313455 Stream step (ScanInit state)
314456
315457 where
@@ -364,11 +506,75 @@ scanWith restart (Fold fstep initial extract) (Stream sstep state) =
364506 Stop -> return Stop
365507 step _ ScanDone = return Stop
366508
367−{-# INLINE scanOnce #-}
368−scanOnce :: Monad m
509+-- XXX It may be useful to have a version of scan where we can keep the
510+-- accumulator independent of the value emitted. So that we do not necessarily
511+-- have to keep a value in the accumulator which we are not using. We can pass
512+-- an extraction function that will take the accumulator and the current value
513+-- of the element and emit the next value in the stream. That will also make it
514+-- possible to modify the accumulator after using it. In fact, the step function
515+-- can return new accumulator and the value to be emitted. The signature would
516+-- be more like mapAccumL.
517+
518+-- | Strict left scan. Scan a stream using the given monadic fold.
519+--
520+-- >>> s = Stream.fromList [1..10]
521+-- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s
522+-- [0,1,3,6]
523+--
524+-- See also: 'usingStateT'
525+--
526+
527+-- EXPLANATION:
528+-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)
529+--
530+-- Like 'map', 'scanl'' too is a one to one transformation,
531+-- however it adds an extra element.
532+--
533+-- >>> s = Stream.fromList [1,2,3,4]
534+-- >>> Stream.fold Fold.toList $ scanl' (+) 0 s
535+-- [0,1,3,6,10]
536+--
537+-- >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s
538+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
539+--
540+-- The output of 'scanl'' is the initial value of the accumulator followed by
541+-- all the intermediate steps and the final result of 'foldl''.
542+--
543+-- By streaming the accumulated state after each fold step, we can share the
544+-- state across multiple stages of stream composition. Each stage can modify or
545+-- extend the state, do some processing with it and emit it for the next stage,
546+-- thus modularizing the stream processing. This can be useful in
547+-- stateful or event-driven programming.
548+--
549+-- Consider the following monolithic example, computing the sum and the product
550+-- of the elements in a stream in one go using a @foldl'@:
551+--
552+-- >>> foldl' step z = Stream.fold (Fold.foldl' step z)
553+-- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s
554+-- (10,24)
555+--
556+-- Using @scanl'@ we can make it modular by computing the sum in the first
557+-- stage and passing it down to the next stage for computing the product:
558+--
559+-- >>> :{
560+-- foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)
561+-- $ scanl' (\(s, _) x -> (s + x, x)) (0,1)
562+-- $ Stream.fromList [1,2,3,4]
563+-- :}
564+-- (10,24)
565+--
566+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
567+-- lazy expressions inside the accumulator, it is recommended that a strict
568+-- data structure is used for accumulator.
569+--
570+{-# INLINE scan #-}
571+scan :: Monad m
369572 => FL.Fold m a b -> Stream m a -> Stream m b
370−scanOnce = scanWith False
573+scan = scanWith False
371574
575+-- | Like 'scan' but restarts scanning afresh when the scanning fold
576+-- terminates.
577+--
372578{-# INLINE scanMany #-}
373579scanMany :: Monad m
374580 => FL.Fold m a b -> Stream m a -> Stream m b
@@ -557,6 +763,17 @@ scanlM' fstep begin (Stream step state) = Stream step' Nothing
557763 Skip s -> return $ Skip (Just (s, acc))
558764 Stop -> return Stop
559765
766+-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
767+-- that it provides an additional @done@ function to be applied on the
768+-- accumulator when the stream stops. The result of @done@ is also emitted in
769+-- the stream.
770+--
771+-- This function can be used to allocate a resource in the beginning of the
772+-- scan and release it when the stream ends or to flush the internal state of
773+-- the scan at the end.
774+--
775+-- /Pre-release/
776+--
560777{-# INLINE scanlMAfter' #-}
561778scanlMAfter' :: Monad m
562779 => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b
@@ -644,7 +861,28 @@ scanl1' f = scanl1M' (\x y -> return (f x y))
644861-- Filtering
645862-------------------------------------------------------------------------------
646863
864+-- | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a
865+-- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a
866+-- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with
867+-- index or time.
868+--
869+-- >>> filterWithIndex = Stream.with Stream.indexed Stream.filter
870+--
871+-- /Pre-release/
872+{-# INLINE with #-}
873+with :: Monad m =>
874+ (Stream m a -> Stream m (s, a))
875+ -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))
876+ -> (((s, a) -> b) -> Stream m a -> Stream m a)
877+with f comb g = fmap snd . comb g . f
878+
647879-- Adapted from the vector package
880+
881+-- | Same as 'filter' but with a monadic predicate.
882+--
883+-- >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing
884+-- >>> filterM p = Stream.mapMaybeM (f p)
885+--
648886{-# INLINE_NORMAL filterM #-}
649887filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
650888filterM f (Stream step state) = Stream step' state
@@ -661,13 +899,55 @@ filterM f (Stream step state) = Stream step' state
661899 Skip s -> return $ Skip s
662900 Stop -> return Stop
663901
902+-- | Include only those elements that pass a predicate.
903+--
904+-- >>> filter p = Stream.filterM (return . p)
905+-- >>> filter p = Stream.mapMaybe (\x -> if p x then Just x else Nothing)
906+-- >>> filter p = Stream.scanMaybe (Fold.filtering p)
907+--
664908{-# INLINE filter #-}
665909filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
666910filter f = filterM (return . f)
911+-- filter p = scanMaybe (FL.filtering p)
912+
913+-- | Drop repeated elements that are adjacent to each other using the supplied
914+-- comparison function.
915+--
916+-- >>> uniq = Stream.uniqBy (==)
917+--
918+-- To strip duplicate path separators:
919+--
920+-- >>> input = Stream.fromList "//a//b"
921+-- >>> f x y = x == '/' && y == '/'
922+-- >>> Stream.fold Fold.toList $ Stream.uniqBy f input
923+-- "/a/b"
924+--
925+-- Space: @O(1)@
926+--
927+-- /Pre-release/
928+--
929+{-# INLINE uniqBy #-}
930+uniqBy :: Monad m =>
931+ (a -> a -> Bool) -> Stream m a -> Stream m a
932+-- uniqBy eq = scanMaybe (FL.uniqBy eq)
933+uniqBy eq = catMaybes . rollingMap f
934+
935+ where
936+
937+ f pre curr =
938+ case pre of
939+ Nothing -> Just curr
940+ Just x -> if x `eq` curr then Nothing else Just curr
667941
668942-- Adapted from the vector package
943+
944+-- | Drop repeated elements that are adjacent to each other.
945+--
946+-- >>> uniq = Stream.uniqBy (==)
947+--
669948{-# INLINE_NORMAL uniq #-}
670949uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
950+-- uniq = scanMaybe FL.uniq
671951uniq (Stream step state) = Stream step' (Nothing, state)
672952 where
673953 {-# INLINE_LATE step' #-}
@@ -685,8 +965,16 @@ uniq (Stream step state) = Stream step' (Nothing, state)
685965 Skip s -> return $ Skip (Just x, s)
686966 Stop -> return Stop
687967
968+-- | Deletes the first occurrence of the element in the stream that satisfies
969+-- the given equality predicate.
970+--
971+-- >>> input = Stream.fromList [1,3,3,5]
972+-- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input
973+-- [1,3,5]
974+--
688975{-# INLINE_NORMAL deleteBy #-}
689976deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
977+-- deleteBy cmp x = scanMaybe (FL.deleteBy cmp x)
690978deleteBy eq x (Stream step state) = Stream step' (state, False)
691979 where
692980 {-# INLINE_LATE step' #-}
@@ -705,11 +993,69 @@ deleteBy eq x (Stream step state) = Stream step' (state, False)
705993 Skip s -> return $ Skip (s, True)
706994 Stop -> return Stop
707995
996+-- | Strip all leading and trailing occurrences of an element passing a
997+-- predicate and make all other consecutive occurrences uniq.
998+--
999+-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)
1000+--
1001+-- @
1002+-- > Stream.prune isSpace (Stream.fromList " hello world! ")
1003+-- "hello world!"
1004+--
1005+-- @
1006+--
1007+-- Space: @O(1)@
1008+--
1009+-- /Unimplemented/
1010+{-# INLINE prune #-}
1011+prune ::
1012+ -- (Monad m, Eq a) =>
1013+ (a -> Bool) -> Stream m a -> Stream m a
1014+prune = error "Not implemented yet!"
1015+
1016+-- Possible implementation:
1017+-- @repeated =
1018+-- Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@
1019+--
1020+-- 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a
1021+-- single element.
1022+
1023+-- | Emit only repeated elements, once.
1024+--
1025+-- /Unimplemented/
1026+repeated :: -- (Monad m, Eq a) =>
1027+ Stream m a -> Stream m a
1028+repeated = undefined
1029+
7081030------------------------------------------------------------------------------
7091031-- Trimming
7101032------------------------------------------------------------------------------
7111033
1034+-- | Take all consecutive elements at the end of the stream for which the
1035+-- predicate is true.
1036+--
1037+-- O(n) space, where n is the number elements taken.
1038+--
1039+-- /Unimplemented/
1040+{-# INLINE takeWhileLast #-}
1041+takeWhileLast :: -- Monad m =>
1042+ (a -> Bool) -> Stream m a -> Stream m a
1043+takeWhileLast = undefined -- fromStreamD $ D.takeWhileLast n $ toStreamD m
1044+
1045+-- | Like 'takeWhile' and 'takeWhileLast' combined.
1046+--
1047+-- O(n) space, where n is the number elements taken from the end.
1048+--
1049+-- /Unimplemented/
1050+{-# INLINE takeWhileAround #-}
1051+takeWhileAround :: -- Monad m =>
1052+ (a -> Bool) -> Stream m a -> Stream m a
1053+takeWhileAround = undefined -- fromStreamD $ D.takeWhileAround n $ toStreamD m
1054+
7121055-- Adapted from the vector package
1056+
1057+-- | Discard first 'n' elements from the stream and take the rest.
1058+--
7131059{-# INLINE_NORMAL drop #-}
7141060drop :: Monad m => Int -> Stream m a -> Stream m a
7151061drop n (Stream step state) = Stream step' (state, Just n)
@@ -739,8 +1085,11 @@ data DropWhileState s a
7391085 | DropWhileYield a s
7401086 | DropWhileNext s
7411087
1088+-- | Same as 'dropWhile' but with a monadic predicate.
1089+--
7421090{-# INLINE_NORMAL dropWhileM #-}
7431091dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
1092+-- dropWhileM p = scanMaybe (FL.droppingWhileM p)
7441093dropWhileM f (Stream step state) = Stream step' (DropWhileDrop state)
7451094 where
7461095 {-# INLINE_LATE step' #-}
@@ -764,14 +1113,58 @@ dropWhileM f (Stream step state) = Stream step' (DropWhileDrop state)
7641113
7651114 step' _ (DropWhileYield x st) = return $ Yield x (DropWhileNext st)
7661115
1116+-- | Drop elements in the stream as long as the predicate succeeds and then
1117+-- take the rest of the stream.
1118+--
7671119{-# INLINE dropWhile #-}
7681120dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
1121+-- dropWhile p = scanMaybe (FL.droppingWhile p)
7691122dropWhile f = dropWhileM (return . f)
7701123
1124+-- | Drop @n@ elements at the end of the stream.
1125+--
1126+-- O(n) space, where n is the number elements dropped.
1127+--
1128+-- /Unimplemented/
1129+{-# INLINE dropLast #-}
1130+dropLast :: -- Monad m =>
1131+ Int -> Stream m a -> Stream m a
1132+dropLast = undefined -- fromStreamD $ D.dropLast n $ toStreamD m
1133+
1134+-- | Drop all consecutive elements at the end of the stream for which the
1135+-- predicate is true.
1136+--
1137+-- O(n) space, where n is the number elements dropped.
1138+--
1139+-- /Unimplemented/
1140+{-# INLINE dropWhileLast #-}
1141+dropWhileLast :: -- Monad m =>
1142+ (a -> Bool) -> Stream m a -> Stream m a
1143+dropWhileLast = undefined -- fromStreamD $ D.dropWhileLast n $ toStreamD m
1144+
1145+-- | Like 'dropWhile' and 'dropWhileLast' combined.
1146+--
1147+-- O(n) space, where n is the number elements dropped from the end.
1148+--
1149+-- /Unimplemented/
1150+{-# INLINE dropWhileAround #-}
1151+dropWhileAround :: -- Monad m =>
1152+ (a -> Bool) -> Stream m a -> Stream m a
1153+dropWhileAround = undefined -- fromStreamD $ D.dropWhileAround n $ toStreamD m
1154+
7711155------------------------------------------------------------------------------
7721156-- Inserting Elements
7731157------------------------------------------------------------------------------
7741158
1159+-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
1160+-- @stream@ that is less than @elem@ when compared using @cmp@.
1161+--
1162+-- >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)
1163+--
1164+-- >>> input = Stream.fromList [1,3,5]
1165+-- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input
1166+-- [1,2,3,5]
1167+--
7751168{-# INLINE_NORMAL insertBy #-}
7761169insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
7771170insertBy cmp a (Stream step state) = Stream step' (state, False, Nothing)
@@ -799,6 +1192,22 @@ data LoopState x s = FirstYield s
7991192 | InterspersingYield s
8001193 | YieldAndCarry x s
8011194
1195+-- intersperseM = intersperseMWith 1
1196+
1197+-- | Insert an effect and its output before consuming an element of a stream
1198+-- except the first one.
1199+--
1200+-- >>> input = Stream.fromList "hello"
1201+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input
1202+-- h.,e.,l.,l.,o"h,e,l,l,o"
1203+--
1204+-- Be careful about the order of effects. In the above example we used trace
1205+-- after the intersperse, if we use it before the intersperse the output would
1206+-- be he.l.l.o."h,e,l,l,o".
1207+--
1208+-- >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input
1209+-- he.l.l.o."h,e,l,l,o"
1210+--
8021211{-# INLINE_NORMAL intersperseM #-}
8031212intersperseM :: Monad m => m a -> Stream m a -> Stream m a
8041213intersperseM m (Stream step state) = Stream step' (FirstYield state)
@@ -823,10 +1232,24 @@ intersperseM m (Stream step state) = Stream step' (FirstYield state)
8231232
8241233 step' _ (YieldAndCarry x st) = return $ Yield x (InterspersingYield st)
8251234
1235+-- | Insert a pure value between successive elements of a stream.
1236+--
1237+-- >>> input = Stream.fromList "hello"
1238+-- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input
1239+-- "h,e,l,l,o"
1240+--
8261241{-# INLINE intersperse #-}
8271242intersperse :: Monad m => a -> Stream m a -> Stream m a
8281243intersperse a = intersperseM (return a)
8291244
1245+-- | Insert a side effect before consuming an element of a stream except the
1246+-- first one.
1247+--
1248+-- >>> input = Stream.fromList "hello"
1249+-- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input
1250+-- h.e.l.l.o
1251+--
1252+-- /Pre-release/
8301253{-# INLINE_NORMAL intersperseM_ #-}
8311254intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a
8321255intersperseM_ m (Stream step1 state1) = Stream step (Left (pure (), state1))
@@ -841,11 +1264,31 @@ intersperseM_ m (Stream step1 state1) = Stream step (Left (pure (), state1))
8411264
8421265 step _ (Right st) = return $ Skip $ Left (void m, st)
8431266
1267+-- | Intersperse a monadic action into the input stream after every @n@
1268+-- elements.
1269+--
1270+-- >> input = Stream.fromList "hello"
1271+-- >> Stream.fold Fold.toList $ Stream.intersperseMWith 2 (return ',') input
1272+-- "he,ll,o"
1273+--
1274+-- /Unimplemented/
1275+{-# INLINE intersperseMWith #-}
1276+intersperseMWith :: -- Monad m =>
1277+ Int -> m a -> Stream m a -> Stream m a
1278+intersperseMWith _n _f _xs = undefined
1279+
8441280data SuffixState s a
8451281 = SuffixElem s
8461282 | SuffixSuffix s
8471283 | SuffixYield a (SuffixState s a)
8481284
1285+-- | Insert an effect and its output after consuming an element of a stream.
1286+--
1287+-- >>> input = Stream.fromList "hello"
1288+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMSuffix (putChar '.' >> return ',') input
1289+-- h.,e.,l.,l.,o.,"h,e,l,l,o,"
1290+--
1291+-- /Pre-release/
8491292{-# INLINE_NORMAL intersperseMSuffix #-}
8501293intersperseMSuffix :: forall m a. Monad m => m a -> Stream m a -> Stream m a
8511294intersperseMSuffix action (Stream step state) = Stream step' (SuffixElem state)
@@ -863,6 +1306,14 @@ intersperseMSuffix action (Stream step state) = Stream step' (SuffixElem state)
8631306
8641307 step' _ (SuffixYield x next) = return $ Yield x next
8651308
1309+-- | Insert a side effect after consuming an element of a stream.
1310+--
1311+-- >>> input = Stream.fromList "hello"
1312+-- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffix_ (threadDelay 1000000) input
1313+-- "hello"
1314+--
1315+-- /Pre-release/
1316+--
8661317{-# INLINE_NORMAL intersperseMSuffix_ #-}
8671318intersperseMSuffix_ :: Monad m => m b -> Stream m a -> Stream m a
8681319intersperseMSuffix_ m (Stream step1 state1) = Stream step (Left state1)
@@ -884,7 +1335,15 @@ data SuffixSpanState s a
8841335 | SuffixSpanLast
8851336 | SuffixSpanStop
8861337
887−-- | intersperse after every n items
1338+-- | Like 'intersperseMSuffix' but intersperses an effectful action into the
1339+-- input stream after every @n@ elements and after the last element.
1340+--
1341+-- >>> input = Stream.fromList "hello"
1342+-- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffixWith 2 (return ',') input
1343+-- "he,ll,o,"
1344+--
1345+-- /Pre-release/
1346+--
8881347{-# INLINE_NORMAL intersperseMSuffixWith #-}
8891348intersperseMSuffixWith :: forall m a. Monad m
8901349 => Int -> m a -> Stream m a -> Stream m a
@@ -910,21 +1369,113 @@ intersperseMSuffixWith n action (Stream step state) =
9101369
9111370 step' _ SuffixSpanStop = return Stop
9121371
1372+-- | Insert a side effect before consuming an element of a stream.
1373+--
1374+-- Definition:
1375+--
1376+-- >>> intersperseMPrefix_ m = Stream.mapM (\x -> void m >> return x)
1377+--
1378+-- >>> input = Stream.fromList "hello"
1379+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMPrefix_ (putChar '.' >> return ',') input
1380+-- .h.e.l.l.o"hello"
1381+--
1382+-- Same as 'trace_'.
1383+--
1384+-- /Pre-release/
1385+--
1386+{-# INLINE intersperseMPrefix_ #-}
1387+intersperseMPrefix_ :: Monad m => m b -> Stream m a -> Stream m a
1388+intersperseMPrefix_ m = mapM (\x -> void m >> return x)
1389+
1390+------------------------------------------------------------------------------
1391+-- Inserting Time
1392+------------------------------------------------------------------------------
1393+
1394+-- XXX This should be in Prelude, should we export this as a helper function?
1395+
1396+-- | Block the current thread for specified number of seconds.
1397+{-# INLINE sleep #-}
1398+sleep :: MonadIO m => Double -> m ()
1399+sleep n = liftIO $ threadDelay $ round $ n * 1000000
1400+
1401+-- | Introduce a delay of specified seconds between elements of the stream.
1402+--
1403+-- Definition:
1404+--
1405+-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000
1406+-- >>> delay = Stream.intersperseM_ . sleep
1407+--
1408+-- Example:
1409+--
1410+-- >>> input = Stream.enumerateFromTo 1 3
1411+-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delay 1 input
1412+-- 1
1413+-- 2
1414+-- 3
1415+--
1416+{-# INLINE delay #-}
1417+delay :: MonadIO m => Double -> Stream m a -> Stream m a
1418+delay = intersperseM_ . sleep
1419+
1420+-- | Introduce a delay of specified seconds after consuming an element of a
1421+-- stream.
1422+--
1423+-- Definition:
1424+--
1425+-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000
1426+-- >>> delayPost = Stream.intersperseMSuffix_ . sleep
1427+--
1428+-- Example:
1429+--
1430+-- >>> input = Stream.enumerateFromTo 1 3
1431+-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPost 1 input
1432+-- 1
1433+-- 2
1434+-- 3
1435+--
1436+-- /Pre-release/
1437+--
1438+{-# INLINE delayPost #-}
1439+delayPost :: MonadIO m => Double -> Stream m a -> Stream m a
1440+delayPost n = intersperseMSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000
1441+
1442+-- | Introduce a delay of specified seconds before consuming an element of a
1443+-- stream.
1444+--
1445+-- Definition:
1446+--
1447+-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000
1448+-- >>> delayPre = Stream.intersperseMPrefix_. sleep
1449+--
1450+-- Example:
1451+--
1452+-- >>> input = Stream.enumerateFromTo 1 3
1453+-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPre 1 input
1454+-- 1
1455+-- 2
1456+-- 3
1457+--
1458+-- /Pre-release/
1459+--
1460+{-# INLINE delayPre #-}
1461+delayPre :: MonadIO m => Double -> Stream m a -> Stream m a
1462+delayPre = intersperseMPrefix_. sleep
1463+
9131464------------------------------------------------------------------------------
9141465-- Reordering
9151466------------------------------------------------------------------------------
9161467
917−-- We can implement reverse as:
1468+-- | Returns the elements of the stream in reverse order. The stream must be
1469+-- finite. Note that this necessarily buffers the entire stream in memory.
9181470--
919−-- > reverse = foldlS (flip cons) nil
1471+-- Definition:
9201472--
921−-- However, this implementation is unusable because of the horrible performance
922−-- of cons. So we just convert it to a list first and then stream from the
923−-- list.
1473+-- >>> reverse m = Stream.concatEffect $ Stream.fold Fold.toListRev m >>= return . Stream.fromList
9241474--
925−-- XXX Maybe we can use an Array instead of a list here?
9261475{-# INLINE_NORMAL reverse #-}
9271476reverse :: Monad m => Stream m a -> Stream m a
1477+reverse m = concatEffect $ fold FL.toListRev m <&> fromList
1478+{-
9281479reverse m = Stream step Nothing
9291480 where
9301481 {-# INLINE_LATE step #-}
@@ -933,47 +1484,57 @@ reverse m = Stream step Nothing
9331484 return $ Skip (Just xs)
9341485 step _ (Just (x:xs)) = return $ Yield x (Just xs)
9351486 step _ (Just []) = return Stop
936−
937−-- Much faster reverse for Storables
938−{-
939−{-# INLINE_NORMAL reverse' #-}
940−reverse' :: forall m a. (MonadIO m, Storable a) => Stream m a -> Stream m a
941−-- This commented implementation copies the whole stream into one single array
942−-- and then streams from that array, this has exactly the same performance as
943−-- the chunked code in IsStream.Common.reverse' . Though this could be problematic due to
944−-- unbounded large allocations. However, if we use an idiomatic implementation
945−-- of arraysOf instead of the custom implementation then the chunked code
946−-- becomes worse by 6 times. Need to investigate if that can be improved.
947−import Foreign.ForeignPtr (touchForeignPtr)
948−import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
949−import Foreign.Ptr (Ptr, plusPtr)
950−import Streamly.Internal.Data.Array.Mut.Type (sizeOfElem)
951−reverse' m = Stream step Nothing
952− where
953− {-# INLINE_LATE step #-}
954− step _ Nothing = do
955− arr <- A.fromStreamD m
956− let p = A.arrEnd arr `plusPtr` negate (sizeOfElem (undefined :: a))
957− return $ Skip $ Just (A.aStart arr, p)
958−
959− step _ (Just (start, p)) | p < unsafeForeignPtrToPtr start = return Stop
960−
961− step _ (Just (start, p)) = do
962− let !x = A.unsafeInlineIO $ do
963− r <- peek p
964− touchForeignPtr start
965− return r
966− next = p `plusPtr` negate (sizeOfElem (undefined :: a))
967− return $ Yield x (Just (start, next))
9681487-}
9691488
1489+-- | Like 'reverse' but several times faster, requires an 'Unbox' instance.
1490+--
1491+-- /O(n) space/
1492+--
1493+-- /Pre-release/
1494+{-# INLINE reverseUnbox #-}
1495+reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
1496+reverseUnbox =
1497+ A.flattenArraysRev -- unfoldMany A.readRev
1498+ . fromStreamK
1499+ . K.reverse
1500+ . toStreamK
1501+ . A.arraysOf defaultChunkSize
1502+
1503+-- | Buffer until the next element in sequence arrives. The function argument
1504+-- determines the difference in sequence numbers. This could be useful in
1505+-- implementing sequenced streams, for example, TCP reassembly.
1506+--
1507+-- /Unimplemented/
1508+--
1509+{-# INLINE reassembleBy #-}
1510+reassembleBy
1511+ :: -- Monad m =>
1512+ Fold m a b
1513+ -> (a -> a -> Int)
1514+ -> Stream m a
1515+ -> Stream m b
1516+reassembleBy = undefined
1517+
9701518------------------------------------------------------------------------------
9711519-- Position Indexing
9721520------------------------------------------------------------------------------
9731521
9741522-- Adapted from the vector package
1523+
1524+-- |
1525+-- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
1526+-- >>> indexed = Stream.postscan f
1527+-- >>> indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)
1528+-- >>> indexedR n = fmap (\(i, a) -> (n - i, a)) . indexed
1529+--
1530+-- Pair each element in a stream with its index, starting from index 0.
1531+--
1532+-- >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.fromList "hello"
1533+-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
1534+--
9751535{-# INLINE_NORMAL indexed #-}
9761536indexed :: Monad m => Stream m a -> Stream m (Int, a)
1537+-- indexed = scanMaybe FL.indexing
9771538indexed (Stream step state) = Stream step' (state, 0)
9781539 where
9791540 {-# INLINE_LATE step' #-}
@@ -985,8 +1546,23 @@ indexed (Stream step state) = Stream step' (state, 0)
9851546 Stop -> return Stop
9861547
9871548-- Adapted from the vector package
1549+
1550+-- |
1551+-- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
1552+-- >>> indexedR n = Stream.postscan (f n)
1553+--
1554+-- >>> s n = Stream.enumerateFromThen n (n - 1)
1555+-- >>> indexedR n = Stream.zipWith (,) (s n)
1556+--
1557+-- Pair each element in a stream with its index, starting from the
1558+-- given index @n@ and counting down.
1559+--
1560+-- >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.fromList "hello"
1561+-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
1562+--
9881563{-# INLINE_NORMAL indexedR #-}
9891564indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
1565+-- indexedR n = scanMaybe (FL.indexingRev n)
9901566indexedR m (Stream step state) = Stream step' (state, m)
9911567 where
9921568 {-# INLINE_LATE step' #-}
@@ -998,10 +1574,77 @@ indexedR m (Stream step state) = Stream step' (state, m)
9981574 Skip s -> return $ Skip (s, i)
9991575 Stop -> return Stop
10001576
1577+-------------------------------------------------------------------------------
1578+-- Time Indexing
1579+-------------------------------------------------------------------------------
1580+
1581+-- Note: The timestamp stream must be the second stream in the zip so that the
1582+-- timestamp is generated after generating the stream element and not before.
1583+-- If we do not do that then the following example will generate the same
1584+-- timestamp for first two elements:
1585+--
1586+-- Stream.fold Fold.toList $ Stream.timestamped $ Stream.delay $ Stream.enumerateFromTo 1 3
1587+
1588+-- | Pair each element in a stream with an absolute timestamp, using a clock of
1589+-- specified granularity. The timestamp is generated just before the element
1590+-- is consumed.
1591+--
1592+-- >>> Stream.fold Fold.toList $ Stream.timestampWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1593+-- [(AbsTime (TimeSpec {sec = ..., nsec = ...}),1),(AbsTime (TimeSpec {sec = ..., nsec = ...}),2),(AbsTime (TimeSpec {sec = ..., nsec = ...}),3)]
1594+--
1595+-- /Pre-release/
1596+--
1597+{-# INLINE timestampWith #-}
1598+timestampWith :: (MonadIO m)
1599+ => Double -> Stream m a -> Stream m (AbsTime, a)
1600+timestampWith g stream = zipWith (flip (,)) stream (absTimesWith g)
1601+
1602+-- TBD: check performance vs a custom implementation without using zipWith.
1603+--
1604+-- /Pre-release/
1605+--
1606+{-# INLINE timestamped #-}
1607+timestamped :: (MonadIO m)
1608+ => Stream m a -> Stream m (AbsTime, a)
1609+timestamped = timestampWith 0.01
1610+
1611+-- | Pair each element in a stream with relative times starting from 0, using a
1612+-- clock with the specified granularity. The time is measured just before the
1613+-- element is consumed.
1614+--
1615+-- >>> Stream.fold Fold.toList $ Stream.timeIndexWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1616+-- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]
1617+--
1618+-- /Pre-release/
1619+--
1620+{-# INLINE timeIndexWith #-}
1621+timeIndexWith :: (MonadIO m)
1622+ => Double -> Stream m a -> Stream m (RelTime64, a)
1623+timeIndexWith g stream = zipWith (flip (,)) stream (relTimesWith g)
1624+
1625+-- | Pair each element in a stream with relative times starting from 0, using a
1626+-- 10 ms granularity clock. The time is measured just before the element is
1627+-- consumed.
1628+--
1629+-- >>> Stream.fold Fold.toList $ Stream.timeIndexed $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1630+-- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]
1631+--
1632+-- /Pre-release/
1633+--
1634+{-# INLINE timeIndexed #-}
1635+timeIndexed :: (MonadIO m)
1636+ => Stream m a -> Stream m (RelTime64, a)
1637+timeIndexed = timeIndexWith 0.01
1638+
10011639------------------------------------------------------------------------------
10021640-- Searching
10031641------------------------------------------------------------------------------
10041642
1643+-- | Find all the indices where the element in the stream satisfies the given
1644+-- predicate.
1645+--
1646+-- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
1647+--
10051648{-# INLINE_NORMAL findIndices #-}
10061649findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
10071650findIndices p (Stream step state) = Stream step' (state, 0)
@@ -1014,6 +1657,15 @@ findIndices p (Stream step state) = Stream step' (state, 0)
10141657 Skip s -> Skip (s, i)
10151658 Stop -> Stop
10161659
1660+-- | Find all the indices where the value of the element in the stream is equal
1661+-- to the given value.
1662+--
1663+-- >>> elemIndices a = Stream.findIndices (== a)
1664+--
1665+{-# INLINE elemIndices #-}
1666+elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
1667+elemIndices a = findIndices (== a)
1668+
10171669{-# INLINE_NORMAL slicesBy #-}
10181670slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
10191671slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
@@ -1039,8 +1691,13 @@ slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
10391691
10401692data RollingMapState s a = RollingMapGo s a
10411693
1694+-- | Like 'rollingMap' but with an effectful map function.
1695+--
1696+-- /Pre-release/
1697+--
10421698{-# INLINE rollingMapM #-}
10431699rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
1700+-- rollingMapM f = scanMaybe (FL.slide2 $ Window.rollingMapM f)
10441701rollingMapM f (Stream step1 state1) = Stream step (RollingMapGo state1 Nothing)
10451702
10461703 where
@@ -1054,16 +1711,31 @@ rollingMapM f (Stream step1 state1) = Stream step (RollingMapGo state1 Nothing)
10541711 Skip s -> return $ Skip $ RollingMapGo s curr
10551712 Stop -> return Stop
10561713
1057−-- | rollingMap is a special case of an incremental sliding fold. It can be
1714+-- rollingMap is a special case of an incremental sliding fold. It can be
10581715-- written as:
10591716--
10601717-- > fld f = slidingWindow 1 (Fold.foldl' (\_ (x,y) -> f y x)
10611718-- > rollingMap f = Stream.postscan (fld f) undefined
1719+
1720+-- | Apply a function on every two successive elements of a stream. The first
1721+-- argument of the map function is the previous element and the second argument
1722+-- is the current element. When the current element is the first element, the
1723+-- previous element is 'Nothing'.
1724+--
1725+-- /Pre-release/
10621726--
10631727{-# INLINE rollingMap #-}
10641728rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
1729+-- rollingMap f = scanMaybe (FL.slide2 $ Window.rollingMap f)
10651730rollingMap f = rollingMapM (\x y -> return $ f x y)
10661731
1732+-- | Like 'rollingMap' but requires at least two elements in the stream,
1733+-- returns an empty stream otherwise.
1734+--
1735+-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.
1736+--
1737+-- /Pre-release/
1738+--
10671739{-# INLINE rollingMap2 #-}
10681740rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b
10691741rollingMap2 f = catMaybes . rollingMap g
@@ -1078,14 +1750,37 @@ rollingMap2 f = catMaybes . rollingMap g
10781750------------------------------------------------------------------------------
10791751
10801752-- XXX Will this always fuse properly?
1753+
1754+-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
1755+-- elements, and return a stream of values extracted from 'Just'.
1756+--
1757+-- Equivalent to:
1758+--
1759+-- >>> mapMaybe f = Stream.catMaybes . fmap f
1760+--
10811761{-# INLINE_NORMAL mapMaybe #-}
10821762mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
10831763mapMaybe f = fmap fromJust . filter isJust . map f
10841764
1765+-- | Like 'mapMaybe' but maps a monadic function.
1766+--
1767+-- Equivalent to:
1768+--
1769+-- >>> mapMaybeM f = Stream.catMaybes . Stream.mapM f
1770+--
1771+-- >>> mapM f = Stream.mapMaybeM (\x -> Just <$> f x)
1772+--
10851773{-# INLINE_NORMAL mapMaybeM #-}
10861774mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b
10871775mapMaybeM f = fmap fromJust . filter isJust . mapM f
10881776
1777+-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
1778+--
1779+-- >>> catMaybes = Stream.mapMaybe id
1780+-- >>> catMaybes = fmap fromJust . Stream.filter isJust
1781+--
1782+-- /Pre-release/
1783+--
10891784{-# INLINE catMaybes #-}
10901785catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
10911786-- catMaybes = fmap fromJust . filter isJust
@@ -1107,7 +1802,43 @@ catMaybes (Stream step state) = Stream step1 state
11071802
11081803-- | Use a filtering fold on a stream.
11091804--
1110−-- /Pre-release/
1805+-- >>> scanMaybe f = Stream.catMaybes . Stream.postscan f
1806+--
11111807{-# INLINE scanMaybe #-}
11121808scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
1113−scanMaybe f = catMaybes . postscanOnce f
1809+scanMaybe f = catMaybes . postscan f
1810+
1811+------------------------------------------------------------------------------
1812+-- Either streams
1813+------------------------------------------------------------------------------
1814+
1815+-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
1816+--
1817+-- >>> catLefts = fmap (fromLeft undefined) . Stream.filter isLeft
1818+--
1819+-- /Pre-release/
1820+--
1821+{-# INLINE catLefts #-}
1822+catLefts :: Monad m => Stream m (Either a b) -> Stream m a
1823+catLefts = fmap (fromLeft undefined) . filter isLeft
1824+
1825+-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
1826+--
1827+-- >>> catRights = fmap (fromRight undefined) . Stream.filter isRight
1828+--
1829+-- /Pre-release/
1830+--
1831+{-# INLINE catRights #-}
1832+catRights :: Monad m => Stream m (Either a b) -> Stream m b
1833+catRights = fmap (fromRight undefined) . filter isRight
1834+
1835+-- | Remove the either wrapper and flatten both lefts and as well as rights in
1836+-- the output stream.
1837+--
1838+-- >>> catEithers = fmap (either id id)
1839+--
1840+-- /Pre-release/
1841+--
1842+{-# INLINE catEithers #-}
1843+catEithers :: Monad m => Stream m (Either a a) -> Stream m a
1844+catEithers = fmap (either id id)
core/src/Streamly/Internal/Data/Stream/StreamD/Transformer.hs+68−2
@@ -16,8 +16,10 @@ module Streamly.Internal.Data.Stream.StreamD.Transformer
1616 -- * Transform Inner Monad
1717 , liftInner
1818 , runReaderT
19+ , usingReaderT
1920 , evalStateT
2021 , runStateT
22+ , usingStateT
2123 )
2224where
2325
@@ -34,6 +36,14 @@ import qualified Control.Monad.Trans.State.Strict as State
3436
3537import Streamly.Internal.Data.Stream.StreamD.Type
3638
39+-- $setup
40+-- >>> :m
41+-- >>> import Control.Monad.Trans.Class (lift)
42+-- >>> import Control.Monad.Trans.Identity (runIdentityT)
43+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
44+
45+-- | Lazy left fold to a transformer monad.
46+--
3747{-# INLINE_NORMAL foldlT #-}
3848foldlT :: (Monad m, Monad (s m), MonadTrans s)
3949 => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
@@ -46,9 +56,19 @@ foldlT fstep begin (Stream step state) = go SPEC begin state
4656 Skip s -> go SPEC acc s
4757 Stop -> acc
4858
49−-- Right fold to some transformer (T) monad. This can be useful to implement
50−-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
59+-- | Right fold to a transformer monad. This is the most general right fold
60+-- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'
61+-- implementation can be more efficient:
62+--
63+-- >>> foldrS = Stream.foldrT
64+--
65+-- >>> step f x xs = lift $ f x (runIdentityT xs)
66+-- >>> foldrM f z s = runIdentityT $ Stream.foldrT (step f) (lift z) s
67+--
68+-- 'foldrT' can be used to translate streamly streams to other transformer
69+-- monads e.g. to a different streaming type.
5170--
71+-- /Pre-release/
5272{-# INLINE_NORMAL foldrT #-}
5373foldrT :: (Monad m, Monad (t m), MonadTrans t)
5474 => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
@@ -66,6 +86,9 @@ foldrT f final (Stream step state) = go SPEC state
6686-- Transform Inner Monad
6787-------------------------------------------------------------------------------
6888
89+-- | Lift the inner monad @m@ of @Stream m a@ to @t m@ where @t@ is a monad
90+-- transformer.
91+--
6992{-# INLINE_NORMAL liftInner #-}
7093liftInner :: (Monad m, MonadTrans t, Monad (t m))
7194 => Stream m a -> Stream (t m) a
@@ -79,6 +102,12 @@ liftInner (Stream step state) = Stream step' state
79102 Skip s -> Skip s
80103 Stop -> Stop
81104
105+------------------------------------------------------------------------------
106+-- Sharing read only state in a stream
107+------------------------------------------------------------------------------
108+
109+-- | Evaluate the inner monad of a stream as 'ReaderT'.
110+--
82111{-# INLINE_NORMAL runReaderT #-}
83112runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a
84113runReaderT env (Stream step state) = Stream step' (state, env)
@@ -92,6 +121,25 @@ runReaderT env (Stream step state) = Stream step' (state, env)
92121 Skip s -> Skip (s, return sv)
93122 Stop -> Stop
94123
124+-- | Run a stream transformation using a given environment.
125+--
126+{-# INLINE usingReaderT #-}
127+usingReaderT
128+ :: Monad m
129+ => m r
130+ -> (Stream (ReaderT r m) a -> Stream (ReaderT r m) a)
131+ -> Stream m a
132+ -> Stream m a
133+usingReaderT r f xs = runReaderT r $ f $ liftInner xs
134+
135+------------------------------------------------------------------------------
136+-- Sharing read write state in a stream
137+------------------------------------------------------------------------------
138+
139+-- | Evaluate the inner monad of a stream as 'StateT'.
140+--
141+-- >>> evalStateT s = fmap snd . Stream.runStateT s
142+--
95143{-# INLINE_NORMAL evalStateT #-}
96144evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a
97145evalStateT initial (Stream step state) = Stream step' (state, initial)
@@ -105,6 +153,9 @@ evalStateT initial (Stream step state) = Stream step' (state, initial)
105153 Skip s -> Skip (s, return sv')
106154 Stop -> Stop
107155
156+-- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting
157+-- state and value pair after each step.
158+--
108159{-# INLINE_NORMAL runStateT #-}
109160runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)
110161runStateT initial (Stream step state) = Stream step' (state, initial)
@@ -117,3 +168,18 @@ runStateT initial (Stream step state) = Stream step' (state, initial)
117168 Yield x s -> Yield (sv', x) (s, return sv')
118169 Skip s -> Skip (s, return sv')
119170 Stop -> Stop
171+
172+-- | Run a stateful (StateT) stream transformation using a given state.
173+--
174+-- >>> usingStateT s f = Stream.evalStateT s . f . Stream.liftInner
175+--
176+-- See also: 'scan'
177+--
178+{-# INLINE usingStateT #-}
179+usingStateT
180+ :: Monad m
181+ => m s
182+ -> (Stream (StateT s m) a -> Stream (StateT s m) a)
183+ -> Stream m a
184+ -> Stream m a
185+usingStateT s f = evalStateT s . f . liftInner
core/src/Streamly/Internal/Data/Stream/StreamD/Type.hs+744−64
@@ -1,4 +1,4 @@
1−#include "inline.hs"
1+{-# LANGUAGE UndecidableInstances #-}
22
33-- |
44-- Module : Streamly.Internal.Data.Stream.StreamD.Type
@@ -17,127 +17,172 @@ module Streamly.Internal.Data.Stream.StreamD.Type
1717 (
1818 -- * The stream type
1919 Step (..)
20− -- XXX UnStream is exported to avoid a performance issue in concatMap if we
21− -- use the pattern synonym "Stream".
20+ -- XXX UnStream is exported to avoid a performance issue in some
21+ -- combinators if we use the pattern synonym "Stream".
2222 , Stream (Stream, UnStream)
2323
24− -- * Primitives
25− , nilM
26− , consM
27− , uncons
24+ -- * CrossStream type wrapper
25+ , CrossStream
26+ , unCross
27+ , mkCross
28+
29+ -- * Conversion to StreamK
30+ , fromStreamK
31+ , toStreamK
2832
2933 -- * From Unfold
3034 , unfold
3135
32− -- * From Values
36+ -- * Construction
37+ -- ** Primitives
38+ , nilM
39+ , consM
40+
41+ -- ** From Values
3342 , fromPure
3443 , fromEffect
3544
36− -- * From Containers
37− , fromList
45+ -- ** From Containers
46+ , Streamly.Internal.Data.Stream.StreamD.Type.fromList
3847
39− -- * Conversions From/To
40− , fromStreamK
41− , toStreamK
48+ -- * Elimination
49+ -- ** Primitives
50+ , uncons
4251
43− -- * Running a 'Fold'
44− , fold
52+ -- ** Strict Left Folds
53+ , Streamly.Internal.Data.Stream.StreamD.Type.fold
4554 , foldBreak
46− , foldContinue
55+ , foldAddLazy
56+ , foldAdd
4757 , foldEither
4858
49− -- * Right Folds
50− , foldrM
51− , foldrMx
52− , foldr
53− , foldrS
54−
55− -- * Left Folds
56− , foldl'
59+ , Streamly.Internal.Data.Stream.StreamD.Type.foldl'
5760 , foldlM'
5861 , foldlx'
5962 , foldlMx'
6063
61− -- * Special Folds
62− , drain
63−
64− -- * To Containers
65− , toList
64+ -- ** Lazy Right Folds
65+ , foldrM
66+ , foldrMx
67+ , Streamly.Internal.Data.Stream.StreamD.Type.foldr
68+ , foldrS
6669
67− -- * Multi-stream folds
68− , eqBy
69− , cmpBy
70+ -- ** Specific Folds
71+ , drain
72+ , Streamly.Internal.Data.Stream.StreamD.Type.toList
7073
71− -- * Transformations
74+ -- * Mapping
7275 , map
7376 , mapM
77+
78+ -- * Stateful Filters
7479 , take
7580 , takeWhile
7681 , takeWhileM
7782 , takeEndBy
7883 , takeEndByM
7984
80− -- * Nesting
85+ -- * Combining Two Streams
86+ -- ** Zipping
87+ , zipWithM
88+ , zipWith
89+
90+ -- ** Cross Product
8191 , crossApply
8292 , crossApplyFst
8393 , crossApplySnd
94+ , crossWith
95+ , cross
8496
97+ -- * Unfold Many
8598 , ConcatMapUState (..)
8699 , unfoldMany
87100
101+ -- * Concat
102+ , concatEffect
88103 , concatMap
89104 , concatMapM
90− , concatEffect
105+ , concat
91106
92− -- * Expanding trees top down
107+ -- * Unfold Iterate
93108 , unfoldIterateDfs
94109 , unfoldIterateBfs
95110 , unfoldIterateBfsRev
96111
112+ -- * Concat Iterate
97113 , concatIterateScan
98114 , concatIterateDfs
99115 , concatIterateBfs
100116 , concatIterateBfsRev
101117
118+ -- * Fold Many
102119 , FoldMany (..) -- for inspection testing
103120 , FoldManyPost (..)
104121 , foldMany
105122 , foldManyPost
106123 , chunksOf
107−
108124 , refoldMany
109125
110− -- * Folding trees bottom up
126+ -- * Fold Iterate
111127 , reduceIterateBfs
112128 , foldIterateBfs
129+
130+ -- * Multi-stream folds
131+ , eqBy
132+ , cmpBy
113133 )
114134where
115135
136+#include "inline.hs"
137+
116138import Control.Applicative (liftA2)
139+import Control.Monad.Catch (MonadThrow, throwM)
140+import Control.Monad.Trans.Class (MonadTrans(lift))
141+import Control.Monad.IO.Class (MonadIO(..))
142+import Data.Foldable (Foldable(foldl'), fold, foldr)
117143import Data.Functor (($>))
118144import Data.Functor.Identity (Identity(..))
145+import Data.Maybe (fromMaybe)
146+import Data.Semigroup (Endo(..))
119147import Fusion.Plugin.Types (Fuse(..))
120148import GHC.Base (build)
149+import GHC.Exts (IsList(..), IsString(..), oneShot)
121150import GHC.Types (SPEC(..))
122−import Prelude hiding (map, mapM, foldr, take, concatMap, takeWhile)
151+import Prelude hiding (map, mapM, take, concatMap, takeWhile, zipWith, concat)
152+import Text.Read
153+ ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
154+ , readListPrecDefault)
123155
156+import Streamly.Internal.BaseCompat ((#.))
124157import Streamly.Internal.Data.Fold.Type (Fold(..))
158+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
125159import Streamly.Internal.Data.Refold.Type (Refold(..))
126160import Streamly.Internal.Data.Stream.StreamD.Step (Step (..))
127161import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
128162import Streamly.Internal.Data.Unfold.Type (Unfold(..))
129163
130−import qualified Streamly.Internal.Data.Fold.Type as FL
164+import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)
131165import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
132166#ifdef USE_UNFOLDS_EVERYWHERE
133167import qualified Streamly.Internal.Data.Unfold.Type as Unfold
134168#endif
135169
170+-- $setup
171+-- >>> import Streamly.Internal.Data.Stream (CrossStream(..))
172+-- >>> import qualified Streamly.Data.Fold as Fold
173+-- >>> import qualified Streamly.Data.Parser as Parser
174+-- >>> import qualified Streamly.Data.Stream as Stream
175+-- >>> import qualified Streamly.Data.Unfold as Unfold
176+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
177+-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
178+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
179+
136180------------------------------------------------------------------------------
137181-- The direct style stream type
138182------------------------------------------------------------------------------
139183
140184-- gst = global state
185+
141186-- | A stream consists of a step function that generates the next step given a
142187-- current state, and the current state.
143188data Stream m a =
@@ -160,11 +205,19 @@ pattern Stream step state <- (unShare -> UnStream step state)
160205-- Primitives
161206------------------------------------------------------------------------------
162207
163−-- | An empty 'Stream' with a side effect.
208+-- | A stream that terminates without producing any output, but produces a side
209+-- effect.
210+--
211+-- >>> Stream.fold Fold.toList (Stream.nilM (print "nil"))
212+-- "nil"
213+-- []
214+--
215+-- /Pre-release/
164216{-# INLINE_NORMAL nilM #-}
165217nilM :: Applicative m => m b -> Stream m a
166218nilM m = Stream (\_ _ -> m $> Stop) ()
167219
220+-- | Like 'cons' but fuses an effect instead of a pure value.
168221{-# INLINE_NORMAL consM #-}
169222consM :: Applicative m => m a -> Stream m a -> Stream m a
170223consM m (Stream step state) = Stream step1 Nothing
@@ -179,7 +232,35 @@ consM m (Stream step state) = Stream step1 Nothing
179232 Skip s -> Skip (Just s)
180233 Stop -> Stop) <$> step gst st
181234
182−-- | Does not fuse, has the same performance as the StreamK version.
235+-- | Decompose a stream into its head and tail. If the stream is empty, returns
236+-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
237+-- the head of the stream and @ma@ its tail.
238+--
239+-- Properties:
240+--
241+-- >>> Nothing <- Stream.uncons Stream.nil
242+-- >>> Just ("a", t) <- Stream.uncons (Stream.cons "a" Stream.nil)
243+--
244+-- This can be used to consume the stream in an imperative manner one element
245+-- at a time, as it just breaks down the stream into individual elements and we
246+-- can loop over them as we deem fit. For example, this can be used to convert
247+-- a streamly stream into other stream types.
248+--
249+-- All the folds in this module can be expressed in terms of 'uncons', however,
250+-- this is generally less efficient than specific folds because it takes apart
251+-- the stream one element at a time, therefore, does not take adavantage of
252+-- stream fusion.
253+--
254+-- 'foldBreak' is a more general way of consuming a stream piecemeal.
255+--
256+-- >>> :{
257+-- uncons xs = do
258+-- r <- Stream.foldBreak Fold.one xs
259+-- return $ case r of
260+-- (Nothing, _) -> Nothing
261+-- (Just h, t) -> Just (h, t)
262+-- :}
263+--
183264{-# INLINE_NORMAL uncons #-}
184265uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
185266uncons (UnStream step state) = go SPEC state
@@ -197,7 +278,13 @@ uncons (UnStream step state) = go SPEC state
197278
198279data UnfoldState s = UnfoldNothing | UnfoldJust s
199280
200−-- | Convert an 'Unfold' into a 'Stream' by supplying it a seed.
281+-- | Convert an 'Unfold' into a stream by supplying it an input seed.
282+--
283+-- >>> s = Stream.unfold Unfold.replicateM (3, putStrLn "hello")
284+-- >>> Stream.fold Fold.drain s
285+-- hello
286+-- hello
287+-- hello
201288--
202289{-# INLINE_NORMAL unfold #-}
203290unfold :: Applicative m => Unfold m a b -> a -> Stream m b
@@ -217,7 +304,12 @@ unfold (Unfold ustep inject) seed = Stream step UnfoldNothing
217304-- From Values
218305------------------------------------------------------------------------------
219306
220−-- | Create a singleton 'Stream' from a pure value.
307+-- | Create a singleton stream from a pure value.
308+--
309+-- >>> fromPure a = a `Stream.cons` Stream.nil
310+-- >>> fromPure = pure
311+-- >>> fromPure = fromEffect . pure
312+--
221313{-# INLINE_NORMAL fromPure #-}
222314fromPure :: Applicative m => a -> Stream m a
223315fromPure x = Stream (\_ s -> pure $ step undefined s) True
@@ -226,7 +318,14 @@ fromPure x = Stream (\_ s -> pure $ step undefined s) True
226318 step _ True = Yield x False
227319 step _ False = Stop
228320
229−-- | Create a singleton 'Stream' from a monadic action.
321+-- | Create a singleton stream from a monadic action.
322+--
323+-- >>> fromEffect m = m `consM` Stream.nil
324+-- >>> fromEffect = Stream.sequence . Stream.fromPure
325+--
326+-- >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")
327+-- hello
328+--
230329{-# INLINE_NORMAL fromEffect #-}
231330fromEffect :: Applicative m => m a -> Stream m a
232331fromEffect m = Stream step True
@@ -242,7 +341,8 @@ fromEffect m = Stream step True
242341------------------------------------------------------------------------------
243342
244343-- Adapted from the vector package.
245−-- | Convert a list of pure values to a 'Stream'
344+
345+-- | Construct a stream from a list of pure values.
246346{-# INLINE_LATE fromList #-}
247347fromList :: Applicative m => [a] -> Stream m a
248348#ifdef USE_UNFOLDS_EVERYWHERE
@@ -295,12 +395,38 @@ toStreamK (Stream step state) = go state
295395-- Running a 'Fold'
296396------------------------------------------------------------------------------
297397
398+-- >>> fold f = Fold.extractM . Stream.foldAddLazy f
399+-- >>> fold f = Stream.fold Fold.one . Stream.foldManyPost f
400+-- >>> fold f = Fold.extractM <=< Stream.foldAdd f
401+
402+-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
403+-- expression strictly at each step. The behavior is similar to 'foldl''. A
404+-- 'Fold' can terminate early without consuming the full stream. See the
405+-- documentation of individual 'Fold's for termination behavior.
406+--
407+-- Definitions:
408+--
409+-- >>> fold f = fmap fst . Stream.foldBreak f
410+-- >>> fold f = Stream.parse (Parser.fromFold f)
411+--
412+-- Example:
413+--
414+-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
415+-- 5050
416+--
298417{-# INLINE_NORMAL fold #-}
299418fold :: Monad m => Fold m a b -> Stream m a -> m b
300419fold fld strm = do
301420 (b, _) <- foldBreak fld strm
302421 return b
303422
423+-- | Fold resulting in either breaking the stream or continuation of the fold.
424+-- Instead of supplying the input stream in one go we can run the fold multiple
425+-- times, each time supplying the next segment of the input stream. If the fold
426+-- has not yet finished it returns a fold that can be run again otherwise it
427+-- returns the fold result and the residual stream.
428+--
429+-- /Internal/
304430{-# INLINE_NORMAL foldEither #-}
305431foldEither :: Monad m =>
306432 Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
@@ -324,6 +450,9 @@ foldEither (Fold fstep begin done) (UnStream step state) = do
324450 Skip s -> go SPEC fs s
325451 Stop -> return $! Left (Fold fstep (return $ FL.Partial fs) done)
326452
453+-- | Like 'fold' but also returns the remaining stream. The resulting stream
454+-- would be 'Stream.nil' if the stream finished before the fold.
455+--
327456{-# INLINE_NORMAL foldBreak #-}
328457foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
329458foldBreak fld strm = do
@@ -342,9 +471,18 @@ foldBreak fld strm = do
342471
343472 nil = Stream (\_ _ -> return Stop) ()
344473
345−{-# INLINE_NORMAL foldContinue #-}
346−foldContinue :: Monad m => Fold m a b -> Stream m a -> Fold m a b
347−foldContinue (Fold fstep finitial fextract) (Stream sstep state) =
474+-- | Append a stream to a fold lazily to build an accumulator incrementally.
475+--
476+-- Example, to continue folding a list of streams on the same sum fold:
477+--
478+-- >>> streams = [Stream.fromList [1..5], Stream.fromList [6..10]]
479+-- >>> f = Prelude.foldl Stream.foldAddLazy Fold.sum streams
480+-- >>> Stream.fold f Stream.nil
481+-- 55
482+--
483+{-# INLINE_NORMAL foldAddLazy #-}
484+foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b
485+foldAddLazy (Fold fstep finitial fextract) (Stream sstep state) =
348486 Fold fstep initial fextract
349487
350488 where
@@ -367,6 +505,15 @@ foldContinue (Fold fstep finitial fextract) (Stream sstep state) =
367505 Skip s -> go SPEC fs s
368506 Stop -> return $ FL.Partial fs
369507
508+-- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce
509+
510+-- |
511+-- >>> foldAdd = flip Fold.addStream
512+--
513+foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)
514+foldAdd f =
515+ Streamly.Internal.Data.Stream.StreamD.Type.fold (FL.duplicate f)
516+
370517------------------------------------------------------------------------------
371518-- Right Folds
372519------------------------------------------------------------------------------
@@ -391,6 +538,23 @@ foldContinue (Fold fstep finitial fextract) (Stream sstep state) =
391538--
392539-- S.foldrM (\x t -> if x then return t else return False) (return True)
393540-- (S.fromList [False,undefined] :: Stream IO Bool)
541+
542+-- | Right associative/lazy pull fold. @foldrM build final stream@ constructs
543+-- an output structure using the step function @build@. @build@ is invoked with
544+-- the next input element and the remaining (lazy) tail of the output
545+-- structure. It builds a lazy output expression using the two. When the "tail
546+-- structure" in the output expression is evaluated it calls @build@ again thus
547+-- lazily consuming the input @stream@ until either the output expression built
548+-- by @build@ is free of the "tail" or the input is exhausted in which case
549+-- @final@ is used as the terminating case for the output structure. For more
550+-- details see the description in the previous section.
551+--
552+-- Example, determine if any element is 'odd' in a stream:
553+--
554+-- >>> s = Stream.fromList (2:4:5:undefined)
555+-- >>> step x xs = if odd x then return True else xs
556+-- >>> Stream.foldrM step (return False) s
557+-- True
394558--
395559{-# INLINE_NORMAL foldrM #-}
396560foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
@@ -425,6 +589,18 @@ foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
425589-- monads. For example, if "any" is implemented using "foldr" instead of
426590-- "foldrM" it performs the same with Identity monad but performs 1000x slower
427591-- with IO monad.
592+
593+-- | Right fold, lazy for lazy monads and pure streams, and strict for strict
594+-- monads.
595+--
596+-- Please avoid using this routine in strict monads like IO unless you need a
597+-- strict right fold. This is provided only for use in lazy monads (e.g.
598+-- Identity) or pure streams. Note that with this signature it is not possible
599+-- to implement a lazy foldr when the monad @m@ is strict. In that case it
600+-- would be strict in its accumulator and therefore would necessarily consume
601+-- all its input.
602+--
603+-- >>> foldr f z = Stream.foldrM (\a b -> f a <$> b) (return z)
428604--
429605{-# INLINE_NORMAL foldr #-}
430606foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
@@ -520,7 +696,7 @@ drain (Stream step state) = go SPEC state
520696
521697{-# INLINE_NORMAL toList #-}
522698toList :: Monad m => Stream m a -> m [a]
523−toList = foldr (:) []
699+toList = Streamly.Internal.Data.Stream.StreamD.Type.foldr (:) []
524700
525701-- Use foldr/build fusion to fuse with list consumers
526702-- This can be useful when using the IsList instance
@@ -533,7 +709,7 @@ toListFB c n (Stream step state) = go state
533709 Skip s -> go s
534710 Stop -> n
535711
536−{-# RULES "toList Identity" toList = toListId #-}
712+{-# RULES "toList Identity" Streamly.Internal.Data.Stream.StreamD.Type.toList = toListId #-}
537713{-# INLINE_EARLY toListId #-}
538714toListId :: Stream Identity a -> Identity [a]
539715toListId s = Identity $ build (\c n -> toListFB c n s)
@@ -543,6 +719,8 @@ toListId s = Identity $ build (\c n -> toListFB c n s)
543719------------------------------------------------------------------------------
544720
545721-- Adapted from the vector package.
722+
723+-- | Compare two streams for equality
546724{-# INLINE_NORMAL eqBy #-}
547725eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
548726eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2
@@ -571,7 +749,8 @@ eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2
571749 Stop -> return True
572750
573751-- Adapted from the vector package.
574−-- | Compare two streams lexicographically
752+
753+-- | Compare two streams lexicographically.
575754{-# INLINE_NORMAL cmpBy #-}
576755cmpBy
577756 :: Monad m
@@ -606,7 +785,17 @@ cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
606785------------------------------------------------------------------------------
607786
608787-- Adapted from the vector package.
609−-- | Map a monadic function over a 'Stream'
788+
789+-- |
790+-- >>> mapM f = Stream.sequence . fmap f
791+--
792+-- Apply a monadic function to each element of the stream and replace it with
793+-- the output of the resulting action.
794+--
795+-- >>> s = Stream.fromList ["a", "b", "c"]
796+-- >>> Stream.fold Fold.drain $ Stream.mapM putStr s
797+-- abc
798+--
610799{-# INLINE_NORMAL mapM #-}
611800mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
612801mapM f (Stream step state) = Stream step' state
@@ -633,11 +822,149 @@ instance Functor m => Functor (Stream m) where
633822 {-# INLINE (<$) #-}
634823 (<$) = fmap . const
635824
825+------------------------------------------------------------------------------
826+-- Lists
827+------------------------------------------------------------------------------
828+
829+-- XXX Show instance is 10x slower compared to read, we can do much better.
830+-- The list show instance itself is really slow.
831+
832+-- XXX The default definitions of "<" in the Ord instance etc. do not perform
833+-- well, because they do not get inlined. Need to add INLINE in Ord class in
834+-- base?
835+
836+instance IsList (Stream Identity a) where
837+ type (Item (Stream Identity a)) = a
838+
839+ {-# INLINE fromList #-}
840+ fromList = Streamly.Internal.Data.Stream.StreamD.Type.fromList
841+
842+ {-# INLINE toList #-}
843+ toList = runIdentity . Streamly.Internal.Data.Stream.StreamD.Type.toList
844+
845+instance Eq a => Eq (Stream Identity a) where
846+ {-# INLINE (==) #-}
847+ (==) xs ys = runIdentity $ eqBy (==) xs ys
848+
849+instance Ord a => Ord (Stream Identity a) where
850+ {-# INLINE compare #-}
851+ compare xs ys = runIdentity $ cmpBy compare xs ys
852+
853+ {-# INLINE (<) #-}
854+ x < y =
855+ case compare x y of
856+ LT -> True
857+ _ -> False
858+
859+ {-# INLINE (<=) #-}
860+ x <= y =
861+ case compare x y of
862+ GT -> False
863+ _ -> True
864+
865+ {-# INLINE (>) #-}
866+ x > y =
867+ case compare x y of
868+ GT -> True
869+ _ -> False
870+
871+ {-# INLINE (>=) #-}
872+ x >= y =
873+ case compare x y of
874+ LT -> False
875+ _ -> True
876+
877+ {-# INLINE max #-}
878+ max x y = if x <= y then y else x
879+
880+ {-# INLINE min #-}
881+ min x y = if x <= y then x else y
882+
883+instance Show a => Show (Stream Identity a) where
884+ showsPrec p dl = showParen (p > 10) $
885+ showString "fromList " . shows (GHC.Exts.toList dl)
886+
887+instance Read a => Read (Stream Identity a) where
888+ readPrec = parens $ prec 10 $ do
889+ Ident "fromList" <- lexP
890+ Streamly.Internal.Data.Stream.StreamD.Type.fromList <$> readPrec
891+
892+ readListPrec = readListPrecDefault
893+
894+instance (a ~ Char) => IsString (Stream Identity a) where
895+ {-# INLINE fromString #-}
896+ fromString = Streamly.Internal.Data.Stream.StreamD.Type.fromList
897+
898+-------------------------------------------------------------------------------
899+-- Foldable
900+-------------------------------------------------------------------------------
901+
902+-- The default Foldable instance has several issues:
903+-- 1) several definitions do not have INLINE on them, so we provide
904+-- re-implementations with INLINE pragmas.
905+-- 2) the definitions of sum/product/maximum/minimum are inefficient as they
906+-- use right folds, they cannot run in constant memory. We provide
907+-- implementations using strict left folds here.
908+
909+-- There is no Traversable instance because, there is no scalable cons for
910+-- StreamD, use toList and fromList instead.
911+
912+instance (Foldable m, Monad m) => Foldable (Stream m) where
913+
914+ {-# INLINE foldMap #-}
915+ foldMap f =
916+ Data.Foldable.fold
917+ . Streamly.Internal.Data.Stream.StreamD.Type.foldr (mappend . f) mempty
918+
919+ {-# INLINE foldr #-}
920+ foldr f z t = appEndo (foldMap (Endo #. f) t) z
921+
922+ {-# INLINE foldl' #-}
923+ foldl' f z0 xs = Data.Foldable.foldr f' id xs z0
924+ where f' x k = oneShot $ \z -> k $! f z x
925+
926+ {-# INLINE length #-}
927+ length = Data.Foldable.foldl' (\n _ -> n + 1) 0
928+
929+ {-# INLINE elem #-}
930+ elem = any . (==)
931+
932+ {-# INLINE maximum #-}
933+ maximum =
934+ fromMaybe (errorWithoutStackTrace "maximum: empty stream")
935+ . toMaybe
936+ . Data.Foldable.foldl' getMax Nothing'
937+
938+ where
939+
940+ getMax Nothing' x = Just' x
941+ getMax (Just' mx) x = Just' $! max mx x
942+
943+ {-# INLINE minimum #-}
944+ minimum =
945+ fromMaybe (errorWithoutStackTrace "minimum: empty stream")
946+ . toMaybe
947+ . Data.Foldable.foldl' getMin Nothing'
948+
949+ where
950+
951+ getMin Nothing' x = Just' x
952+ getMin (Just' mn) x = Just' $! min mn x
953+
954+ {-# INLINE sum #-}
955+ sum = Data.Foldable.foldl' (+) 0
956+
957+ {-# INLINE product #-}
958+ product = Data.Foldable.foldl' (*) 1
959+
636960-------------------------------------------------------------------------------
637961-- Filtering
638962-------------------------------------------------------------------------------
639963
640964-- Adapted from the vector package.
965+
966+-- | Take first 'n' elements from the stream and discard the rest.
967+--
641968{-# INLINE_NORMAL take #-}
642969take :: Applicative m => Int -> Stream m a -> Stream m a
643970take n (Stream step state) = n `seq` Stream step' (state, 0)
@@ -653,8 +980,12 @@ take n (Stream step state) = n `seq` Stream step' (state, 0)
653980 step' _ (_, _) = pure Stop
654981
655982-- Adapted from the vector package.
983+
984+-- | Same as 'takeWhile' but with a monadic predicate.
985+--
656986{-# INLINE_NORMAL takeWhileM #-}
657987takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
988+-- takeWhileM p = scanMaybe (FL.takingEndByM_ (\x -> not <$> p x))
658989takeWhileM f (Stream step state) = Stream step' state
659990 where
660991 {-# INLINE_LATE step' #-}
@@ -667,6 +998,8 @@ takeWhileM f (Stream step state) = Stream step' state
667998 Skip s -> return $ Skip s
668999 Stop -> return Stop
6691000
1001+-- | End the stream as soon as the predicate fails on an element.
1002+--
6701003{-# INLINE takeWhile #-}
6711004takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
6721005takeWhile f = takeWhileM (return . f)
@@ -697,10 +1030,64 @@ takeEndByM f (Stream step state) = Stream step' (Just state)
6971030takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
6981031takeEndBy f = takeEndByM (return . f)
6991032
1033+------------------------------------------------------------------------------
1034+-- Zipping
1035+------------------------------------------------------------------------------
1036+
1037+-- | Like 'zipWith' but using a monadic zipping function.
1038+--
1039+{-# INLINE_NORMAL zipWithM #-}
1040+zipWithM :: Monad m
1041+ => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
1042+zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)
1043+ where
1044+ {-# INLINE_LATE step #-}
1045+ step gst (sa, sb, Nothing) = do
1046+ r <- stepa (adaptState gst) sa
1047+ return $
1048+ case r of
1049+ Yield x sa' -> Skip (sa', sb, Just x)
1050+ Skip sa' -> Skip (sa', sb, Nothing)
1051+ Stop -> Stop
1052+
1053+ step gst (sa, sb, Just x) = do
1054+ r <- stepb (adaptState gst) sb
1055+ case r of
1056+ Yield y sb' -> do
1057+ z <- f x y
1058+ return $ Yield z (sa, sb', Nothing)
1059+ Skip sb' -> return $ Skip (sa, sb', Just x)
1060+ Stop -> return Stop
1061+
1062+{-# RULES "zipWithM xs xs"
1063+ forall f xs. zipWithM @Identity f xs xs = mapM (\x -> f x x) xs #-}
1064+
1065+-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
1066+-- elements @a@ and @b@ are then zipped using the supplied zip function and the
1067+-- result @c@ is yielded to the consumer.
1068+--
1069+-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
1070+-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
1071+--
1072+-- >>> s1 = Stream.fromList [1,2,3]
1073+-- >>> s2 = Stream.fromList [4,5,6]
1074+-- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2
1075+-- [5,7,9]
1076+--
1077+{-# INLINE zipWith #-}
1078+zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
1079+zipWith f = zipWithM (\a b -> return (f a b))
1080+
7001081------------------------------------------------------------------------------
7011082-- Combine N Streams - concatAp
7021083------------------------------------------------------------------------------
7031084
1085+-- | Apply a stream of functions to a stream of values and flatten the results.
1086+--
1087+-- Note that the second stream is evaluated multiple times.
1088+--
1089+-- >>> crossApply = Stream.crossWith id
1090+--
7041091{-# INLINE_NORMAL crossApply #-}
7051092crossApply :: Functor f => Stream f (a -> b) -> Stream f a -> Stream f b
7061093crossApply (Stream stepa statea) (Stream stepb stateb) =
@@ -786,6 +1173,39 @@ instance Applicative f => Applicative (Stream f) where
7861173 (<*) = crossApplyFst
7871174-}
7881175
1176+-- |
1177+-- Definition:
1178+--
1179+-- >>> crossWith f m1 m2 = fmap f m1 `Stream.crossApply` m2
1180+--
1181+-- Note that the second stream is evaluated multiple times.
1182+--
1183+{-# INLINE crossWith #-}
1184+crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
1185+crossWith f m1 m2 = fmap f m1 `crossApply` m2
1186+
1187+-- | Given a @Stream m a@ and @Stream m b@ generate a stream with all possible
1188+-- combinations of the tuple @(a, b)@.
1189+--
1190+-- Definition:
1191+--
1192+-- >>> cross = Stream.crossWith (,)
1193+--
1194+-- The second stream is evaluated multiple times. If that is not desired it can
1195+-- be cached in an 'Data.Array.Array' and then generated from the array before
1196+-- calling this function. Caching may also improve performance if the stream is
1197+-- expensive to evaluate.
1198+--
1199+-- See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused
1200+-- alternative.
1201+--
1202+-- Time: O(m x n)
1203+--
1204+-- /Pre-release/
1205+{-# INLINE cross #-}
1206+cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)
1207+cross = crossWith (,)
1208+
7891209------------------------------------------------------------------------------
7901210-- Combine N Streams - unfoldMany
7911211------------------------------------------------------------------------------
@@ -804,6 +1224,10 @@ data ConcatMapUState o i =
8041224-- optimization via fusion. This can be many times more efficient than
8051225-- 'concatMap'.
8061226
1227+-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
1228+-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
1229+-- therefore provide many times better performance.
1230+--
8071231{-# INLINE_NORMAL unfoldMany #-}
8081232unfoldMany :: Monad m => Unfold m a b -> Stream m a -> Stream m b
8091233unfoldMany (Unfold istep inject) (Stream ostep ost) =
@@ -831,6 +1255,14 @@ unfoldMany (Unfold istep inject) (Stream ostep ost) =
8311255------------------------------------------------------------------------------
8321256
8331257-- Adapted from the vector package.
1258+
1259+-- | Map a stream producing monadic function on each element of the stream
1260+-- and then flatten the results into a single stream. Since the stream
1261+-- generation function is monadic, unlike 'concatMap', it can produce an
1262+-- effect at the beginning of each iteration of the inner loop.
1263+--
1264+-- See 'unfoldMany' for a fusible alternative.
1265+--
8341266{-# INLINE_NORMAL concatMapM #-}
8351267concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
8361268concatMapM f (Stream step state) = Stream step' (Left state)
@@ -861,10 +1293,28 @@ concatMapM f (Stream step state) = Stream step' (Left state)
8611293 return $ Skip (Right (Stream inner_step inner_s, st))
8621294 Stop -> return $ Skip (Left st)
8631295
1296+-- | Map a stream producing function on each element of the stream and then
1297+-- flatten the results into a single stream.
1298+--
1299+-- >>> concatMap f = Stream.concatMapM (return . f)
1300+-- >>> concatMap f = Stream.concat . fmap f
1301+-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
1302+--
1303+-- See 'unfoldMany' for a fusible alternative.
1304+--
8641305{-# INLINE concatMap #-}
8651306concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
8661307concatMap f = concatMapM (return . f)
8671308
1309+-- | Flatten a stream of streams to a single stream.
1310+--
1311+-- >>> concat = Stream.concatMap id
1312+--
1313+-- /Pre-release/
1314+{-# INLINE concat #-}
1315+concat :: Monad m => Stream m (Stream m a) -> Stream m a
1316+concat = concatMap id
1317+
8681318-- XXX The idea behind this rule is to rewrite any calls to "concatMap
8691319-- fromArray" automatically to flattenArrays which is much faster. However, we
8701320-- need an INLINE_EARLY on concatMap for this rule to fire. But if we use
@@ -876,10 +1326,16 @@ concatMap f = concatMapM (return . f)
8761326-- {-# RULES "concatMap Array.toStreamD"
8771327-- concatMap Array.toStreamD = Array.flattenArray #-}
8781328
879−-- |
880−-- Definition:
1329+-- >>> concatEffect = Stream.concat . lift -- requires (MonadTrans t)
1330+-- >>> concatEffect = join . lift -- requires (MonadTrans t, Monad (Stream m))
1331+
1332+-- | Given a stream value in the underlying monad, lift and join the underlying
1333+-- monad with the stream monad.
1334+--
1335+-- >>> concatEffect = Stream.concat . Stream.fromEffect
1336+-- >>> concatEffect eff = Stream.concatMapM (\() -> eff) (fromPure ())
8811337--
882−-- > concatEffect generator = concatMapM (\() -> generator) (fromPure ())
1338+-- See also: 'concat', 'sequence'
8831339--
8841340{-# INLINE concatEffect #-}
8851341concatEffect :: Monad m => m (Stream m a) -> Stream m a
@@ -903,6 +1359,13 @@ instance Monad m => Monad (Stream m) where
9031359-- Traversing a tree top down
9041360------------------------------------------------------------------------------
9051361
1362+-- Next stream is to be generated by the return value of the previous stream. A
1363+-- general intuitive way of doing that could be to use an appending monad
1364+-- instance for streams where the result of the previous stream is used to
1365+-- generate the next one. In the first pass we can just emit the values in the
1366+-- stream and keep building a buffered list/stream, once done we can then
1367+-- process the buffered stream.
1368+
9061369-- | Generate a stream from an initial state, scan and concat the stream,
9071370-- generate a stream again from the final state of the previous scan and repeat
9081371-- the process.
@@ -938,9 +1401,11 @@ concatIterateScan scanner generate initial = Stream step (Left initial)
9381401-- have to store any state. This makes the stored state proportional to the
9391402-- number of non-leaf nodes rather than total number of nodes.
9401403
941−-- | This function may be slightly faster than concatIterateBfs because it
942−-- traverses the elements on a level in reverse order, therefore, does not have
943−-- to reverse the list stroing those.
1404+-- | Same as 'concatIterateBfs' except that the traversal of the last
1405+-- element on a level is emitted first and then going backwards up to the first
1406+-- element (reversed ordering). This may be slightly faster than
1407+-- 'concatIterateBfs'.
1408+--
9441409{-# INLINE_NORMAL concatIterateBfsRev #-}
9451410concatIterateBfsRev :: Monad m =>
9461411 (a -> Maybe (Stream m a))
@@ -966,6 +1431,17 @@ concatIterateBfsRev f stream = Stream step (stream, [])
9661431 (y:ys) -> return $ Skip (y, ys)
9671432 [] -> return Stop
9681433
1434+-- | Similar to 'concatIterateDfs' except that it traverses the stream in
1435+-- breadth first style (BFS). First, all the elements in the input stream are
1436+-- emitted, and then their traversals are emitted.
1437+--
1438+-- Example, list a directory tree using BFS:
1439+--
1440+-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)
1441+-- >>> input = Stream.fromPure (Left ".")
1442+-- >>> ls = Stream.concatIterateBfs f input
1443+--
1444+-- /Pre-release/
9691445{-# INLINE_NORMAL concatIterateBfs #-}
9701446concatIterateBfs :: Monad m =>
9711447 (a -> Maybe (Stream m a))
@@ -994,6 +1470,19 @@ concatIterateBfs f stream = Stream step (stream, [], [])
9941470 (x:xs1) -> return $ Skip (x, xs1, [])
9951471 [] -> return Stop
9961472
1473+-- | Traverse the stream in depth first style (DFS). Map each element in the
1474+-- input stream to a stream and flatten, recursively map the resulting elements
1475+-- as well to a stream and flatten until no more streams are generated.
1476+--
1477+-- Example, list a directory tree using DFS:
1478+--
1479+-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)
1480+-- >>> input = Stream.fromPure (Left ".")
1481+-- >>> ls = Stream.concatIterateDfs f input
1482+--
1483+-- This is equivalent to using @concatIterateWith StreamK.append@.
1484+--
1485+-- /Pre-release/
9971486{-# INLINE_NORMAL concatIterateDfs #-}
9981487concatIterateDfs :: Monad m =>
9991488 (a -> Maybe (Stream m a))
@@ -1024,6 +1513,15 @@ data IterateUnfoldState o i =
10241513 IterateUnfoldOuter o
10251514 | IterateUnfoldInner o i [i]
10261515
1516+-- | Same as @concatIterateDfs@ but more efficient due to stream fusion.
1517+--
1518+-- Example, list a directory tree using DFS:
1519+--
1520+-- >>> f = Unfold.either Dir.eitherReaderPaths Unfold.nil
1521+-- >>> input = Stream.fromPure (Left ".")
1522+-- >>> ls = Stream.unfoldIterateDfs f input
1523+--
1524+-- /Pre-release/
10271525{-# INLINE_NORMAL unfoldIterateDfs #-}
10281526unfoldIterateDfs :: Monad m =>
10291527 Unfold m a a
@@ -1061,6 +1559,10 @@ data IterateUnfoldBFSRevState o i =
10611559 IterateUnfoldBFSRevOuter o [i]
10621560 | IterateUnfoldBFSRevInner i [i]
10631561
1562+-- | Like 'unfoldIterateBfs' but processes the children in reverse order,
1563+-- therefore, may be slightly faster.
1564+--
1565+-- /Pre-release/
10641566{-# INLINE_NORMAL unfoldIterateBfsRev #-}
10651567unfoldIterateBfsRev :: Monad m =>
10661568 Unfold m a a
@@ -1101,6 +1603,9 @@ data IterateUnfoldBFSState o i =
11011603 IterateUnfoldBFSOuter o [i]
11021604 | IterateUnfoldBFSInner i [i] [i]
11031605
1606+-- | Like 'unfoldIterateDfs' but uses breadth first style traversal.
1607+--
1608+-- /Pre-release/
11041609{-# INLINE_NORMAL unfoldIterateBfs #-}
11051610unfoldIterateBfs :: Monad m =>
11061611 Unfold m a a
@@ -1143,9 +1648,10 @@ unfoldIterateBfs (Unfold istep inject) (Stream ostep ost) =
11431648-- Folding a tree bottom up
11441649------------------------------------------------------------------------------
11451650
1146−-- | Binary BFS style reduce, folds a level entirely, before starting to fold
1147−-- the next level. The last elements of a previously folded level are folded
1148−-- first.
1651+-- | Binary BFS style reduce, folds a level entirely using the supplied fold
1652+-- function, collecting the outputs as next level of the tree, then repeats the
1653+-- same process on the next level. The last elements of a previously folded
1654+-- level are folded first.
11491655{-# INLINE_NORMAL reduceIterateBfs #-}
11501656reduceIterateBfs :: Monad m =>
11511657 (a -> a -> m a) -> Stream m a -> m (Maybe a)
@@ -1206,7 +1712,35 @@ data FoldManyPost s fs b a
12061712 | FoldManyPostYield b (FoldManyPost s fs b a)
12071713 | FoldManyPostDone
12081714
1209−-- | 'Streamly.Internal.Data.Stream.foldManyPost'.
1715+-- XXX Need a more intuitive name, and need to reconcile the names
1716+-- foldMany/fold/parse/parseMany/parseManyPost etc.
1717+
1718+-- | Like 'foldMany' but evaluates the fold before the stream, and yields its
1719+-- output even if the stream is empty, therefore, always results in a non-empty
1720+-- output even on an empty stream (default result of the fold).
1721+--
1722+-- Example, empty stream:
1723+--
1724+-- >>> f = Fold.take 2 Fold.sum
1725+-- >>> fmany = Stream.fold Fold.toList . Stream.foldManyPost f
1726+-- >>> fmany $ Stream.fromList []
1727+-- [0]
1728+--
1729+-- Example, last fold empty:
1730+--
1731+-- >>> fmany $ Stream.fromList [1..4]
1732+-- [3,7,0]
1733+--
1734+-- Example, last fold non-empty:
1735+--
1736+-- >>> fmany $ Stream.fromList [1..5]
1737+-- [3,7,5]
1738+--
1739+-- Note that using a closed fold e.g. @Fold.take 0@, would result in an
1740+-- infinite stream without consuming the input.
1741+--
1742+-- /Pre-release/
1743+--
12101744{-# INLINE_NORMAL foldManyPost #-}
12111745foldManyPost :: Monad m => Fold m a b -> Stream m a -> Stream m b
12121746foldManyPost (Fold fstep initial extract) (Stream step state) =
@@ -1252,7 +1786,34 @@ data FoldMany s fs b a
12521786
12531787-- XXX Nested foldMany does not fuse.
12541788
1255−-- | 'Streamly.Internal.Data.Stream.foldMany'.
1789+-- | Apply a 'Fold' repeatedly on a stream and emit the results in the
1790+-- output stream. Unlike 'foldManyPost' it evaluates the fold after the stream,
1791+-- therefore, an empty input stream results in an empty output stream.
1792+--
1793+-- Definition:
1794+--
1795+-- >>> foldMany f = Stream.parseMany (Parser.fromFold f)
1796+--
1797+-- Example, empty stream:
1798+--
1799+-- >>> f = Fold.take 2 Fold.sum
1800+-- >>> fmany = Stream.fold Fold.toList . Stream.foldMany f
1801+-- >>> fmany $ Stream.fromList []
1802+-- []
1803+--
1804+-- Example, last fold empty:
1805+--
1806+-- >>> fmany $ Stream.fromList [1..4]
1807+-- [3,7]
1808+--
1809+-- Example, last fold non-empty:
1810+--
1811+-- >>> fmany $ Stream.fromList [1..5]
1812+-- [3,7,5]
1813+--
1814+-- Note that using a closed fold e.g. @Fold.take 0@, would result in an
1815+-- infinite stream on a non-empty input stream.
1816+--
12561817{-# INLINE_NORMAL foldMany #-}
12571818foldMany :: Monad m => Fold m a b -> Stream m a -> Stream m b
12581819foldMany (Fold fstep initial extract) (Stream step state) =
@@ -1340,3 +1901,122 @@ refoldMany (Refold fstep inject extract) action (Stream step state) =
13401901 return $ Skip (FoldManyYield b FoldManyDone)
13411902 step' _ (FoldManyYield b next) = return $ Yield b next
13421903 step' _ FoldManyDone = return Stop
1904+
1905+------------------------------------------------------------------------------
1906+-- Stream with a cross product style monad instance
1907+------------------------------------------------------------------------------
1908+
1909+-- XXX CrossStream performs better than the CrossStreamK when nesting two
1910+-- loops, however, CrossStreamK seems to be better for more than two nestings,
1911+-- need to do more perf investigation.
1912+
1913+-- | A newtype wrapper for the 'Stream' type with a cross product style monad
1914+-- instance.
1915+--
1916+-- A 'Monad' bind behaves like a @for@ loop:
1917+--
1918+-- >>> :{
1919+-- Stream.fold Fold.toList $ Stream.unCross $ do
1920+-- x <- Stream.mkCross $ Stream.fromList [1,2]
1921+-- -- Perform the following actions for each x in the stream
1922+-- return x
1923+-- :}
1924+-- [1,2]
1925+--
1926+-- Nested monad binds behave like nested @for@ loops:
1927+--
1928+-- >>> :{
1929+-- Stream.fold Fold.toList $ Stream.unCross $ do
1930+-- x <- Stream.mkCross $ Stream.fromList [1,2]
1931+-- y <- Stream.mkCross $ Stream.fromList [3,4]
1932+-- -- Perform the following actions for each x, for each y
1933+-- return (x, y)
1934+-- :}
1935+-- [(1,3),(1,4),(2,3),(2,4)]
1936+--
1937+newtype CrossStream m a = CrossStream {unCrossStream :: Stream m a}
1938+ deriving (Functor, Foldable)
1939+
1940+{-# INLINE mkCross #-}
1941+mkCross :: Stream m a -> CrossStream m a
1942+mkCross = CrossStream
1943+
1944+{-# INLINE unCross #-}
1945+unCross :: CrossStream m a -> Stream m a
1946+unCross = unCrossStream
1947+
1948+-- Pure (Identity monad) stream instances
1949+deriving instance IsList (CrossStream Identity a)
1950+deriving instance (a ~ Char) => IsString (CrossStream Identity a)
1951+deriving instance Eq a => Eq (CrossStream Identity a)
1952+deriving instance Ord a => Ord (CrossStream Identity a)
1953+
1954+-- Do not use automatic derivation for this to show as "fromList" rather than
1955+-- "fromList Identity".
1956+instance Show a => Show (CrossStream Identity a) where
1957+ {-# INLINE show #-}
1958+ show (CrossStream xs) = show xs
1959+
1960+instance Read a => Read (CrossStream Identity a) where
1961+ {-# INLINE readPrec #-}
1962+ readPrec = fmap CrossStream readPrec
1963+
1964+------------------------------------------------------------------------------
1965+-- Applicative
1966+------------------------------------------------------------------------------
1967+
1968+-- Note: we need to define all the typeclass operations because we want to
1969+-- INLINE them.
1970+instance Monad m => Applicative (CrossStream m) where
1971+ {-# INLINE pure #-}
1972+ pure x = CrossStream (fromPure x)
1973+
1974+ {-# INLINE (<*>) #-}
1975+ (CrossStream s1) <*> (CrossStream s2) =
1976+ CrossStream (crossApply s1 s2)
1977+
1978+ {-# INLINE liftA2 #-}
1979+ liftA2 f x = (<*>) (fmap f x)
1980+
1981+ {-# INLINE (*>) #-}
1982+ (CrossStream s1) *> (CrossStream s2) =
1983+ CrossStream (crossApplySnd s1 s2)
1984+
1985+ {-# INLINE (<*) #-}
1986+ (CrossStream s1) <* (CrossStream s2) =
1987+ CrossStream (crossApplyFst s1 s2)
1988+
1989+------------------------------------------------------------------------------
1990+-- Monad
1991+------------------------------------------------------------------------------
1992+
1993+instance Monad m => Monad (CrossStream m) where
1994+ return = pure
1995+
1996+ -- Benchmarks better with StreamD bind and pure:
1997+ -- toList, filterAllout, *>, *<, >> (~2x)
1998+ --
1999+
2000+ -- Benchmarks better with CPS bind and pure:
2001+ -- Prime sieve (25x)
2002+ -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
2003+ --
2004+ {-# INLINE (>>=) #-}
2005+ (>>=) (CrossStream m) f = CrossStream (concatMap (unCrossStream . f) m)
2006+
2007+ {-# INLINE (>>) #-}
2008+ (>>) = (*>)
2009+
2010+------------------------------------------------------------------------------
2011+-- Transformers
2012+------------------------------------------------------------------------------
2013+
2014+instance (MonadIO m) => MonadIO (CrossStream m) where
2015+ liftIO x = CrossStream (fromEffect $ liftIO x)
2016+
2017+instance MonadTrans CrossStream where
2018+ {-# INLINE lift #-}
2019+ lift x = CrossStream (fromEffect x)
2020+
2021+instance (MonadThrow m) => MonadThrow (CrossStream m) where
2022+ throwM = lift . throwM
core/src/Streamly/Internal/Data/Stream/StreamDK.hs+41−145
@@ -1,156 +1,52 @@
1−#include "inline.hs"
2−
31-- |
42-- Module : Streamly.Internal.Data.Stream.StreamDK
53-- Copyright : (c) 2019 Composewell Technologies
6−-- License : BSD3
4+-- License : BSD-3-Clause
75-- Maintainer : streamly@composewell.com
86-- Stability : experimental
97-- Portability : GHC
108--
11−
9+--
10+-- This module has the following problems due to rewrite rules:
11+--
12+-- * Rewrite rules lead to optimization problems, blocking fusion in some
13+-- cases, specifically when combining multiple operations e.g. (filter . drop).
14+-- * Rewrite rules lead to problems when calling a function recursively. For
15+-- example, the StreamD version of foldBreak cannot be used recursively when
16+-- wrapped in rewrite rules because each recursive call adds a roundtrip
17+-- conversion from D to K and back to D. We can use the StreamK versions of
18+-- these though because the rewrite rule gets eliminated in that case.
19+-- * If we have a unified module, we need two different versions of several
20+-- operations e.g. appendK and appendD, both are useful in different cases.
21+--
1222module Streamly.Internal.Data.Stream.StreamDK
13− (
14− -- * Stream Type
15−
16− Stream
17− , Step (..)
18−
19− -- * Construction
20− , nil
21− , cons
22− , consM
23− , unfoldr
24− , unfoldrM
25− , replicateM
26−
27− -- * Folding
28− , uncons
29− , foldrS
30−
31− -- * Specific Folds
32− , drain
23+ ( module Streamly.Internal.Data.Stream.Type
24+ , module Streamly.Internal.Data.Stream.Bottom
25+ , module Streamly.Internal.Data.Stream.Eliminate
26+ , module Streamly.Internal.Data.Stream.Exception
27+ , module Streamly.Internal.Data.Stream.Expand
28+ , module Streamly.Internal.Data.Stream.Generate
29+ , module Streamly.Internal.Data.Stream.Lift
30+ , module Streamly.Internal.Data.Stream.Reduce
31+ , module Streamly.Internal.Data.Stream.Transform
32+ , module Streamly.Internal.Data.Stream.Cross
33+ , module Streamly.Internal.Data.Stream.Zip
34+
35+ -- modules having dependencies on libraries other than base
36+ , module Streamly.Internal.Data.Stream.Transformer
3337 )
3438where
3539
36−import Streamly.Internal.Data.Stream.StreamDK.Type (Stream(..), Step(..))
37−
38−-------------------------------------------------------------------------------
39−-- Construction
40−-------------------------------------------------------------------------------
41−
42−nil :: Monad m => Stream m a
43−nil = Stream $ return Stop
44−
45−{-# INLINE_NORMAL cons #-}
46−cons :: Monad m => a -> Stream m a -> Stream m a
47−cons x xs = Stream $ return $ Yield x xs
48−
49−consM :: Monad m => m a -> Stream m a -> Stream m a
50−consM eff xs = Stream $ eff >>= \x -> return $ Yield x xs
51−
52−unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a
53−unfoldrM next state = Stream (step' state)
54− where
55− step' st = do
56− r <- next st
57− return $ case r of
58− Just (x, s) -> Yield x (Stream (step' s))
59− Nothing -> Stop
60−{-
61−unfoldrM next s0 = buildM $ \yld stp ->
62− let go s = do
63− r <- next s
64− case r of
65− Just (a, b) -> yld a (go b)
66− Nothing -> stp
67− in go s0
68−-}
69−
70−{-# INLINE unfoldr #-}
71−unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a
72−unfoldr next s0 = build $ \yld stp ->
73− let go s =
74− case next s of
75− Just (a, b) -> yld a (go b)
76− Nothing -> stp
77− in go s0
78−
79−replicateM :: Monad m => Int -> a -> Stream m a
80−replicateM n x = Stream (step n)
81− where
82− step i = return $
83− if i <= 0
84− then Stop
85− else Yield x (Stream (step (i - 1)))
86−
87−-------------------------------------------------------------------------------
88−-- Folding
89−-------------------------------------------------------------------------------
90−
91−uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
92−uncons (Stream step) = do
93− r <- step
94− return $ case r of
95− Yield x xs -> Just (x, xs)
96− Stop -> Nothing
97−
98−-- | Lazy right associative fold to a stream.
99−{-# INLINE_NORMAL foldrS #-}
100−foldrS :: Monad m
101− => (a -> Stream m b -> Stream m b)
102− -> Stream m b
103− -> Stream m a
104− -> Stream m b
105−foldrS f streamb = go
106− where
107− go (Stream stepa) = Stream $ do
108− r <- stepa
109− case r of
110− Yield x xs -> let Stream step = f x (go xs) in step
111− Stop -> let Stream step = streamb in step
112−
113−{-# INLINE_LATE foldrM #-}
114−foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
115−foldrM fstep acc = go
116− where
117− go (Stream step) = do
118− r <- step
119− case r of
120− Yield x xs -> fstep x (go xs)
121− Stop -> acc
122−
123−{-# INLINE_NORMAL build #-}
124−build :: Monad m
125− => forall a. (forall b. (a -> b -> b) -> b -> b) -> Stream m a
126−build g = g cons nil
127−
128−{-# RULES
129−"foldrM/build" forall k z (g :: forall b. (a -> b -> b) -> b -> b).
130− foldrM k z (build g) = g k z #-}
131−
132−{-
133−-- To fuse foldrM with unfoldrM we need the type m1 to be polymorphic such that
134−-- it is either Monad m or Stream m. So that we can use cons/nil as well as
135−-- monadic construction function as its arguments.
136−--
137−{-# INLINE_NORMAL buildM #-}
138−buildM :: Monad m
139− => forall a. (forall b. (a -> m1 b -> m1 b) -> m1 b -> m1 b) -> Stream m a
140−buildM g = g cons nil
141−-}
142−
143−-------------------------------------------------------------------------------
144−-- Specific folds
145−-------------------------------------------------------------------------------
146−
147−{-# INLINE drain #-}
148−drain :: Monad m => Stream m a -> m ()
149−drain = foldrM (\_ xs -> xs) (return ())
150−{-
151−drain (Stream step) = do
152− r <- step
153− case r of
154− Yield _ next -> drain next
155− Stop -> return ()
156− -}
40+import Streamly.Internal.Data.Stream.Bottom
41+import Streamly.Internal.Data.Stream.Cross
42+import Streamly.Internal.Data.Stream.Eliminate
43+import Streamly.Internal.Data.Stream.Exception
44+import Streamly.Internal.Data.Stream.Expand
45+import Streamly.Internal.Data.Stream.Generate
46+import Streamly.Internal.Data.Stream.Lift
47+import Streamly.Internal.Data.Stream.Reduce
48+import Streamly.Internal.Data.Stream.Transform
49+import Streamly.Internal.Data.Stream.Type
50+import Streamly.Internal.Data.Stream.Zip
51+
52+import Streamly.Internal.Data.Stream.Transformer
core/src/Streamly/Internal/Data/Stream/StreamK.hs+207−21
@@ -7,18 +7,56 @@
77-- Stability : experimental
88-- Portability : GHC
99--
10+-- To run examples in this module:
1011--
11−-- Continuation passing style (CPS) stream implementation. The symbol 'K' below
12−-- denotes a function as well as a Kontinuation.
12+-- >>> import qualified Streamly.Data.Fold as Fold
13+-- >>> import qualified Streamly.Data.Stream as Stream
14+-- >>> import qualified Streamly.Data.Stream.StreamK as StreamK
1315--
14−-- @
15−-- import qualified Streamly.Internal.Data.Stream.StreamK as K
16−-- @
16+-- We will add some more imports in the examples as needed.
17+--
18+-- For effectful streams we will use the following IO action:
19+--
20+-- >>> effect n = print n >> return n
21+--
22+-- = Overview
23+--
24+-- Continuation passing style (CPS) stream implementation. The 'K' in 'StreamK'
25+-- stands for Kontinuation.
26+--
27+-- StreamK can be constructed like lists, except that they use 'nil' instead of
28+-- '[]' and 'cons' instead of ':'.
29+--
30+-- `cons` adds a pure value at the head of the stream:
31+--
32+-- >>> import Streamly.Data.Stream.StreamK (StreamK, cons, consM, nil)
33+-- >>> stream = 1 `cons` 2 `cons` nil :: StreamK IO Int
34+--
35+-- Convert 'StreamK' to 'Stream' to use functions from the
36+-- "Streamly.Data.Stream" module:
37+--
38+-- >>> Stream.fold Fold.toList $ StreamK.toStream stream -- IO [Int]
39+-- [1,2]
40+--
41+-- `consM` adds an effect at the head of the stream:
42+--
43+-- >>> stream = effect 1 `consM` effect 2 `consM` nil
44+-- >>> Stream.fold Fold.toList $ StreamK.toStream stream
45+-- 1
46+-- 2
47+-- [1,2]
1748--
1849module Streamly.Internal.Data.Stream.StreamK
1950 (
2051 -- * The stream type
21− Stream(..)
52+ Stream(..) -- XXX stop exporting this
53+ , StreamK
54+ , fromStream
55+ , toStream
56+
57+ , CrossStreamK
58+ , unCross
59+ , mkCross
2260
2361 -- * Construction Primitives
2462 , mkStream
@@ -78,7 +116,9 @@ module Streamly.Internal.Data.Stream.StreamK
78116 , fold
79117 , foldBreak
80118 , foldEither
119+ , foldConcat
81120 , parseBreak
121+ , parse
82122
83123 -- ** Specialized Folds
84124 , drain
@@ -135,6 +175,7 @@ module Streamly.Internal.Data.Stream.StreamK
135175
136176 -- ** Reordering
137177 , reverse
178+ , sortBy
138179
139180 -- ** Map and Filter
140181 , mapMaybe
@@ -152,18 +193,25 @@ module Streamly.Internal.Data.Stream.StreamK
152193 , crossApply
153194 , crossApplySnd
154195 , crossApplyFst
196+ , crossWith
155197
156198 , concatMapWith
157199 , concatMap
158200 , concatEffect
159201 , bindWith
160− , concatPairsWith
202+ , concatIterateWith
203+ , concatIterateLeftsWith
204+ , concatIterateScanWith
205+
206+ , mergeMapWith
207+ , mergeIterateWith
161208
162209 -- ** Transformation comprehensions
163210 , the
164211
165212 -- * Semigroup Style Composition
166− , serial
213+ , append
214+ , interleave
167215
168216 -- * Utilities
169217 , consM
@@ -175,11 +223,15 @@ where
175223#include "assert.hs"
176224
177225import Control.Monad (void, join)
226+import GHC.Types (SPEC(..))
178227import Streamly.Internal.Data.Fold.Type (Fold(..))
228+import Streamly.Internal.Data.Producer.Type (Producer(..))
179229import Streamly.Internal.Data.SVar.Type (adaptState, defState)
180230
181231import qualified Streamly.Internal.Data.Fold.Type as FL
232+import qualified Streamly.Internal.Data.Parser as Parser
182233import qualified Streamly.Internal.Data.Parser.ParserD.Type as PR
234+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
183235import qualified Prelude
184236
185237import Prelude
@@ -193,14 +245,25 @@ import Streamly.Internal.Data.Parser.ParserD (ParseError(..))
193245
194246-- $setup
195247-- >>> :m
248+-- >>> import qualified Streamly.Data.Fold as Fold
249+-- >>> import qualified Streamly.Data.Parser as Parser
250+-- >>> import qualified Streamly.Data.Stream as Stream
251+-- >>> import qualified Streamly.Data.Stream.StreamK as StreamK
252+-- >>> import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
253+--
254+
255+{-# INLINE fromStream #-}
256+fromStream :: Monad m => Stream.Stream m a -> StreamK m a
257+fromStream = Stream.toStreamK
258+
259+{-# INLINE toStream #-}
260+toStream :: Applicative m => StreamK m a -> Stream.Stream m a
261+toStream = Stream.fromStreamK
196262
197263-------------------------------------------------------------------------------
198264-- Generation
199265-------------------------------------------------------------------------------
200266
201−{-# INLINE unfoldrM #-}
202−unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> Stream m a
203−unfoldrM = unfoldrMWith consM
204267{-
205268-- Generalization of concurrent streams/SVar via unfoldr.
206269--
@@ -265,11 +328,6 @@ fromList = fromFoldable
265328-- Elimination by Folding
266329-------------------------------------------------------------------------------
267330
268−-- | Lazy right associative fold.
269−{-# INLINE foldr #-}
270−foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
271−foldr step acc = foldrM (\x xs -> xs >>= \b -> return (step x b)) (return acc)
272−
273331{-# INLINE foldr1 #-}
274332foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)
275333foldr1 step m = do
@@ -297,6 +355,21 @@ foldlMx' step begin done = go begin
297355 yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r
298356 in foldStream defState yieldk single stop m1
299357
358+-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
359+-- expression strictly at each step. The behavior is similar to 'foldl''. A
360+-- 'Fold' can terminate early without consuming the full stream. See the
361+-- documentation of individual 'Fold's for termination behavior.
362+--
363+-- Definitions:
364+--
365+-- >>> fold f = fmap fst . StreamK.foldBreak f
366+-- >>> fold f = StreamK.parse (Parser.fromFold f)
367+--
368+-- Example:
369+--
370+-- >>> StreamK.fold Fold.sum $ StreamK.fromStream $ Stream.enumerateFromTo 1 100
371+-- 5050
372+--
300373{-# INLINABLE fold #-}
301374fold :: Monad m => FL.Fold m a b -> Stream m a -> m b
302375fold (FL.Fold step begin done) m = do
@@ -318,6 +391,13 @@ fold (FL.Fold step begin done) m = do
318391 FL.Done b1 -> return b1
319392 in foldStream defState yieldk single stop m1
320393
394+-- | Fold resulting in either breaking the stream or continuation of the fold.
395+-- Instead of supplying the input stream in one go we can run the fold multiple
396+-- times, each time supplying the next segment of the input stream. If the fold
397+-- has not yet finished it returns a fold that can be run again otherwise it
398+-- returns the fold result and the residual stream.
399+--
400+-- /Internal/
321401{-# INLINE foldEither #-}
322402foldEither :: Monad m =>
323403 Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
@@ -344,6 +424,9 @@ foldEither (FL.Fold step begin done) m = do
344424 FL.Done b1 -> return $ Right (b1, r)
345425 in foldStream defState yieldk single stop m1
346426
427+-- | Like 'fold' but also returns the remaining stream. The resulting stream
428+-- would be 'StreamK.nil' if the stream finished before the fold.
429+--
347430{-# INLINE foldBreak #-}
348431foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
349432foldBreak fld strm = do
@@ -358,6 +441,68 @@ foldBreak fld strm = do
358441 b <- extract s
359442 return (b, nil)
360443
444+-- XXX Array folds can be implemented using this.
445+-- foldContainers? Specialized to foldArrays.
446+
447+-- | Generate streams from individual elements of a stream and fold the
448+-- concatenation of those streams using the supplied fold. Return the result of
449+-- the fold and residual stream.
450+--
451+-- For example, this can be used to efficiently fold an Array Word8 stream
452+-- using Word8 folds.
453+--
454+-- /Internal/
455+{-# INLINE foldConcat #-}
456+foldConcat :: Monad m =>
457+ Producer m a b -> Fold m b c -> StreamK m a -> m (c, StreamK m a)
458+foldConcat
459+ (Producer pstep pinject pextract)
460+ (Fold fstep begin done)
461+ stream = do
462+
463+ res <- begin
464+ case res of
465+ FL.Partial fs -> go fs stream
466+ FL.Done fb -> return (fb, stream)
467+
468+ where
469+
470+ go !acc m1 = do
471+ let stop = do
472+ r <- done acc
473+ return (r, nil)
474+ single a = do
475+ st <- pinject a
476+ res <- go1 SPEC acc st
477+ case res of
478+ Left fs -> do
479+ r <- done fs
480+ return (r, nil)
481+ Right (b, s) -> do
482+ x <- pextract s
483+ return (b, fromPure x)
484+ yieldk a r = do
485+ st <- pinject a
486+ res <- go1 SPEC acc st
487+ case res of
488+ Left fs -> go fs r
489+ Right (b, s) -> do
490+ x <- pextract s
491+ return (b, x `cons` r)
492+ in foldStream defState yieldk single stop m1
493+
494+ {-# INLINE go1 #-}
495+ go1 !_ !fs st = do
496+ r <- pstep st
497+ case r of
498+ Stream.Yield x s -> do
499+ res <- fstep fs x
500+ case res of
501+ FL.Done b -> return $ Right (b, s)
502+ FL.Partial fs1 -> go1 SPEC fs1 s
503+ Stream.Skip s -> go1 SPEC fs s
504+ Stream.Stop -> return $ Left fs
505+
361506-- | Like 'foldl'' but with a monadic step function.
362507{-# INLINE foldlM' #-}
363508foldlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> m b
@@ -993,13 +1138,13 @@ splitAt n ls
9931138 (xs', xs'') = splitAt' (m - 1) xs
9941139
9951140-- | Run a 'Parser' over a stream and return rest of the Stream.
996−{-# INLINE_NORMAL parseBreak #-}
997−parseBreak
1141+{-# INLINE_NORMAL parseBreakD #-}
1142+parseBreakD
9981143 :: Monad m
9991144 => PR.Parser a m b
10001145 -> Stream m a
10011146 -> m (Either ParseError b, Stream m a)
1002−parseBreak (PR.Parser pstep initial extract) stream = do
1147+parseBreakD (PR.Parser pstep initial extract) stream = do
10031148 res <- initial
10041149 case res of
10051150 PR.IPartial s -> goStream stream [] s
@@ -1052,7 +1197,7 @@ parseBreak (PR.Parser pstep initial extract) stream = do
10521197 assertM(n <= length (x:buf))
10531198 let src0 = Prelude.take n (x:buf)
10541199 src = Prelude.reverse src0
1055− return (Right b, serial (fromList src) r)
1200+ return (Right b, append (fromList src) r)
10561201 PR.Error err -> return (Left (ParseError err), r)
10571202 in foldStream defState yieldk single stop st
10581203
@@ -1076,5 +1221,46 @@ parseBreak (PR.Parser pstep initial extract) stream = do
10761221 assert (n <= length (x:buf)) (return ())
10771222 let src0 = Prelude.take n (x:buf)
10781223 src = Prelude.reverse src0
1079− return (Right b, serial (fromList src) st)
1224+ return (Right b, append (fromList src) st)
10801225 PR.Error err -> return (Left (ParseError err), nil)
1226+
1227+-- | Parse a stream using the supplied 'Parser'.
1228+--
1229+-- /CPS/
1230+--
1231+{-# INLINE parseBreak #-}
1232+parseBreak :: Monad m =>
1233+ Parser.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
1234+parseBreak p = parseBreakD (PR.fromParserK p)
1235+
1236+{-# INLINE parse #-}
1237+parse :: Monad m =>
1238+ Parser.Parser a m b -> Stream m a -> m (Either ParseError b)
1239+parse f = fmap fst . parseBreak f
1240+
1241+-- | Sort the input stream using a supplied comparison function.
1242+--
1243+-- Sorting can be achieved by simply:
1244+--
1245+-- >>> sortBy cmp = StreamK.mergeMapWith (StreamK.mergeBy cmp) StreamK.fromPure
1246+--
1247+-- However, this combinator uses a parser to first split the input stream into
1248+-- down and up sorted segments and then merges them to optimize sorting when
1249+-- pre-sorted sequences exist in the input stream.
1250+--
1251+-- /O(n) space/
1252+--
1253+{-# INLINE sortBy #-}
1254+sortBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a
1255+-- sortBy f = Stream.concatPairsWith (Stream.mergeBy f) Stream.fromPure
1256+sortBy cmp =
1257+ let p =
1258+ Parser.groupByRollingEither
1259+ (\x -> (< GT) . cmp x)
1260+ FL.toStreamKRev
1261+ FL.toStreamK
1262+ in mergeMapWith (mergeBy cmp) id
1263+ . Stream.toStreamK
1264+ . Stream.catRights -- its a non-failing backtracking parser
1265+ . Stream.parseMany (fmap (either id id) p)
1266+ . Stream.fromStreamK
core/src/Streamly/Internal/Data/Stream/StreamDK/Type.hs → core/src/Streamly/Internal/Data/Stream/StreamK/Alt.hsrenamed+143−3
@@ -92,13 +92,153 @@
9292-- a push style CPS representation should be able to be used along with StreamK
9393-- to efficiently implement composable folds.
9494
95−module Streamly.Internal.Data.Stream.StreamDK.Type
96− ( Step(..)
97− , Stream (..)
95+module Streamly.Internal.Data.Stream.StreamK.Alt
96+ (
97+ -- * Stream Type
98+
99+ Stream
100+ , Step (..)
101+
102+ -- * Construction
103+ , nil
104+ , cons
105+ , consM
106+ , unfoldr
107+ , unfoldrM
108+ , replicateM
109+
110+ -- * Folding
111+ , uncons
112+ , foldrS
113+
114+ -- * Specific Folds
115+ , drain
98116 )
99117where
100118
119+#include "inline.hs"
120+
101121-- XXX Use Cons and Nil instead of Yield and Stop?
102122data Step m a = Yield a (Stream m a) | Stop
103123
104124newtype Stream m a = Stream (m (Step m a))
125+
126+-------------------------------------------------------------------------------
127+-- Construction
128+-------------------------------------------------------------------------------
129+
130+nil :: Monad m => Stream m a
131+nil = Stream $ return Stop
132+
133+{-# INLINE_NORMAL cons #-}
134+cons :: Monad m => a -> Stream m a -> Stream m a
135+cons x xs = Stream $ return $ Yield x xs
136+
137+consM :: Monad m => m a -> Stream m a -> Stream m a
138+consM eff xs = Stream $ eff >>= \x -> return $ Yield x xs
139+
140+unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a
141+unfoldrM next state = Stream (step' state)
142+ where
143+ step' st = do
144+ r <- next st
145+ return $ case r of
146+ Just (x, s) -> Yield x (Stream (step' s))
147+ Nothing -> Stop
148+{-
149+unfoldrM next s0 = buildM $ \yld stp ->
150+ let go s = do
151+ r <- next s
152+ case r of
153+ Just (a, b) -> yld a (go b)
154+ Nothing -> stp
155+ in go s0
156+-}
157+
158+{-# INLINE unfoldr #-}
159+unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a
160+unfoldr next s0 = build $ \yld stp ->
161+ let go s =
162+ case next s of
163+ Just (a, b) -> yld a (go b)
164+ Nothing -> stp
165+ in go s0
166+
167+replicateM :: Monad m => Int -> a -> Stream m a
168+replicateM n x = Stream (step n)
169+ where
170+ step i = return $
171+ if i <= 0
172+ then Stop
173+ else Yield x (Stream (step (i - 1)))
174+
175+-------------------------------------------------------------------------------
176+-- Folding
177+-------------------------------------------------------------------------------
178+
179+uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
180+uncons (Stream step) = do
181+ r <- step
182+ return $ case r of
183+ Yield x xs -> Just (x, xs)
184+ Stop -> Nothing
185+
186+-- | Lazy right associative fold to a stream.
187+{-# INLINE_NORMAL foldrS #-}
188+foldrS :: Monad m
189+ => (a -> Stream m b -> Stream m b)
190+ -> Stream m b
191+ -> Stream m a
192+ -> Stream m b
193+foldrS f streamb = go
194+ where
195+ go (Stream stepa) = Stream $ do
196+ r <- stepa
197+ case r of
198+ Yield x xs -> let Stream step = f x (go xs) in step
199+ Stop -> let Stream step = streamb in step
200+
201+{-# INLINE_LATE foldrM #-}
202+foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
203+foldrM fstep acc = go
204+ where
205+ go (Stream step) = do
206+ r <- step
207+ case r of
208+ Yield x xs -> fstep x (go xs)
209+ Stop -> acc
210+
211+{-# INLINE_NORMAL build #-}
212+build :: Monad m
213+ => forall a. (forall b. (a -> b -> b) -> b -> b) -> Stream m a
214+build g = g cons nil
215+
216+{-# RULES
217+"foldrM/build" forall k z (g :: forall b. (a -> b -> b) -> b -> b).
218+ foldrM k z (build g) = g k z #-}
219+
220+{-
221+-- To fuse foldrM with unfoldrM we need the type m1 to be polymorphic such that
222+-- it is either Monad m or Stream m. So that we can use cons/nil as well as
223+-- monadic construction function as its arguments.
224+--
225+{-# INLINE_NORMAL buildM #-}
226+buildM :: Monad m
227+ => forall a. (forall b. (a -> m1 b -> m1 b) -> m1 b -> m1 b) -> Stream m a
228+buildM g = g cons nil
229+-}
230+
231+-------------------------------------------------------------------------------
232+-- Specific folds
233+-------------------------------------------------------------------------------
234+
235+{-# INLINE drain #-}
236+drain :: Monad m => Stream m a -> m ()
237+drain = foldrM (\_ xs -> xs) (return ())
238+{-
239+drain (Stream step) = do
240+ r <- step
241+ case r of
242+ Yield _ next -> drain next
243+ Stop -> return ()
244+ -}
core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs+754−69
@@ -1,3 +1,4 @@
1+{-# LANGUAGE UndecidableInstances #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamK.Type
34-- Copyright : (c) 2017 Composewell Technologies
@@ -13,12 +14,16 @@
1314--
1415module Streamly.Internal.Data.Stream.StreamK.Type
1516 (
16− -- * The stream type
17− Stream (..)
18− , toStreamK
19− , fromStreamK
17+ -- * StreamK type
18+ Stream (..) -- XXX stop exporting this
19+ , StreamK
2020
21− -- * foldr/build
21+ -- * CrossStreamK type wrapper
22+ , CrossStreamK
23+ , unCross
24+ , mkCross
25+
26+ -- * foldr/build Fusion
2227 , mkStream
2328 , foldStream
2429 , foldStreamShared
@@ -32,8 +37,10 @@ module Streamly.Internal.Data.Stream.StreamK.Type
3237 , buildSM
3338 , augmentS
3439 , augmentSM
40+ , unShare
3541
3642 -- * Construction
43+ -- ** Primitives
3744 , fromStopK
3845 , fromYieldK
3946 , consK
@@ -44,58 +51,89 @@ module Streamly.Internal.Data.Stream.StreamK.Type
4451 , nil
4552 , nilM
4653
47− -- * Generation
48− , fromEffect
49− , fromPure
54+ -- ** Unfolding
5055 , unfoldr
5156 , unfoldrMWith
57+ , unfoldrM
58+
59+ -- ** From Values
60+ , fromEffect
61+ , fromPure
5262 , repeat
5363 , repeatMWith
5464 , replicateMWith
65+
66+ -- ** From Indices
5567 , fromIndicesMWith
68+
69+ -- ** Iteration
5670 , iterateMWith
71+
72+ -- ** From Containers
5773 , fromFoldable
5874 , fromFoldableM
75+
76+ -- ** Cyclic
5977 , mfix
6078
6179 -- * Elimination
80+ -- ** Primitives
6281 , uncons
63− , foldl'
82+
83+ -- ** Strict Left Folds
84+ , Streamly.Internal.Data.Stream.StreamK.Type.foldl'
6485 , foldlx'
86+
87+ -- ** Lazy Right Folds
88+ , Streamly.Internal.Data.Stream.StreamK.Type.foldr
89+
90+ -- ** Specific Folds
6591 , drain
6692 , null
6793 , tail
6894 , init
6995
70− -- * Transformation
71− , conjoin
72− , serial
96+ -- * Mapping
7397 , map
7498 , mapMWith
7599 , mapMSerial
76− , unShare
77100
101+ -- * Combining Two Streams
102+ -- ** Appending
103+ , conjoin
104+ , append
105+
106+ -- ** Interleave
107+ , interleave
108+ , interleaveFst
109+ , interleaveMin
110+
111+ -- ** Cross Product
78112 , crossApplyWith
79113 , crossApply
80114 , crossApplySnd
81115 , crossApplyFst
116+ , crossWith
117+ , cross
82118
119+ -- * Concat
120+ , before
121+ , concatEffect
122+ , concatMapEffect
83123 , concatMapWith
84124 , concatMap
85125 , bindWith
86− , concatPairsWith
126+ , concatIterateWith
127+ , concatIterateLeftsWith
128+ , concatIterateScanWith
87129
130+ -- * Merge
131+ , mergeMapWith
132+ , mergeIterateWith
133+
134+ -- * Buffered Operations
88135 , foldlS
89136 , reverse
90−
91− , before
92− , concatEffect
93− , concatMapEffect
94−
95− -- * Interleave
96− , interleave
97− , interleaveFst
98− , interleaveMin
99137 )
100138where
101139
@@ -103,21 +141,44 @@ where
103141
104142-- import Control.Applicative (liftA2)
105143import Control.Monad ((>=>))
144+import Control.Monad.Catch (MonadThrow, throwM)
145+import Control.Monad.Trans.Class (MonadTrans(lift))
146+import Control.Applicative (liftA2)
147+import Control.Monad.IO.Class (MonadIO(..))
148+import Data.Foldable (Foldable(foldl'), fold, foldr)
106149import Data.Function (fix)
150+import Data.Functor.Identity (Identity(..))
151+import Data.Maybe (fromMaybe)
152+import Data.Semigroup (Endo(..))
153+import GHC.Exts (IsList(..), IsString(..), oneShot)
154+import Streamly.Internal.BaseCompat ((#.))
155+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
107156import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
157+import Text.Read
158+ ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
159+ , readListPrecDefault)
108160
109161import qualified Prelude
110162
111163import Prelude hiding
112164 (map, mapM, concatMap, foldr, repeat, null, reverse, tail, init)
113165
166+-- $setup
167+-- >>> import Data.Function (fix, (&))
168+-- >>> import Data.Semigroup (cycle1)
169+-- >>> import Streamly.Internal.Data.Stream.StreamK (CrossStreamK(..))
170+-- >>> import qualified Streamly.Data.Fold as Fold
171+-- >>> import qualified Streamly.Data.Stream as Stream
172+-- >>> import qualified Streamly.Data.Stream.StreamK as StreamK
173+-- >>> import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
174+-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
175+
114176------------------------------------------------------------------------------
115177-- Basic stream type
116178------------------------------------------------------------------------------
117179
118−-- | The type @Stream m a@ represents a monadic stream of values of type 'a'
119−-- constructed using actions in monad 'm'. It uses stop, singleton and yield
120−-- continuations equivalent to the following direct style type:
180+-- It uses stop, singleton and yield continuations equivalent to the following
181+-- direct style type:
121182--
122183-- @
123184-- data Stream m a = Stop | Singleton a | Yield a (Stream m a)
@@ -132,8 +193,17 @@ import Prelude hiding
132193-- single element. We build singleton streams in the implementation of 'pure'
133194-- for Applicative and Monad, and in 'lift' for MonadTrans.
134195
135−-- XXX remove the Stream type parameter from State as it is always constant.
136−-- We can remove it from SVar as well
196+-- XXX remove the State param.
197+
198+-- | Continuation Passing Style (CPS) version of "Streamly.Data.Stream.Stream".
199+-- Unlike "Streamly.Data.Stream.Stream", 'StreamK' can be composed recursively
200+-- without affecting performance.
201+--
202+-- Semigroup instance appends two streams:
203+--
204+-- >>> (<>) = Stream.append
205+--
206+type StreamK = Stream
137207
138208newtype Stream m a =
139209 MkStream (forall r.
@@ -144,14 +214,6 @@ newtype Stream m a =
144214 -> m r
145215 )
146216
147−{-# INLINE fromStreamK #-}
148−fromStreamK :: Stream m a -> Stream m a
149−fromStreamK = id
150−
151−{-# INLINE toStreamK #-}
152−toStreamK :: Stream m a -> Stream m a
153−toStreamK = id
154−
155217mkStream
156218 :: (forall r. State Stream m a
157219 -> (a -> Stream m a -> m r)
@@ -195,17 +257,22 @@ consK k r = mkStream $ \_ yld _ _ -> k (`yld` r)
195257infixr 5 `cons`
196258
197259-- faster than consM because there is no bind.
198−-- | Construct a stream by adding a pure value at the head of an existing
199−-- stream. For serial streams this is the same as @(return a) \`consM` r@ but
200−-- more efficient. For concurrent streams this is not concurrent whereas
201−-- 'consM' is concurrent. For example:
260+
261+-- | A right associative prepend operation to add a pure value at the head of
262+-- an existing stream::
202263--
203−-- @
204−-- > toList $ 1 \`cons` 2 \`cons` 3 \`cons` nil
264+-- >>> s = 1 `StreamK.cons` 2 `StreamK.cons` 3 `StreamK.cons` StreamK.nil
265+-- >>> Stream.fold Fold.toList (StreamK.toStream s)
205266-- [1,2,3]
206−-- @
207267--
208−-- @since 0.1.0
268+-- It can be used efficiently with 'Prelude.foldr':
269+--
270+-- >>> fromFoldable = Prelude.foldr StreamK.cons StreamK.nil
271+--
272+-- Same as the following but more efficient:
273+--
274+-- >>> cons x xs = return x `StreamK.consM` xs
275+--
209276{-# INLINE_NORMAL cons #-}
210277cons :: a -> Stream m a -> Stream m a
211278cons a r = mkStream $ \_ yield _ _ -> yield a r
@@ -224,35 +291,44 @@ infixr 5 .:
224291(.:) :: a -> Stream m a -> Stream m a
225292(.:) = cons
226293
227−-- | An empty stream.
294+-- | A stream that terminates without producing any output or side effect.
228295--
229−-- @
230−-- > toList nil
296+-- >>> Stream.fold Fold.toList (StreamK.toStream StreamK.nil)
231297-- []
232−-- @
233298--
234−-- @since 0.1.0
235299{-# INLINE_NORMAL nil #-}
236300nil :: Stream m a
237301nil = mkStream $ \_ _ _ stp -> stp
238302
239−-- | An empty stream producing a side effect.
303+-- | A stream that terminates without producing any output, but produces a side
304+-- effect.
240305--
241−-- @
242−-- > toList (nilM (print "nil"))
306+-- >>> Stream.fold Fold.toList (StreamK.toStream (StreamK.nilM (print "nil")))
243307-- "nil"
244308-- []
245−-- @
246309--
247310-- /Pre-release/
248311{-# INLINE_NORMAL nilM #-}
249312nilM :: Applicative m => m b -> Stream m a
250313nilM m = mkStream $ \_ _ _ stp -> m *> stp
251314
315+-- | Create a singleton stream from a pure value.
316+--
317+-- >>> fromPure a = a `cons` StreamK.nil
318+-- >>> fromPure = pure
319+-- >>> fromPure = StreamK.fromEffect . pure
320+--
252321{-# INLINE_NORMAL fromPure #-}
253322fromPure :: a -> Stream m a
254323fromPure a = mkStream $ \_ _ single _ -> single a
255324
325+-- | Create a singleton stream from a monadic action.
326+--
327+-- >>> fromEffect m = m `consM` StreamK.nil
328+--
329+-- >>> Stream.fold Fold.drain $ StreamK.toStream $ StreamK.fromEffect (putStrLn "hello")
330+-- hello
331+--
256332{-# INLINE_NORMAL fromEffect #-}
257333fromEffect :: Monad m => m a -> Stream m a
258334fromEffect m = mkStream $ \_ _ single _ -> m >>= single
@@ -262,6 +338,23 @@ infixr 5 `consM`
262338-- NOTE: specializing the function outside the instance definition seems to
263339-- improve performance quite a bit at times, even if we have the same
264340-- SPECIALIZE in the instance definition.
341+
342+-- | A right associative prepend operation to add an effectful value at the
343+-- head of an existing stream::
344+--
345+-- >>> s = putStrLn "hello" `consM` putStrLn "world" `consM` StreamK.nil
346+-- >>> Stream.fold Fold.drain (StreamK.toStream s)
347+-- hello
348+-- world
349+--
350+-- It can be used efficiently with 'Prelude.foldr':
351+--
352+-- >>> fromFoldableM = Prelude.foldr StreamK.consM StreamK.nil
353+--
354+-- Same as the following but more efficient:
355+--
356+-- >>> consM x xs = StreamK.fromEffect x `StreamK.append` xs
357+--
265358{-# INLINE consM #-}
266359{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
267360consM :: Monad m => m a -> Stream m a -> Stream m a
@@ -361,7 +454,34 @@ foldrSShared = foldrSWith foldStreamShared
361454-- {-# RULES "foldrSShared/app" [1]
362455-- forall ys. foldrSShared consM ys = \xs -> xs `conjoin` ys #-}
363456
364−-- | Lazy right associative fold to a stream.
457+-- | Right fold to a streaming monad.
458+--
459+-- > foldrS StreamK.cons StreamK.nil === id
460+--
461+-- 'foldrS' can be used to perform stateless stream to stream transformations
462+-- like map and filter in general. It can be coupled with a scan to perform
463+-- stateful transformations. However, note that the custom map and filter
464+-- routines can be much more efficient than this due to better stream fusion.
465+--
466+-- >>> input = StreamK.fromStream $ Stream.fromList [1..5]
467+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS StreamK.cons StreamK.nil input
468+-- [1,2,3,4,5]
469+--
470+-- Find if any element in the stream is 'True':
471+--
472+-- >>> step x xs = if odd x then StreamK.fromPure True else xs
473+-- >>> input = StreamK.fromStream (Stream.fromList (2:4:5:undefined)) :: StreamK IO Int
474+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS step (StreamK.fromPure False) input
475+-- [True]
476+--
477+-- Map (+2) on odd elements and filter out the even elements:
478+--
479+-- >>> step x xs = if odd x then (x + 2) `StreamK.cons` xs else xs
480+-- >>> input = StreamK.fromStream (Stream.fromList [1..5]) :: Stream IO Int
481+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS step StreamK.nil input
482+-- [3,5,7]
483+--
484+-- /Pre-release/
365485{-# INLINE_NORMAL foldrS #-}
366486foldrS ::
367487 (a -> Stream m b -> Stream m b)
@@ -741,16 +861,25 @@ null m =
741861-- Semigroup
742862------------------------------------------------------------------------------
743863
744−infixr 6 `serial`
864+infixr 6 `append`
745865
746866-- | Appends two streams sequentially, yielding all elements from the first
747867-- stream, and then all elements from the second stream.
748868--
749−{-# INLINE serial #-}
750−serial :: Stream m a -> Stream m a -> Stream m a
869+-- >>> s1 = StreamK.fromStream $ Stream.fromList [1,2]
870+-- >>> s2 = StreamK.fromStream $ Stream.fromList [3,4]
871+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ s1 `StreamK.append` s2
872+-- [1,2,3,4]
873+--
874+-- This has O(n) append performance where @n@ is the number of streams. It can
875+-- be used to efficiently fold an infinite lazy container of streams using
876+-- 'concatMapWith' et. al.
877+--
878+{-# INLINE append #-}
879+append :: Stream m a -> Stream m a -> Stream m a
751880-- XXX This doubles the time of toNullAp benchmark, may not be fusing properly
752881-- serial xs ys = augmentS (\c n -> foldrS c n xs) ys
753−serial m1 m2 = go m1
882+append m1 m2 = go m1
754883 where
755884 go m = mkStream $ \st yld sng stp ->
756885 let stop = foldStream st yld sng stp m2
@@ -764,7 +893,7 @@ conjoin :: Monad m => Stream m a -> Stream m a -> Stream m a
764893conjoin xs = augmentSM (\c n -> foldrSM c n xs)
765894
766895instance Semigroup (Stream m a) where
767− (<>) = serial
896+ (<>) = append
768897
769898------------------------------------------------------------------------------
770899-- Monoid
@@ -861,6 +990,163 @@ mapMWith cns f = go
861990instance Monad m => Functor (Stream m) where
862991 fmap = map
863992
993+------------------------------------------------------------------------------
994+-- Lists
995+------------------------------------------------------------------------------
996+
997+-- Serial streams can act like regular lists using the Identity monad
998+
999+-- XXX Show instance is 10x slower compared to read, we can do much better.
1000+-- The list show instance itself is really slow.
1001+
1002+-- XXX The default definitions of "<" in the Ord instance etc. do not perform
1003+-- well, because they do not get inlined. Need to add INLINE in Ord class in
1004+-- base?
1005+
1006+instance IsList (Stream Identity a) where
1007+ type (Item (Stream Identity a)) = a
1008+
1009+ {-# INLINE fromList #-}
1010+ fromList = fromFoldable
1011+
1012+ {-# INLINE toList #-}
1013+ toList = Data.Foldable.foldr (:) []
1014+
1015+-- XXX Fix these
1016+{-
1017+instance Eq a => Eq (Stream Identity a) where
1018+ {-# INLINE (==) #-}
1019+ (==) xs ys = runIdentity $ eqBy (==) xs ys
1020+
1021+instance Ord a => Ord (Stream Identity a) where
1022+ {-# INLINE compare #-}
1023+ compare xs ys = runIdentity $ cmpBy compare xs ys
1024+
1025+ {-# INLINE (<) #-}
1026+ x < y =
1027+ case compare x y of
1028+ LT -> True
1029+ _ -> False
1030+
1031+ {-# INLINE (<=) #-}
1032+ x <= y =
1033+ case compare x y of
1034+ GT -> False
1035+ _ -> True
1036+
1037+ {-# INLINE (>) #-}
1038+ x > y =
1039+ case compare x y of
1040+ GT -> True
1041+ _ -> False
1042+
1043+ {-# INLINE (>=) #-}
1044+ x >= y =
1045+ case compare x y of
1046+ LT -> False
1047+ _ -> True
1048+
1049+ {-# INLINE max #-}
1050+ max x y = if x <= y then y else x
1051+
1052+ {-# INLINE min #-}
1053+ min x y = if x <= y then x else y
1054+-}
1055+
1056+instance Show a => Show (Stream Identity a) where
1057+ showsPrec p dl = showParen (p > 10) $
1058+ showString "fromList " . shows (toList dl)
1059+
1060+instance Read a => Read (Stream Identity a) where
1061+ readPrec = parens $ prec 10 $ do
1062+ Ident "fromList" <- lexP
1063+ fromList <$> readPrec
1064+
1065+ readListPrec = readListPrecDefault
1066+
1067+instance (a ~ Char) => IsString (Stream Identity a) where
1068+ {-# INLINE fromString #-}
1069+ fromString = fromList
1070+
1071+-------------------------------------------------------------------------------
1072+-- Foldable
1073+-------------------------------------------------------------------------------
1074+
1075+-- | Lazy right associative fold.
1076+{-# INLINE foldr #-}
1077+foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
1078+foldr step acc = foldrM (\x xs -> xs >>= \b -> return (step x b)) (return acc)
1079+
1080+-- The default Foldable instance has several issues:
1081+-- 1) several definitions do not have INLINE on them, so we provide
1082+-- re-implementations with INLINE pragmas.
1083+-- 2) the definitions of sum/product/maximum/minimum are inefficient as they
1084+-- use right folds, they cannot run in constant memory. We provide
1085+-- implementations using strict left folds here.
1086+
1087+instance (Foldable m, Monad m) => Foldable (Stream m) where
1088+
1089+ {-# INLINE foldMap #-}
1090+ foldMap f =
1091+ fold
1092+ . Streamly.Internal.Data.Stream.StreamK.Type.foldr (mappend . f) mempty
1093+
1094+ {-# INLINE foldr #-}
1095+ foldr f z t = appEndo (foldMap (Endo #. f) t) z
1096+
1097+ {-# INLINE foldl' #-}
1098+ foldl' f z0 xs = Data.Foldable.foldr f' id xs z0
1099+ where f' x k = oneShot $ \z -> k $! f z x
1100+
1101+ {-# INLINE length #-}
1102+ length = Data.Foldable.foldl' (\n _ -> n + 1) 0
1103+
1104+ {-# INLINE elem #-}
1105+ elem = any . (==)
1106+
1107+ {-# INLINE maximum #-}
1108+ maximum =
1109+ fromMaybe (errorWithoutStackTrace "maximum: empty stream")
1110+ . toMaybe
1111+ . Data.Foldable.foldl' getMax Nothing'
1112+
1113+ where
1114+
1115+ getMax Nothing' x = Just' x
1116+ getMax (Just' mx) x = Just' $! max mx x
1117+
1118+ {-# INLINE minimum #-}
1119+ minimum =
1120+ fromMaybe (errorWithoutStackTrace "minimum: empty stream")
1121+ . toMaybe
1122+ . Data.Foldable.foldl' getMin Nothing'
1123+
1124+ where
1125+
1126+ getMin Nothing' x = Just' x
1127+ getMin (Just' mn) x = Just' $! min mn x
1128+
1129+ {-# INLINE sum #-}
1130+ sum = Data.Foldable.foldl' (+) 0
1131+
1132+ {-# INLINE product #-}
1133+ product = Data.Foldable.foldl' (*) 1
1134+
1135+-------------------------------------------------------------------------------
1136+-- Traversable
1137+-------------------------------------------------------------------------------
1138+
1139+instance Traversable (Stream Identity) where
1140+ {-# INLINE traverse #-}
1141+ traverse f xs =
1142+ runIdentity
1143+ $ Streamly.Internal.Data.Stream.StreamK.Type.foldr
1144+ consA (pure mempty) xs
1145+
1146+ where
1147+
1148+ consA x ys = liftA2 cons (f x) ys
1149+
8641150-------------------------------------------------------------------------------
8651151-- Nesting
8661152-------------------------------------------------------------------------------
@@ -895,6 +1181,15 @@ crossApplyWith par fstream stream = go1 fstream
8951181 yieldk a r = yld (f a) (go2 f r)
8961182 in foldStream (adaptState st) yieldk single stp m
8971183
1184+-- | Apply a stream of functions to a stream of values and flatten the results.
1185+--
1186+-- Note that the second stream is evaluated multiple times.
1187+--
1188+-- Definition:
1189+--
1190+-- >>> crossApply = StreamK.crossApplyWith StreamK.append
1191+-- >>> crossApply = Stream.crossWith id
1192+--
8981193{-# INLINE crossApply #-}
8991194crossApply ::
9001195 Stream m (a -> b)
@@ -979,6 +1274,39 @@ crossApplyFst fstream stream = go1 fstream
9791274 yieldk _ r = yld f (go3 f r)
9801275 in foldStream (adaptState st) yieldk single stp m
9811276
1277+-- |
1278+-- Definition:
1279+--
1280+-- >>> crossWith f m1 m2 = fmap f m1 `StreamK.crossApply` m2
1281+--
1282+-- Note that the second stream is evaluated multiple times.
1283+--
1284+{-# INLINE crossWith #-}
1285+crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
1286+crossWith f m1 m2 = fmap f m1 `crossApply` m2
1287+
1288+-- | Given a @Stream m a@ and @Stream m b@ generate a stream with all possible
1289+-- combinations of the tuple @(a, b)@.
1290+--
1291+-- Definition:
1292+--
1293+-- >>> cross = StreamK.crossWith (,)
1294+--
1295+-- The second stream is evaluated multiple times. If that is not desired it can
1296+-- be cached in an 'Data.Array.Array' and then generated from the array before
1297+-- calling this function. Caching may also improve performance if the stream is
1298+-- expensive to evaluate.
1299+--
1300+-- See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused
1301+-- alternative.
1302+--
1303+-- Time: O(m x n)
1304+--
1305+-- /Pre-release/
1306+{-# INLINE cross #-}
1307+cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)
1308+cross = crossWith (,)
1309+
9821310-- XXX This is just concatMapWith with arguments flipped. We need to keep this
9831311-- instead of using a concatMap style definition because the bind
9841312-- implementation in Async and WAsync streams show significant perf degradation
@@ -1010,7 +1338,6 @@ bindWith par m1 f = go m1
10101338-- argument specifies a merge or concat function that is used to merge the
10111339-- streams generated by the map function.
10121340--
1013−-- @since 0.7.0
10141341{-# INLINE concatMapWith #-}
10151342concatMapWith
10161343 ::
@@ -1022,7 +1349,7 @@ concatMapWith par f xs = bindWith par xs f
10221349
10231350{-# INLINE concatMap #-}
10241351concatMap :: (a -> Stream m b) -> Stream m a -> Stream m b
1025−concatMap = concatMapWith serial
1352+concatMap = concatMapWith append
10261353
10271354{-
10281355-- Fused version.
@@ -1042,17 +1369,34 @@ concatMap_ f xs = buildS
10421369 (\c n -> foldrSShared (\x b -> foldrSShared c b (unShare $ f x)) n xs)
10431370-}
10441371
1045−-- | See 'Streamly.Internal.Data.Stream.concatPairsWith' for
1046−-- documentation.
1372+-- | Combine streams in pairs using a binary combinator, the resulting streams
1373+-- are then combined again in pairs recursively until we get to a single
1374+-- combined stream. The composition would thus form a binary tree.
1375+--
1376+-- For example, you can sort a stream using merge sort like this:
1377+--
1378+-- >>> s = StreamK.fromStream $ Stream.fromList [5,1,7,9,2]
1379+-- >>> generate = StreamK.fromPure
1380+-- >>> combine = StreamK.mergeBy compare
1381+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.mergeMapWith combine generate s
1382+-- [1,2,5,7,9]
1383+--
1384+-- Note that if the stream length is not a power of 2, the binary tree composed
1385+-- by mergeMapWith would not be balanced, which may or may not be important
1386+-- depending on what you are trying to achieve.
1387+--
1388+-- /Caution: the stream of streams must be finite/
10471389--
1048−{-# INLINE concatPairsWith #-}
1049−concatPairsWith
1390+-- /Pre-release/
1391+--
1392+{-# INLINE mergeMapWith #-}
1393+mergeMapWith
10501394 ::
10511395 (Stream m b -> Stream m b -> Stream m b)
10521396 -> (a -> Stream m b)
10531397 -> Stream m a
10541398 -> Stream m b
1055−concatPairsWith combine f str = go (leafPairs str)
1399+mergeMapWith combine f str = go (leafPairs str)
10561400
10571401 where
10581402
@@ -1139,17 +1483,172 @@ concatUnfoldr :: IsStream t
11391483concatUnfoldr = undefined
11401484-}
11411485
1486+------------------------------------------------------------------------------
1487+-- concatIterate - Map and flatten Trees of Streams
1488+------------------------------------------------------------------------------
1489+
1490+-- | Yield an input element in the output stream, map a stream generator on it
1491+-- and repeat the process on the resulting stream. Resulting streams are
1492+-- flattened using the 'concatMapWith' combinator. This can be used for a depth
1493+-- first style (DFS) traversal of a tree like structure.
1494+--
1495+-- Example, list a directory tree using DFS:
1496+--
1497+-- >>> f = StreamK.fromStream . either Dir.readEitherPaths (const Stream.nil)
1498+-- >>> input = StreamK.fromPure (Left ".")
1499+-- >>> ls = StreamK.concatIterateWith StreamK.append f input
1500+--
1501+-- Note that 'iterateM' is a special case of 'concatIterateWith':
1502+--
1503+-- >>> iterateM f = StreamK.concatIterateWith StreamK.append (StreamK.fromEffect . f) . StreamK.fromEffect
1504+--
1505+-- /Pre-release/
1506+--
1507+{-# INLINE concatIterateWith #-}
1508+concatIterateWith ::
1509+ (Stream m a -> Stream m a -> Stream m a)
1510+ -> (a -> Stream m a)
1511+ -> Stream m a
1512+ -> Stream m a
1513+concatIterateWith combine f = iterateStream
1514+
1515+ where
1516+
1517+ iterateStream = concatMapWith combine generate
1518+
1519+ generate x = x `cons` iterateStream (f x)
1520+
1521+-- | Like 'concatIterateWith' but uses the pairwise flattening combinator
1522+-- 'mergeMapWith' for flattening the resulting streams. This can be used for a
1523+-- balanced traversal of a tree like structure.
1524+--
1525+-- Example, list a directory tree using balanced traversal:
1526+--
1527+-- >>> f = StreamK.fromStream . either Dir.readEitherPaths (const Stream.nil)
1528+-- >>> input = StreamK.fromPure (Left ".")
1529+-- >>> ls = StreamK.mergeIterateWith StreamK.interleave f input
1530+--
1531+-- /Pre-release/
1532+--
1533+{-# INLINE mergeIterateWith #-}
1534+mergeIterateWith ::
1535+ (Stream m a -> Stream m a -> Stream m a)
1536+ -> (a -> Stream m a)
1537+ -> Stream m a
1538+ -> Stream m a
1539+mergeIterateWith combine f = iterateStream
1540+
1541+ where
1542+
1543+ iterateStream = mergeMapWith combine generate
1544+
1545+ generate x = x `cons` iterateStream (f x)
1546+
1547+------------------------------------------------------------------------------
1548+-- Flattening Graphs
1549+------------------------------------------------------------------------------
1550+
1551+-- To traverse graphs we need a state to be carried around in the traversal.
1552+-- For example, we can use a hashmap to store the visited status of nodes.
1553+
1554+-- | Like 'iterateMap' but carries a state in the stream generation function.
1555+-- This can be used to traverse graph like structures, we can remember the
1556+-- visited nodes in the state to avoid cycles.
1557+--
1558+-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
1559+-- traverse graphs. However, this function provides a more localized state
1560+-- instead of using a global state.
1561+--
1562+-- See also: 'mfix'
1563+--
1564+-- /Pre-release/
1565+--
1566+{-# INLINE concatIterateScanWith #-}
1567+concatIterateScanWith
1568+ :: Monad m
1569+ => (Stream m a -> Stream m a -> Stream m a)
1570+ -> (b -> a -> m (b, Stream m a))
1571+ -> m b
1572+ -> Stream m a
1573+ -> Stream m a
1574+concatIterateScanWith combine f initial stream =
1575+ concatEffect $ do
1576+ b <- initial
1577+ iterateStream (b, stream)
1578+
1579+ where
1580+
1581+ iterateStream (b, s) = pure $ concatMapWith combine (generate b) s
1582+
1583+ generate b a = a `cons` feedback b a
1584+
1585+ feedback b a = concatEffect $ f b a >>= iterateStream
1586+
1587+------------------------------------------------------------------------------
1588+-- Either streams
1589+------------------------------------------------------------------------------
1590+
1591+-- Keep concating either streams as long as rights are generated, stop as soon
1592+-- as a left is generated and concat the left stream.
1593+--
1594+-- See also: 'handle'
1595+--
1596+-- /Unimplemented/
1597+--
1598+{-
1599+concatMapEitherWith
1600+ :: (forall x. t m x -> t m x -> t m x)
1601+ -> (a -> t m (Either (Stream m b) b))
1602+ -> Stream m a
1603+ -> Stream m b
1604+concatMapEitherWith = undefined
1605+-}
1606+
1607+-- XXX We should prefer using the Maybe stream returning signatures over this.
1608+-- This API should perhaps be removed in favor of those.
1609+
1610+-- | In an 'Either' stream iterate on 'Left's. This is a special case of
1611+-- 'concatIterateWith':
1612+--
1613+-- >>> concatIterateLeftsWith combine f = StreamK.concatIterateWith combine (either f (const StreamK.nil))
1614+--
1615+-- To traverse a directory tree:
1616+--
1617+-- >>> input = StreamK.fromPure (Left ".")
1618+-- >>> ls = StreamK.concatIterateLeftsWith StreamK.append (StreamK.fromStream . Dir.readEither) input
1619+--
1620+-- /Pre-release/
1621+--
1622+{-# INLINE concatIterateLeftsWith #-}
1623+concatIterateLeftsWith
1624+ :: (b ~ Either a c)
1625+ => (Stream m b -> Stream m b -> Stream m b)
1626+ -> (a -> Stream m b)
1627+ -> Stream m b
1628+ -> Stream m b
1629+concatIterateLeftsWith combine f =
1630+ concatIterateWith combine (either f (const nil))
1631+
11421632------------------------------------------------------------------------------
11431633-- Interleaving
11441634------------------------------------------------------------------------------
11451635
1636+infixr 6 `interleave`
1637+
11461638-- Additionally we can have m elements yield from the first stream and n
11471639-- elements yielding from the second stream. We can also have time slicing
11481640-- variants of positional interleaving, e.g. run first stream for m seconds and
11491641-- run the second stream for n seconds.
1150−--
1151−-- Similar combinators can be implemented using WAhead style.
11521642
1643+-- | Interleaves two streams, yielding one element from each stream
1644+-- alternately. When one stream stops the rest of the other stream is used in
1645+-- the output stream.
1646+--
1647+-- When joining many streams in a left associative manner earlier streams will
1648+-- get exponential priority than the ones joining later. Because of exponential
1649+-- weighting it can be used with 'concatMapWith' even on a large number of
1650+-- streams.
1651+--
11531652{-# INLINE interleave #-}
11541653interleave :: Stream m a -> Stream m a -> Stream m a
11551654interleave m1 m2 = mkStream $ \st yld sng stp -> do
@@ -1158,7 +1657,9 @@ interleave m1 m2 = mkStream $ \st yld sng stp -> do
11581657 yieldk a r = yld a (interleave m2 r)
11591658 foldStream st yieldk single stop m1
11601659
1161−-- | Like `interleaveK` but stops interleaving as soon as the first stream stops.
1660+infixr 6 `interleaveFst`
1661+
1662+-- | Like `interleave` but stops interleaving as soon as the first stream stops.
11621663--
11631664{-# INLINE interleaveFst #-}
11641665interleaveFst :: Stream m a -> Stream m a -> Stream m a
@@ -1174,6 +1675,11 @@ interleaveFst m1 m2 = mkStream $ \st yld sng stp -> do
11741675 yieldk a r = yld a (interleave s1 r)
11751676 in foldStream st yieldk single stop s2
11761677
1678+infixr 6 `interleaveMin`
1679+
1680+-- | Like `interleave` but stops interleaving as soon as any of the two streams
1681+-- stops.
1682+--
11771683{-# INLINE interleaveMin #-}
11781684interleaveMin :: Stream m a -> Stream m a -> Stream m a
11791685interleaveMin m1 m2 = mkStream $ \st yld _ stp -> do
@@ -1214,6 +1720,10 @@ unfoldrMWith cns step = go
12141720 Just (a, b) -> yld a (go b)
12151721 Nothing -> stp
12161722
1723+{-# INLINE unfoldrM #-}
1724+unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> Stream m a
1725+unfoldrM = unfoldrMWith consM
1726+
12171727-- | Generate an infinite stream by repeating a pure value.
12181728--
12191729-- /Pre-release/
@@ -1274,6 +1784,56 @@ tailPartial m = mkStream $ \st yld sng stp ->
12741784 yieldk _ r = foldStream st yld sng stp r
12751785 in foldStream st yieldk single stop m
12761786
1787+-- | We can define cyclic structures using @let@:
1788+--
1789+-- >>> let (a, b) = ([1, b], head a) in (a, b)
1790+-- ([1,1],1)
1791+--
1792+-- The function @fix@ defined as:
1793+--
1794+-- >>> fix f = let x = f x in x
1795+--
1796+-- ensures that the argument of a function and its output refer to the same
1797+-- lazy value @x@ i.e. the same location in memory. Thus @x@ can be defined
1798+-- in terms of itself, creating structures with cyclic references.
1799+--
1800+-- >>> f ~(a, b) = ([1, b], head a)
1801+-- >>> fix f
1802+-- ([1,1],1)
1803+--
1804+-- 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic
1805+-- values.
1806+--
1807+-- Using 'mfix' for streams we can construct a stream in which each element of
1808+-- the stream is defined in a cyclic fashion. The argument of the function
1809+-- being fixed represents the current element of the stream which is being
1810+-- returned by the stream monad. Thus, we can use the argument to construct
1811+-- itself.
1812+--
1813+-- In the following example, the argument @action@ of the function @f@
1814+-- represents the tuple @(x,y)@ returned by it in a given iteration. We define
1815+-- the first element of the tuple in terms of the second.
1816+--
1817+-- >>> import System.IO.Unsafe (unsafeInterleaveIO)
1818+--
1819+-- >>> :{
1820+-- main = Stream.fold (Fold.drainMapM print) $ StreamK.toStream $ StreamK.mfix f
1821+-- where
1822+-- f action = StreamK.unCross $ do
1823+-- let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act
1824+-- x <- StreamK.mkCross $ StreamK.fromStream $ Stream.sequence $ Stream.fromList [incr 1 action, incr 2 action]
1825+-- y <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [4,5]
1826+-- return (x, y)
1827+-- :}
1828+--
1829+-- Note: you cannot achieve this by just changing the order of the monad
1830+-- statements because that would change the order in which the stream elements
1831+-- are generated.
1832+--
1833+-- Note that the function @f@ must be lazy in its argument, that's why we use
1834+-- 'unsafeInterleaveIO' on @action@ because IO monad is strict.
1835+--
1836+-- /Pre-release/
12771837{-# INLINE mfix #-}
12781838mfix :: Monad m => (m a -> Stream m a) -> Stream m a
12791839mfix f = mkStream $ \st yld sng stp ->
@@ -1329,6 +1889,11 @@ tail =
13291889 yieldk _ r = pure $ Just r
13301890 in foldStream defState yieldk single stop
13311891
1892+-- | Extract all but the last element of the stream, if any.
1893+--
1894+-- Note: This will end up buffering the entire stream.
1895+--
1896+-- /Pre-release/
13321897{-# INLINE init #-}
13331898init :: Applicative m => Stream m a -> m (Maybe (Stream m a))
13341899init = go1
@@ -1386,3 +1951,123 @@ concatMapEffect :: Monad m => (b -> Stream m a) -> m b -> Stream m a
13861951concatMapEffect f action =
13871952 mkStream $ \st yld sng stp ->
13881953 action >>= foldStreamShared st yld sng stp . f
1954+
1955+------------------------------------------------------------------------------
1956+-- Stream with a cross product style monad instance
1957+------------------------------------------------------------------------------
1958+
1959+-- | A newtype wrapper for the 'StreamK' type adding a cross product style
1960+-- monad instance.
1961+--
1962+-- A 'Monad' bind behaves like a @for@ loop:
1963+--
1964+-- >>> :{
1965+-- Stream.fold Fold.toList $ StreamK.toStream $ StreamK.unCross $ do
1966+-- x <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [1,2]
1967+-- -- Perform the following actions for each x in the stream
1968+-- return x
1969+-- :}
1970+-- [1,2]
1971+--
1972+-- Nested monad binds behave like nested @for@ loops:
1973+--
1974+-- >>> :{
1975+-- Stream.fold Fold.toList $ StreamK.toStream $ StreamK.unCross $ do
1976+-- x <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [1,2]
1977+-- y <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [3,4]
1978+-- -- Perform the following actions for each x, for each y
1979+-- return (x, y)
1980+-- :}
1981+-- [(1,3),(1,4),(2,3),(2,4)]
1982+--
1983+newtype CrossStreamK m a = CrossStreamK {unCrossStreamK :: Stream m a}
1984+ deriving (Functor, Semigroup, Monoid, Foldable)
1985+
1986+-- | Wrap the 'StreamK' type in a 'CrossStreamK' newtype to enable cross
1987+-- product style applicative and monad instances.
1988+--
1989+-- This is a type level operation with no runtime overhead.
1990+{-# INLINE mkCross #-}
1991+mkCross :: StreamK m a -> CrossStreamK m a
1992+mkCross = CrossStreamK
1993+
1994+-- | Unwrap the 'StreamK' type from 'CrossStreamK' newtype.
1995+--
1996+-- This is a type level operation with no runtime overhead.
1997+{-# INLINE unCross #-}
1998+unCross :: CrossStreamK m a -> StreamK m a
1999+unCross = unCrossStreamK
2000+
2001+-- Pure (Identity monad) stream instances
2002+deriving instance Traversable (CrossStreamK Identity)
2003+deriving instance IsList (CrossStreamK Identity a)
2004+deriving instance (a ~ Char) => IsString (CrossStreamK Identity a)
2005+-- deriving instance Eq a => Eq (CrossStreamK Identity a)
2006+-- deriving instance Ord a => Ord (CrossStreamK Identity a)
2007+
2008+-- Do not use automatic derivation for this to show as "fromList" rather than
2009+-- "fromList Identity".
2010+instance Show a => Show (CrossStreamK Identity a) where
2011+ {-# INLINE show #-}
2012+ show (CrossStreamK xs) = show xs
2013+
2014+instance Read a => Read (CrossStreamK Identity a) where
2015+ {-# INLINE readPrec #-}
2016+ readPrec = fmap CrossStreamK readPrec
2017+
2018+------------------------------------------------------------------------------
2019+-- Applicative
2020+------------------------------------------------------------------------------
2021+
2022+-- Note: we need to define all the typeclass operations because we want to
2023+-- INLINE them.
2024+instance Monad m => Applicative (CrossStreamK m) where
2025+ {-# INLINE pure #-}
2026+ pure x = CrossStreamK (fromPure x)
2027+
2028+ {-# INLINE (<*>) #-}
2029+ (CrossStreamK s1) <*> (CrossStreamK s2) =
2030+ CrossStreamK (crossApply s1 s2)
2031+
2032+ {-# INLINE liftA2 #-}
2033+ liftA2 f x = (<*>) (fmap f x)
2034+
2035+ {-# INLINE (*>) #-}
2036+ (CrossStreamK s1) *> (CrossStreamK s2) =
2037+ CrossStreamK (crossApplySnd s1 s2)
2038+
2039+ {-# INLINE (<*) #-}
2040+ (CrossStreamK s1) <* (CrossStreamK s2) =
2041+ CrossStreamK (crossApplyFst s1 s2)
2042+
2043+------------------------------------------------------------------------------
2044+-- Monad
2045+------------------------------------------------------------------------------
2046+
2047+instance Monad m => Monad (CrossStreamK m) where
2048+ return = pure
2049+
2050+ -- Benchmarks better with CPS bind and pure:
2051+ -- Prime sieve (25x)
2052+ -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
2053+ --
2054+ {-# INLINE (>>=) #-}
2055+ (>>=) (CrossStreamK m) f =
2056+ CrossStreamK (bindWith append m (unCrossStreamK . f))
2057+
2058+ {-# INLINE (>>) #-}
2059+ (>>) = (*>)
2060+
2061+------------------------------------------------------------------------------
2062+-- Transformers
2063+------------------------------------------------------------------------------
2064+
2065+instance (MonadIO m) => MonadIO (CrossStreamK m) where
2066+ liftIO x = CrossStreamK (fromEffect $ liftIO x)
2067+
2068+instance MonadTrans CrossStreamK where
2069+ {-# INLINE lift #-}
2070+ lift x = CrossStreamK (fromEffect x)
2071+
2072+instance (MonadThrow m) => MonadThrow (CrossStreamK m) where
2073+ throwM = lift . throwM
core/src/Streamly/Internal/Data/Stream/Transform.hs+1−1
@@ -427,7 +427,7 @@ scanlMAfter' step initial done stream =
427427--
428428{-# INLINE scan #-}
429429scan :: Monad m => Fold m a b -> Stream m a -> Stream m b
430−scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
430+scan fld m = fromStreamD $ D.scan fld $ toStreamD m
431431
432432-- | Like 'scan' but restarts scanning afresh when the scanning fold
433433-- terminates.
core/src/Streamly/Internal/Data/Stream/Type.hs+31−18
@@ -11,13 +11,16 @@
1111module Streamly.Internal.Data.Stream.Type
1212 (
1313 -- * Stream Type
14− Stream
14+ Stream -- XXX To be removed
15+ , StreamK
1516
1617 -- * Type Conversion
1718 , fromStreamK
1819 , toStreamK
1920 , fromStreamD
2021 , toStreamD
22+ , fromStream
23+ , toStream
2124 , Streamly.Internal.Data.Stream.Type.fromList
2225
2326 -- * Construction
@@ -76,21 +79,23 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
7679--
7780-- >>> (<>) = Stream.append
7881--
79−newtype Stream m a = Stream (K.Stream m a)
82+newtype StreamK m a = StreamK (K.Stream m a)
8083 -- XXX when deriving do we inherit an INLINE?
8184 deriving (Semigroup, Monoid)
8285
86+type Stream = StreamK
87+
8388------------------------------------------------------------------------------
8489-- Conversions
8590------------------------------------------------------------------------------
8691
8792{-# INLINE_EARLY fromStreamK #-}
8893fromStreamK :: K.Stream m a -> Stream m a
89−fromStreamK = Stream
94+fromStreamK = StreamK
9095
9196{-# INLINE_EARLY toStreamK #-}
9297toStreamK :: Stream m a -> K.Stream m a
93−toStreamK (Stream k) = k
98+toStreamK (StreamK k) = k
9499
95100{-# INLINE_EARLY fromStreamD #-}
96101fromStreamD :: Monad m => D.Stream m a -> Stream m a
@@ -100,6 +105,14 @@ fromStreamD = fromStreamK . D.toStreamK
100105toStreamD :: Applicative m => Stream m a -> D.Stream m a
101106toStreamD = D.fromStreamK . toStreamK
102107
108+{-# INLINE fromStream #-}
109+fromStream :: Monad m => D.Stream m a -> Stream m a
110+fromStream = fromStreamD
111+
112+{-# INLINE toStream #-}
113+toStream :: Applicative m => Stream m a -> D.Stream m a
114+toStream = toStreamD
115+
103116------------------------------------------------------------------------------
104117-- Generation
105118------------------------------------------------------------------------------
@@ -162,18 +175,18 @@ instance IsList (Stream Identity a) where
162175 type (Item (Stream Identity a)) = a
163176
164177 {-# INLINE fromList #-}
165− fromList xs = Stream $ P.fromList xs
178+ fromList xs = StreamK $ P.fromList xs
166179
167180 {-# INLINE toList #-}
168− toList (Stream xs) = runIdentity $ P.toList xs
181+ toList (StreamK xs) = runIdentity $ P.toList xs
169182
170183instance Eq a => Eq (Stream Identity a) where
171184 {-# INLINE (==) #-}
172− (==) (Stream xs) (Stream ys) = runIdentity $ P.eqBy (==) xs ys
185+ (==) (StreamK xs) (StreamK ys) = runIdentity $ P.eqBy (==) xs ys
173186
174187instance Ord a => Ord (Stream Identity a) where
175188 {-# INLINE compare #-}
176− compare (Stream xs) (Stream ys) = runIdentity $ P.cmpBy compare xs ys
189+ compare (StreamK xs) (StreamK ys) = runIdentity $ P.cmpBy compare xs ys
177190
178191 {-# INLINE (<) #-}
179192 x < y =
@@ -218,7 +231,7 @@ instance Read a => Read (Stream Identity a) where
218231
219232instance (a ~ Char) => IsString (Stream Identity a) where
220233 {-# INLINE fromString #-}
221− fromString xs = Stream $ P.fromList xs
234+ fromString xs = StreamK $ P.fromList xs
222235
223236-------------------------------------------------------------------------------
224237-- Foldable
@@ -234,7 +247,7 @@ instance (a ~ Char) => IsString (Stream Identity a) where
234247instance (Foldable m, Monad m) => Foldable (Stream m) where
235248
236249 {-# INLINE foldMap #-}
237− foldMap f (Stream xs) = fold $ P.foldr (mappend . f) mempty xs
250+ foldMap f (StreamK xs) = fold $ P.foldr (mappend . f) mempty xs
238251
239252 {-# INLINE foldr #-}
240253 foldr f z t = appEndo (foldMap (Endo #. f) t) z
@@ -283,8 +296,8 @@ instance (Foldable m, Monad m) => Foldable (Stream m) where
283296
284297instance Traversable (Stream Identity) where
285298 {-# INLINE traverse #-}
286− traverse f (Stream xs) =
287− fmap Stream $ runIdentity $ P.foldr consA (pure mempty) xs
299+ traverse f (StreamK xs) =
300+ fmap StreamK $ runIdentity $ P.foldr consA (pure mempty) xs
288301
289302 where
290303
@@ -396,19 +409,19 @@ fromEffect = fromStreamK . K.fromEffect
396409-- >>> crossApply = Stream.crossWith id
397410--
398411{-# INLINE crossApply #-}
399−crossApply :: Monad m => Stream m (a -> b) -> Stream m a -> Stream m b
412+crossApply :: Stream m (a -> b) -> Stream m a -> Stream m b
400413crossApply m1 m2 =
401− fromStreamD $ D.crossApply (toStreamD m1) (toStreamD m2)
414+ fromStreamK $ K.crossApply (toStreamK m1) (toStreamK m2)
402415
403416{-# INLINE crossApplySnd #-}
404−crossApplySnd :: Monad m => Stream m a -> Stream m b -> Stream m b
417+crossApplySnd :: Stream m a -> Stream m b -> Stream m b
405418crossApplySnd m1 m2 =
406− fromStreamD $ D.crossApplySnd (toStreamD m1) (toStreamD m2)
419+ fromStreamK $ K.crossApplySnd (toStreamK m1) (toStreamK m2)
407420
408421{-# INLINE crossApplyFst #-}
409−crossApplyFst :: Monad m => Stream m a -> Stream m b -> Stream m a
422+crossApplyFst :: Stream m a -> Stream m b -> Stream m a
410423crossApplyFst m1 m2 =
411− fromStreamD $ D.crossApplyFst (toStreamD m1) (toStreamD m2)
424+ fromStreamK $ K.crossApplyFst (toStreamK m1) (toStreamK m2)
412425
413426-- |
414427-- Definition:
core/src/Streamly/Internal/Data/Unfold.hs+2−3
@@ -292,7 +292,6 @@ import qualified Data.Tuple as Tuple
292292import qualified Streamly.Internal.Data.Fold.Type as FL
293293import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
294294import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
295−import qualified Streamly.Internal.Data.Stream.Type as Stream
296295import qualified Prelude
297296
298297import Streamly.Internal.Data.Unfold.Enumeration
@@ -604,8 +603,8 @@ fromStreamK = Unfold step pure
604603 Nothing -> Stop) <$> K.uncons stream
605604
606605{-# INLINE fromStream #-}
607−fromStream :: Applicative m => Unfold m (Stream.Stream m a) a
608−fromStream = lmap Stream.toStreamK fromStreamK
606+fromStream :: Applicative m => Unfold m (Stream m a) a
607+fromStream = fromStreamD
609608
610609-------------------------------------------------------------------------------
611610-- Unfolds
core/src/Streamly/Internal/FileSystem/File.hs+9−5
@@ -107,18 +107,18 @@ import Streamly.Data.Fold (chunksOf, drain)
107107import Streamly.Internal.Data.Array.Type (Array(..), writeNUnsafe)
108108import Streamly.Internal.Data.Fold.Type (Fold(..))
109109import Streamly.Data.Stream (Stream)
110+import Streamly.Internal.Data.Unboxed (Unbox)
110111import Streamly.Internal.Data.Unfold.Type (Unfold(..))
111112-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
112113import Streamly.Internal.System.IO (defaultChunkSize)
113114
114115import qualified Streamly.Data.Array as A
116+import qualified Streamly.Data.Stream as S
115117import qualified Streamly.Data.Unfold as UF
116118import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
117119import qualified Streamly.Internal.Data.Fold.Type as FL
118120 (Step(..), snoc, reduce)
119121import qualified Streamly.Internal.FileSystem.Handle as FH
120−import qualified Streamly.Internal.Data.Stream.Chunked as AS
121−import qualified Streamly.Data.Stream as S (fold, bracketIO, mapM)
122122
123123-------------------------------------------------------------------------------
124124-- References
@@ -339,6 +339,10 @@ readWithBufferOf = readerWith
339339reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
340340reader = UF.many A.reader (usingFile FH.chunkReader)
341341
342+{-# INLINE concatChunks #-}
343+concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
344+concatChunks = S.unfoldMany A.reader
345+
342346-- | Generate a stream of bytes from a file specified by path. The stream ends
343347-- when EOF is encountered. File is locked using multiple reader and single
344348-- writer locking mode.
@@ -347,7 +351,7 @@ reader = UF.many A.reader (usingFile FH.chunkReader)
347351--
348352{-# INLINE read #-}
349353read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
350−read file = AS.concat $ withFile file ReadMode FH.readChunks
354+read file = concatChunks $ withFile file ReadMode FH.readChunks
351355
352356{-# DEPRECATED toBytes "Please use 'read' instead" #-}
353357{-# INLINE toBytes #-}
@@ -401,7 +405,7 @@ fromChunks = fromChunksMode WriteMode
401405{-# INLINE fromBytesWith #-}
402406fromBytesWith :: (MonadIO m, MonadCatch m)
403407 => Int -> FilePath -> Stream m Word8 -> m ()
404−fromBytesWith n file xs = fromChunks file $ AS.arraysOf n xs
408+fromBytesWith n file xs = fromChunks file $ S.arraysOf n xs
405409
406410{-# DEPRECATED fromBytesWithBufferOf "Please use 'fromBytesWith' instead" #-}
407411{-# INLINE fromBytesWithBufferOf #-}
@@ -498,7 +502,7 @@ appendChunks = fromChunksMode AppendMode
498502{-# INLINE appendWith #-}
499503appendWith :: (MonadIO m, MonadCatch m)
500504 => Int -> FilePath -> Stream m Word8 -> m ()
501−appendWith n file xs = appendChunks file $ AS.arraysOf n xs
505+appendWith n file xs = appendChunks file $ S.arraysOf n xs
502506
503507-- | Append a byte stream to a file. Combines the bytes in chunks of size up to
504508-- 'A.defaultChunkSize' before writing. If the file exists then the new data
core/src/Streamly/Internal/FileSystem/Handle.hs+10−6
@@ -124,7 +124,7 @@ import Streamly.Internal.Data.Refold.Type (Refold(..))
124124import Streamly.Internal.Data.Unfold.Type (Unfold(..))
125125import Streamly.Internal.Data.Array.Type
126126 (Array(..), writeNUnsafe, unsafeFreezeWithShrink, byteLength)
127−import Streamly.Internal.Data.Stream.Type (Stream)
127+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
128128import Streamly.Internal.Data.Stream.Chunked (lpackArraysChunksOf)
129129-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
130130import Streamly.Internal.System.IO (defaultChunkSize)
@@ -136,7 +136,7 @@ import qualified Streamly.Internal.Data.Stream.Chunked as AS
136136import qualified Streamly.Internal.Data.Array.Mut.Type as MArray
137137import qualified Streamly.Internal.Data.Refold.Type as Refold
138138import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
139−import qualified Streamly.Internal.Data.Stream as S
139+import qualified Streamly.Internal.Data.Stream.StreamD as S
140140import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
141141 (Stream(..), Step(..))
142142import qualified Streamly.Internal.Data.Unfold as UF
@@ -228,7 +228,7 @@ _getChunksWith size h = S.fromStreamK go
228228--
229229{-# INLINE_NORMAL readChunksWith #-}
230230readChunksWith :: MonadIO m => Int -> Handle -> Stream m (Array Word8)
231−readChunksWith size h = S.fromStreamD (D.Stream step ())
231+readChunksWith size h = D.Stream step ()
232232 where
233233 {-# INLINE_LATE step #-}
234234 step _ _ = do
@@ -340,6 +340,10 @@ readerWith = UF.many A.reader chunkReaderWith
340340readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
341341readWithBufferOf = readerWith
342342
343+{-# INLINE concatChunks #-}
344+concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
345+concatChunks = S.unfoldMany A.reader
346+
343347-- | @readWith bufsize handle@ reads a byte stream from a file
344348-- handle, reads are performed in chunks of up to @bufsize@.
345349--
@@ -348,7 +352,7 @@ readWithBufferOf = readerWith
348352-- /Pre-release/
349353{-# INLINE readWith #-}
350354readWith :: MonadIO m => Int -> Handle -> Stream m Word8
351−readWith size h = AS.concat $ readChunksWith size h
355+readWith size h = concatChunks $ readChunksWith size h
352356
353357-- | Unfolds a file handle into a byte stream. IO requests to the device are
354358-- performed in sizes of
@@ -367,7 +371,7 @@ reader = UF.many A.reader chunkReader
367371-- /Pre-release/
368372{-# INLINE read #-}
369373read :: MonadIO m => Handle -> Stream m Word8
370−read = AS.concat . readChunks
374+read = concatChunks . readChunks
371375
372376-------------------------------------------------------------------------------
373377-- Writing
@@ -429,7 +433,7 @@ putChunksWith n h xs = putChunks h $ AS.compact n xs
429433--
430434{-# INLINE putBytesWith #-}
431435putBytesWith :: MonadIO m => Int -> Handle -> Stream m Word8 -> m ()
432−putBytesWith n h m = putChunks h $ S.arraysOf n m
436+putBytesWith n h m = putChunks h $ A.arraysOf n m
433437
434438-- putBytesWith n h m = putChunks h $ AS.arraysOf n m
435439
core/src/Streamly/Internal/Serialize/ToBytes.hs+17−17
@@ -44,10 +44,10 @@ where
4444import Data.Bits (shiftR)
4545import Data.Word (Word8, Word16, Word32, Word64)
4646import GHC.Float (castDoubleToWord64, castFloatToWord32)
47−import Streamly.Internal.Data.Stream (Stream, fromStreamD)
47+import Streamly.Internal.Data.Stream.StreamD (Stream)
4848import Streamly.Internal.Data.Stream.StreamD (Step(..))
4949
50−import qualified Streamly.Data.Stream as Stream
50+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
5151import qualified Streamly.Internal.Data.Stream.StreamD as D
5252
5353import Data.Int (Int8, Int16, Int32, Int64)
@@ -63,7 +63,7 @@ import Data.Int (Int8, Int16, Int32, Int64)
6363-- /Pre-release/
6464--
6565{-# INLINE unit #-}
66−unit :: Stream m Word8
66+unit :: Applicative m => Stream m Word8
6767unit = Stream.fromPure 0
6868
6969{-# INLINE boolToWord8 #-}
@@ -81,7 +81,7 @@ boolToWord8 True = 1
8181-- /Pre-release/
8282--
8383{-# INLINE bool #-}
84−bool :: Bool -> Stream m Word8
84+bool :: Applicative m => Bool -> Stream m Word8
8585bool = Stream.fromPure . boolToWord8
8686
8787{-# INLINE orderingToWord8 #-}
@@ -101,7 +101,7 @@ orderingToWord8 GT = 2
101101-- /Pre-release/
102102--
103103{-# INLINE ordering #-}
104−ordering :: Ordering -> Stream m Word8
104+ordering :: Applicative m => Ordering -> Stream m Word8
105105ordering = Stream.fromPure . orderingToWord8
106106
107107-- | Stream a 'Word8'.
@@ -109,7 +109,7 @@ ordering = Stream.fromPure . orderingToWord8
109109-- /Pre-release/
110110--
111111{-# INLINE word8 #-}
112−word8 :: Word8 -> Stream m Word8
112+word8 :: Applicative m => Word8 -> Stream m Word8
113113word8 = Stream.fromPure
114114
115115data W16State = W16B1 | W16B2 | W16Done
@@ -131,7 +131,7 @@ word16beD w = D.Stream step W16B1
131131--
132132{-# INLINE word16be #-}
133133word16be :: Monad m => Word16 -> Stream m Word8
134−word16be = fromStreamD . word16beD
134+word16be = word16beD
135135
136136-- | Little endian (LSB first) Word16
137137{-# INLINE word16leD #-}
@@ -151,7 +151,7 @@ word16leD w = D.Stream step W16B1
151151--
152152{-# INLINE word16le #-}
153153word16le :: Monad m => Word16 -> Stream m Word8
154−word16le = fromStreamD . word16leD
154+word16le = word16leD
155155
156156data W32State = W32B1 | W32B2 | W32B3 | W32B4 | W32Done
157157
@@ -177,7 +177,7 @@ word32beD w = D.Stream step W32B1
177177--
178178{-# INLINE word32be #-}
179179word32be :: Monad m => Word32 -> Stream m Word8
180−word32be = fromStreamD . word32beD
180+word32be = word32beD
181181
182182-- | Little endian (LSB first) Word32
183183{-# INLINE word32leD #-}
@@ -201,7 +201,7 @@ word32leD w = D.Stream step W32B1
201201--
202202{-# INLINE word32le #-}
203203word32le :: Monad m => Word32 -> Stream m Word8
204−word32le = fromStreamD . word32leD
204+word32le = word32leD
205205
206206data W64State =
207207 W64B1 | W64B2 | W64B3 | W64B4 | W64B5 | W64B6 | W64B7 | W64B8 | W64Done
@@ -232,7 +232,7 @@ word64beD w = D.Stream step W64B1
232232--
233233{-# INLINE word64be #-}
234234word64be :: Monad m => Word64 -> Stream m Word8
235−word64be = fromStreamD . word64beD
235+word64be = word64beD
236236
237237-- | Little endian (LSB first) Word64
238238{-# INLINE word64leD #-}
@@ -260,10 +260,10 @@ word64leD w = D.Stream step W64B1
260260--
261261{-# INLINE word64le #-}
262262word64le :: Monad m => Word64 -> Stream m Word8
263−word64le = fromStreamD . word64leD
263+word64le = word64leD
264264
265265{-# INLINE int8 #-}
266−int8 :: Int8 -> Stream m Word8
266+int8 :: Applicative m => Int8 -> Stream m Word8
267267int8 i = word8 (fromIntegral i :: Word8)
268268
269269-- | Stream a 'Int16' as two bytes, the first byte is the MSB of the Int16
@@ -318,22 +318,22 @@ int64le i = word64le (fromIntegral i :: Word64)
318318-- | Big endian (MSB first) Float
319319{-# INLINE float32be #-}
320320float32be :: Monad m => Float -> Stream m Word8
321−float32be = fromStreamD . word32beD . castFloatToWord32
321+float32be = word32beD . castFloatToWord32
322322
323323-- | Little endian (LSB first) Float
324324{-# INLINE float32le #-}
325325float32le :: Monad m => Float -> Stream m Word8
326−float32le = fromStreamD . word32leD . castFloatToWord32
326+float32le = word32leD . castFloatToWord32
327327
328328-- | Big endian (MSB first) Double
329329{-# INLINE double64be #-}
330330double64be :: Monad m => Double -> Stream m Word8
331−double64be = fromStreamD . word64beD . castDoubleToWord64
331+double64be = word64beD . castDoubleToWord64
332332
333333-- | Little endian (LSB first) Double
334334{-# INLINE double64le #-}
335335double64le :: Monad m => Double -> Stream m Word8
336−double64le = fromStreamD . word64leD . castDoubleToWord64
336+double64le = word64leD . castDoubleToWord64
337337
338338-------------------------------------------------------------------------------
339339-- Host byte order
core/src/Streamly/Internal/Unicode/Stream.hs+14−17
@@ -105,7 +105,7 @@ import System.IO.Unsafe (unsafePerformIO)
105105import Streamly.Internal.Data.Array.Type (Array(..))
106106import Streamly.Internal.Data.Array.Mut.Type (MutableByteArray)
107107import Streamly.Internal.Data.Fold (Fold)
108−import Streamly.Internal.Data.Stream (Stream, fromStreamD, toStreamD)
108+import Streamly.Internal.Data.Stream.StreamD (Stream)
109109import Streamly.Internal.Data.Stream.StreamD (Step (..))
110110import Streamly.Internal.Data.SVar.Type (adaptState)
111111import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
@@ -118,7 +118,7 @@ import qualified Streamly.Data.Unfold as Unfold
118118import qualified Streamly.Internal.Data.Array.Type as Array
119119import qualified Streamly.Internal.Data.Parser as Parser (Parser)
120120import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
121−import qualified Streamly.Internal.Data.Stream as Stream
121+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
122122import qualified Streamly.Internal.Data.Stream.StreamD as D
123123
124124import Prelude hiding (lines, words, unlines, unwords)
@@ -417,7 +417,7 @@ decodeUtf8EitherD = resumeDecodeUtf8EitherD 0 0
417417{-# INLINE decodeUtf8Either #-}
418418decodeUtf8Either :: Monad m
419419 => Stream m Word8 -> Stream m (Either DecodeError Char)
420−decodeUtf8Either = fromStreamD . decodeUtf8EitherD . toStreamD
420+decodeUtf8Either = decodeUtf8EitherD
421421
422422-- |
423423--
@@ -429,8 +429,7 @@ resumeDecodeUtf8Either
429429 -> CodePoint
430430 -> Stream m Word8
431431 -> Stream m (Either DecodeError Char)
432−resumeDecodeUtf8Either st cp =
433− fromStreamD . resumeDecodeUtf8EitherD st cp . toStreamD
432+resumeDecodeUtf8Either = resumeDecodeUtf8EitherD
434433
435434-------------------------------------------------------------------------------
436435-- One shot decoding
@@ -646,7 +645,7 @@ decodeUtf8D = decodeUtf8WithD TransliterateCodingFailure
646645--
647646{-# INLINE decodeUtf8 #-}
648647decodeUtf8 :: Monad m => Stream m Word8 -> Stream m Char
649−decodeUtf8 = fromStreamD . decodeUtf8D . toStreamD
648+decodeUtf8 = decodeUtf8D
650649
651650{-# INLINE decodeUtf8D' #-}
652651decodeUtf8D' :: Monad m => D.Stream m Word8 -> D.Stream m Char
@@ -657,7 +656,7 @@ decodeUtf8D' = decodeUtf8WithD ErrorOnCodingFailure
657656--
658657{-# INLINE decodeUtf8' #-}
659658decodeUtf8' :: Monad m => Stream m Word8 -> Stream m Char
660−decodeUtf8' = fromStreamD . decodeUtf8D' . toStreamD
659+decodeUtf8' = decodeUtf8D'
661660
662661{-# INLINE decodeUtf8D_ #-}
663662decodeUtf8D_ :: Monad m => D.Stream m Word8 -> D.Stream m Char
@@ -668,7 +667,7 @@ decodeUtf8D_ = decodeUtf8WithD DropOnCodingFailure
668667--
669668{-# INLINE decodeUtf8_ #-}
670669decodeUtf8_ :: Monad m => Stream m Word8 -> Stream m Char
671−decodeUtf8_ = fromStreamD . decodeUtf8D_ . toStreamD
670+decodeUtf8_ = decodeUtf8D_
672671
673672-- | Same as 'decodeUtf8'
674673--
@@ -817,8 +816,7 @@ decodeUtf8ArraysD = decodeUtf8ArraysWithD TransliterateCodingFailure
817816-- /Pre-release/
818817{-# INLINE decodeUtf8Arrays #-}
819818decodeUtf8Arrays :: MonadIO m => Stream m (Array Word8) -> Stream m Char
820−decodeUtf8Arrays =
821− fromStreamD . decodeUtf8ArraysD . toStreamD
819+decodeUtf8Arrays = decodeUtf8ArraysD
822820
823821{-# INLINE decodeUtf8ArraysD' #-}
824822decodeUtf8ArraysD' ::
@@ -832,7 +830,7 @@ decodeUtf8ArraysD' = decodeUtf8ArraysWithD ErrorOnCodingFailure
832830-- /Pre-release/
833831{-# INLINE decodeUtf8Arrays' #-}
834832decodeUtf8Arrays' :: MonadIO m => Stream m (Array Word8) -> Stream m Char
835−decodeUtf8Arrays' = fromStreamD . decodeUtf8ArraysD' . toStreamD
833+decodeUtf8Arrays' = decodeUtf8ArraysD'
836834
837835{-# INLINE decodeUtf8ArraysD_ #-}
838836decodeUtf8ArraysD_ ::
@@ -847,8 +845,7 @@ decodeUtf8ArraysD_ = decodeUtf8ArraysWithD DropOnCodingFailure
847845{-# INLINE decodeUtf8Arrays_ #-}
848846decodeUtf8Arrays_ ::
849847 MonadIO m => Stream m (Array Word8) -> Stream m Char
850−decodeUtf8Arrays_ =
851− fromStreamD . decodeUtf8ArraysD_ . toStreamD
848+decodeUtf8Arrays_ = decodeUtf8ArraysD_
852849
853850-------------------------------------------------------------------------------
854851-- Encoding Unicode (UTF-8) Characters
@@ -922,7 +919,7 @@ encodeUtf8D' = D.unfoldMany readCharUtf8'
922919--
923920{-# INLINE encodeUtf8' #-}
924921encodeUtf8' :: Monad m => Stream m Char -> Stream m Word8
925−encodeUtf8' = fromStreamD . encodeUtf8D' . toStreamD
922+encodeUtf8' = encodeUtf8D'
926923
927924{-# INLINE_NORMAL readCharUtf8 #-}
928925readCharUtf8 :: Monad m => Unfold m Char Word8
@@ -941,7 +938,7 @@ encodeUtf8D = D.unfoldMany readCharUtf8
941938--
942939{-# INLINE encodeUtf8 #-}
943940encodeUtf8 :: Monad m => Stream m Char -> Stream m Word8
944−encodeUtf8 = fromStreamD . encodeUtf8D . toStreamD
941+encodeUtf8 = encodeUtf8D
945942
946943{-# INLINE_NORMAL readCharUtf8_ #-}
947944readCharUtf8_ :: Monad m => Unfold m Char Word8
@@ -956,7 +953,7 @@ encodeUtf8D_ = D.unfoldMany readCharUtf8_
956953--
957954{-# INLINE encodeUtf8_ #-}
958955encodeUtf8_ :: Monad m => Stream m Char -> Stream m Word8
959−encodeUtf8_ = fromStreamD . encodeUtf8D_ . toStreamD
956+encodeUtf8_ = encodeUtf8D_
960957
961958-- | Same as 'encodeUtf8'
962959--
@@ -1085,7 +1082,7 @@ isSpace c
10851082-- /Pre-release/
10861083{-# INLINE words #-}
10871084words :: Monad m => Fold m Char b -> Stream m Char -> Stream m b
1088−words f m = Stream.fromStreamD $ D.wordsBy isSpace f (Stream.toStreamD m)
1085+words f = D.wordsBy isSpace f
10891086
10901087-- | Unfold a stream to character streams using the supplied 'Unfold'
10911088-- and concat the results suffixing a newline character @\\n@ to each stream.
core/streamly-core.cabal+21−21
@@ -299,8 +299,10 @@ library
299299 , Streamly.Internal.Data.Stream.StreamD.Transform
300300 , Streamly.Internal.Data.Stream.StreamD.Exception
301301 , Streamly.Internal.Data.Stream.StreamD.Lift
302+ , Streamly.Internal.Data.Stream.StreamD.Top
302303 , Streamly.Internal.Data.Stream.StreamD
303304 , Streamly.Internal.Data.Stream.Common
305+ , Streamly.Internal.Data.Stream
304306
305307 , Streamly.Internal.Data.Parser.ParserD.Tee
306308 , Streamly.Internal.Data.Parser.ParserD
@@ -317,32 +319,16 @@ library
317319 , Streamly.Internal.Data.Parser.Chunked.Type
318320 , Streamly.Internal.Data.Parser.Chunked
319321 , Streamly.Internal.Data.Pipe
320− , Streamly.Internal.Data.Stream.Type
321− , Streamly.Internal.Data.Stream.Eliminate
322− , Streamly.Internal.Data.Stream.Enumerate
323− , Streamly.Internal.Data.Stream.Generate
324− , Streamly.Internal.Data.Stream.Transform
325− , Streamly.Internal.Data.Stream.Bottom
326− , Streamly.Internal.Data.Stream.Exception
327− , Streamly.Internal.Data.Stream.Expand
328− , Streamly.Internal.Data.Stream.Lift
329− , Streamly.Internal.Data.Stream.Reduce
330− , Streamly.Internal.Data.Stream.Top
331322
332323 -- streamly-transformers (non-base)
333324 , Streamly.Internal.Data.Stream.StreamD.Transformer
334325 , Streamly.Internal.Data.Stream.StreamK.Transformer
335− , Streamly.Internal.Data.Stream.Transformer
336326
337327 -- streamly-containers (non-base)
338− , Streamly.Internal.Data.Stream.Container
328+ , Streamly.Internal.Data.Stream.StreamD.Container
339329 , Streamly.Internal.Data.Fold.Container
340330
341− , Streamly.Internal.Data.Stream
342331 , Streamly.Internal.Data.Stream.Chunked
343− , Streamly.Internal.Data.Stream.Zip
344− , Streamly.Internal.Data.Stream.Cross
345− , Streamly.Internal.Data.List
346332
347333 -- streamly-core-data-arrays
348334 , Streamly.Internal.Data.Array.Generic
@@ -405,6 +391,7 @@ library
405391 , Streamly.Data.Fold
406392 , Streamly.Data.Parser
407393 , Streamly.Data.Stream
394+ , Streamly.Data.Stream.StreamK
408395 , Streamly.Data.Unfold
409396 , Streamly.FileSystem.Dir
410397 , Streamly.FileSystem.File
@@ -413,12 +400,25 @@ library
413400 , Streamly.Unicode.Stream
414401 , Streamly.Unicode.String
415402
416− other-modules: Streamly.Data.Stream.Zip
417−
418403 if flag(dev)
419404 exposed-modules:
420− Streamly.Internal.Data.Stream.StreamDK
421− , Streamly.Internal.Data.Stream.StreamDK.Type
405+ Streamly.Internal.Data.Stream.StreamK.Alt
406+ , Streamly.Internal.Data.Stream.Type
407+ , Streamly.Internal.Data.Stream.Eliminate
408+ , Streamly.Internal.Data.Stream.Enumerate
409+ , Streamly.Internal.Data.Stream.Generate
410+ , Streamly.Internal.Data.Stream.Transform
411+ , Streamly.Internal.Data.Stream.Bottom
412+ , Streamly.Internal.Data.Stream.Exception
413+ , Streamly.Internal.Data.Stream.Expand
414+ , Streamly.Internal.Data.Stream.Lift
415+ , Streamly.Internal.Data.Stream.Reduce
416+ , Streamly.Internal.Data.Stream.Transformer
417+ , Streamly.Internal.Data.Stream.StreamDK
418+ , Streamly.Internal.Data.Stream.Zip
419+ , Streamly.Internal.Data.Stream.Cross
420+ , Streamly.Internal.Data.List
421+ , Streamly.Data.Stream.Zip
422422
423423 build-depends:
424424 -- streamly-base
hie.yaml+10
@@ -16,6 +16,12 @@ cradle:
1616 config:
1717 cradle:
1818 cabal:
19+ - path: "./benchmark/Streamly/Benchmark/Data/Array.hs"
20+ component: "bench:Data.Array"
21+ - path: "./benchmark/Streamly/Benchmark/Data/Array/Generic.hs"
22+ component: "bench:Data.Array.Generic"
23+ - path: "./benchmark/Streamly/Benchmark/Data/Array/Mut.hs"
24+ component: "bench:Data.Array.Mut"
1925 - path: "./benchmark/Streamly/Benchmark/Data/Array/Stream.hs"
2026 component: "bench:Data.Array.Stream"
2127 - path: "./benchmark/Streamly/Benchmark/Data/Fold.hs"
@@ -34,12 +40,16 @@ cradle:
3440 component: "bench:Data.Stream.ToStreamK"
3541 - path: "./benchmark/Streamly/Benchmark/Data/Stream/Common.hs"
3642 component: "bench:Data.Stream"
43+ - path: "./benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs"
44+ component: "bench:Data.Stream"
3745 - path: "./benchmark/Streamly/Benchmark/Data/Stream/Expand.hs"
3846 component: "bench:Data.Stream"
3947 - path: "./benchmark/Streamly/Benchmark/Data/Stream/Exceptions.hs"
4048 component: "bench:Data.Stream"
4149 - path: "./benchmark/Streamly/Benchmark/Data/Stream/Generate.hs"
4250 component: "bench:Data.Stream"
51+ - path: "./benchmark/Streamly/Benchmark/Data/Stream/Lift.hs"
52+ component: "bench:Data.Stream"
4353 - path: "./benchmark/Streamly/Benchmark/Data/Stream/Reduce.hs"
4454 component: "bench:Data.Stream"
4555 - path: "./benchmark/Streamly/Benchmark/Data/Stream/Transform.hs"
src/Streamly/Data/Array/Foreign.hs+3−3
@@ -30,8 +30,8 @@
3030-- it to IO monad as follows:
3131--
3232-- >>> import Data.Functor.Identity (Identity, runIdentity)
33−-- >>> s = Stream.fromList [1..10] :: Stream Identity Int
34−-- >>> s1 = Stream.hoist (return . runIdentity) s :: Stream IO Int
33+-- >>> s = Stream.fromList [1..10] :: SerialT Identity Int
34+-- >>> s1 = Stream.hoist (return . runIdentity) s :: SerialT IO Int
3535-- >>> Stream.fold Array.write s1 :: IO (Array Int)
3636-- fromList [1,2,3,4,5,6,7,8,9,10]
3737--
@@ -102,7 +102,7 @@ import Streamly.Internal.Data.Array as A
102102-- >>> :set -fno-warn-deprecations
103103-- >>> :set -XFlexibleContexts
104104-- >>> :set -package streamly
105−-- >>> import Streamly.Internal.Data.Stream (Stream)
105+-- >>> import Streamly.Prelude (SerialT)
106106-- >>> import Streamly.Data.Array (Array)
107107-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
108108-- >>> import qualified Streamly.Data.Array as Array
src/Streamly/Data/Stream/MkType.hs−7
@@ -37,13 +37,6 @@
3737-- $(mkZipType "ParZipStream" "parApply" True)
3838-- :}
3939--
40−-- Example, create a monad type with an interleaving cross product bind:
41−--
42−-- >>> :{
43−-- interleaveBind = flip (Stream.concatMapWith Stream.interleave)
44−-- $(mkCrossType "InterleaveStream" "interleaveBind" False)
45−-- :}
46−--
4740-- Example, create a monad type with an eager concurrent cross product bind:
4841--
4942-- >>> :{
src/Streamly/Internal/Data/SmallArray.hs+3−4
@@ -56,7 +56,6 @@ import Streamly.Internal.Data.Unfold.Type (Unfold(..))
5656import qualified Streamly.Internal.Data.Stream.StreamD as D
5757import qualified Streamly.Internal.Data.Fold.Type as FL
5858 (Fold (..), Step (Done, Partial))
59−import qualified Streamly.Internal.Data.Stream.Type as Stream
6059
6160import Streamly.Internal.Data.SmallArray.Type
6261
@@ -165,15 +164,15 @@ instance NFData a => NFData (SmallArray a) where
165164fromStreamN :: MonadIO m => Int -> Stream m a -> m (SmallArray a)
166165fromStreamN n m = do
167166 when (n < 0) $ error "fromStreamN: negative write count specified"
168− fromStreamDN n $ D.fromStreamK $ Stream.toStreamK m
167+ fromStreamDN n m
169168
170169{-# INLINE_EARLY read #-}
171170read :: Monad m => SmallArray a -> Stream m a
172−read = Stream.fromStreamK . D.toStreamK . toStreamD
171+read = toStreamD
173172
174173{-# INLINE_EARLY readRev #-}
175174readRev :: Monad m => SmallArray a -> Stream m a
176−readRev = Stream.fromStreamK . D.toStreamK . toStreamDRev
175+readRev = toStreamDRev
177176
178177{-# INLINE fold #-}
179178fold :: Monad m => Fold m a b -> SmallArray a -> m b
src/Streamly/Internal/Data/Stream/Ahead.hs+2−1
@@ -1,4 +1,5 @@
11{-# LANGUAGE UndecidableInstances #-}
2+{-# OPTIONS_GHC -fno-warn-deprecations #-}
23
34-- |
45-- Module : Streamly.Internal.Data.Stream.Ahead
@@ -55,7 +56,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
5556 , nil, concatMapWith, fromPure, bindWith)
5657import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
5758 (mapM, fromStreamK, toStreamK)
58−import qualified Streamly.Internal.Data.Stream as Stream (toStreamK)
59+import qualified Streamly.Internal.Data.Stream.Serial as Stream (toStreamK)
5960
6061import Streamly.Internal.Data.Stream.SVar.Generate
6162import Streamly.Internal.Data.SVar
src/Streamly/Internal/Data/Stream/Async.hs+2−1
@@ -1,4 +1,5 @@
11{-# LANGUAGE UndecidableInstances #-}
2+{-# OPTIONS_GHC -fno-warn-deprecations #-}
23
34#include "inline.hs"
45
@@ -66,7 +67,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
6667 , nil, concatMapWith, fromPure, bindWith)
6768import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
6869 (Stream(..), Step(..), mapM, toStreamK, fromStreamK)
69−import qualified Streamly.Internal.Data.Stream as Stream (toStreamK)
70+import qualified Streamly.Internal.Data.Stream.Serial as Stream (toStreamK)
7071
7172import Streamly.Internal.Data.SVar
7273
src/Streamly/Internal/Data/Stream/Concurrent.hs+8−11
@@ -110,11 +110,11 @@ import Streamly.Internal.Data.Stream.Channel.Types
110110 , concatMapDivK
111111 )
112112import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
113−import Streamly.Internal.Data.Stream.Type (Stream)
113+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
114114import Streamly.Internal.Data.Stream.StreamD (Step(..))
115115
116116import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed
117−import qualified Streamly.Internal.Data.Stream as Stream
117+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
118118import qualified Streamly.Internal.Data.Stream.StreamD as D
119119import qualified Streamly.Internal.Data.Stream.StreamK as K
120120import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
@@ -206,10 +206,10 @@ _appendGeneric newChan modifier stream1 stream2 = K.concatEffect action
206206 toChannelK chan stream1
207207 FirstStops -> do
208208 toChannelK chan stream2
209− toChannelK chan (K.serial stream1 done)
209+ toChannelK chan (K.append stream1 done)
210210 AnyStops -> do
211− toChannelK chan (K.serial stream2 done)
212− toChannelK chan (K.serial stream1 done)
211+ toChannelK chan (K.append stream2 done)
212+ toChannelK chan (K.append stream1 done)
213213 return $ Stream.toStreamK $ fromChannel chan
214214
215215-- | Create a new channel and add both the streams to it for async evaluation.
@@ -319,7 +319,7 @@ parConcatMapChanKAny :: MonadAsync m =>
319319 Channel m b -> (a -> K.Stream m b) -> K.Stream m a -> K.Stream m b
320320parConcatMapChanKAny chan f stream =
321321 let done = K.nilM (stopChannel chan)
322− run q = concatMapDivK q (\x -> K.serial (f x) done)
322+ run q = concatMapDivK q (\x -> K.append (f x) done)
323323 in K.concatMapEffect (`run` stream) (mkEnqueue chan run)
324324
325325{-# INLINE parConcatMapChanKFirst #-}
@@ -335,7 +335,7 @@ parConcatMapChanKFirst chan f stream =
335335 Just (h, t) -> do
336336 q <- mkEnqueue chan run
337337 q t
338− return $ K.serial (f h) done
338+ return $ K.append (f h) done
339339
340340{-# INLINE parConcatMapChanKGeneric #-}
341341parConcatMapChanKGeneric :: MonadAsync m =>
@@ -771,7 +771,4 @@ tapCount ::
771771 -> (Stream m Int -> m b)
772772 -> Stream m a
773773 -> Stream m a
774−tapCount predicate f xs =
775− Stream.fromStreamD
776− $ tapCountD predicate (f . Stream.fromStreamD)
777− $ Stream.toStreamD xs
774+tapCount = tapCountD
src/Streamly/Internal/Data/Stream/Concurrent/Channel.hs+4−4
@@ -51,7 +51,7 @@ module Streamly.Internal.Data.Stream.Concurrent.Channel
5151where
5252
5353import Streamly.Internal.Control.Concurrent (MonadAsync)
54−import Streamly.Internal.Data.Stream (Stream, fromStreamK, toStreamK)
54+import Streamly.Internal.Data.Stream.StreamD (Stream)
5555import Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
5656 (fromChannel, fromChannelK, toChannel, toChannelK)
5757
@@ -59,7 +59,7 @@ import qualified Streamly.Internal.Data.Stream.Concurrent.Channel.Append
5959 as Append
6060import qualified Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
6161 as Interleave
62−import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
62+import qualified Streamly.Internal.Data.Stream.StreamK as K
6363
6464import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
6565import Streamly.Internal.Data.Stream.Channel.Types
@@ -102,5 +102,5 @@ withChannel :: MonadAsync m =>
102102 -> (Channel m b -> Stream m a -> Stream m b)
103103 -> Stream m b
104104withChannel modifier input evaluator =
105− let f chan stream = toStreamK $ evaluator chan (fromStreamK stream)
106− in fromStreamK $ withChannelK modifier (toStreamK input) f
105+ let f chan stream = K.fromStream $ evaluator chan (K.toStream stream)
106+ in K.toStream $ withChannelK modifier (K.fromStream input) f
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Operations.hs+2−2
@@ -34,11 +34,11 @@ import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
3434import Data.Maybe (isNothing)
3535import Streamly.Internal.Control.Concurrent
3636 (MonadAsync, MonadRunInIO, askRunInIO)
37−import Streamly.Internal.Data.Stream.Type (Stream)
37+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
3838import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
3939import System.Mem (performMajorGC)
4040
41−import qualified Streamly.Internal.Data.Stream as Stream
41+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
4242import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
4343import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
4444
src/Streamly/Internal/Data/Stream/Exception/Lifted.hs+4−6
@@ -35,7 +35,7 @@ import Data.Map.Strict (Map)
3535import GHC.Exts (inline)
3636import Streamly.Internal.Control.Concurrent
3737 (MonadRunInIO, MonadAsync, withRunInIO)
38−import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
38+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
3939import Streamly.Internal.Data.IOFinalizer.Lifted
4040 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
4141import Streamly.Internal.Data.Stream.StreamD (Step(..))
@@ -153,8 +153,7 @@ bracket3 :: (MonadAsync m, MonadCatch m)
153153 -> (b -> m e)
154154 -> (b -> Stream m a)
155155 -> Stream m a
156−bracket3 bef aft gc exc bet = fromStreamD $
157− bracket3D bef aft exc gc (toStreamD . bet)
156+bracket3 = bracket3D
158157
159158-- | Run the alloc action @m b@ with async exceptions disabled but keeping
160159-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
@@ -236,7 +235,7 @@ after ::
236235 (MonadIO m, MonadBaseControl IO m)
237236#endif
238237 => m b -> Stream m a -> Stream m a
239−after action xs = fromStreamD $ afterD action $ toStreamD xs
238+after = afterD
240239
241240data RetryState emap s1 s2
242241 = RetryWithMap emap s1
@@ -329,5 +328,4 @@ retry :: (MonadCatch m, Exception e, Ord e)
329328 -- ^ default handler for those exceptions that are not in the map
330329 -> Stream m a
331330 -> Stream m a
332−retry emap handler inp =
333− fromStreamD $ retryD emap (toStreamD . handler) $ toStreamD inp
331+retry = retryD
src/Streamly/Internal/Data/Stream/IsStream.hs+13−9
@@ -1,5 +1,4 @@
11{-# OPTIONS_GHC -Wno-deprecations -Wno-orphans #-}
2−{-# LANGUAGE StandaloneDeriving #-}
32
43-- |
54-- Module : Streamly.Internal.Data.Stream.IsStream
@@ -23,15 +22,13 @@ module Streamly.Internal.Data.Stream.IsStream {-# DEPRECATED "Please use \"Stre
2322 , module Streamly.Internal.Data.Stream.IsStream.Exception
2423 , module Streamly.Internal.Data.Stream.IsStream.Lift
2524 , module Streamly.Internal.Data.Stream.IsStream.Top
25+ , fromStream
26+ , toStream
2627 )
2728where
2829
29−import Control.DeepSeq (NFData(..), NFData1(..))
30−import Data.Functor.Identity (Identity(..))
31−import Streamly.Internal.Data.Stream.Zip (ZipStream(..))
32−
3330import Streamly.Internal.Data.Stream.IsStream.Top
34−import Streamly.Internal.Data.Stream.IsStream.Eliminate
31+import Streamly.Internal.Data.Stream.IsStream.Eliminate hiding (toStream)
3532import Streamly.Internal.Data.Stream.IsStream.Exception
3633import Streamly.Internal.Data.Stream.IsStream.Generate
3734import Streamly.Internal.Data.Stream.IsStream.Lift
@@ -40,7 +37,14 @@ import Streamly.Internal.Data.Stream.IsStream.Reduce
4037import Streamly.Internal.Data.Stream.IsStream.Transform
4138import Streamly.Internal.Data.Stream.IsStream.Type
4239 hiding (cmpBy, drain, eqBy, foldl', fold, toList, toStream
43− , fromEffect, fromPure, repeat)
40+ , fromEffect, fromPure, repeat, fromStream)
41+
42+import qualified Streamly.Internal.Data.Stream.StreamD as D
43+
44+{-# INLINE fromStream #-}
45+fromStream :: (IsStream t, Monad m) => D.Stream m a -> t m a
46+fromStream = fromStreamD
4447
45−deriving instance NFData a => NFData (ZipStream Identity a)
46−deriving instance NFData1 (ZipStream Identity)
48+{-# INLINE toStream #-}
49+toStream :: (IsStream t, Monad m) => t m a -> D.Stream m a
50+toStream = toStreamD
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+33−9
@@ -24,8 +24,7 @@ module Streamly.Internal.Data.Stream.IsStream.Common {-# DEPRECATED "Please use
2424
2525 -- * Elimination
2626 , foldContinue
27− , Stream.fold
28− , Stream.foldBreak
27+ , fold
2928
3029 -- * Transformation
3130 , map
@@ -82,7 +81,6 @@ import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
8281import Streamly.Internal.System.IO (defaultChunkSize)
8382import Streamly.Internal.Data.Unboxed (Unbox)
8483
85−import qualified Streamly.Data.Stream as Stream (fold, foldBreak)
8684import qualified Streamly.Internal.Data.Array.Type as A
8785import qualified Streamly.Internal.Data.Stream.Async as Async
8886import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
@@ -90,10 +88,10 @@ import qualified Streamly.Internal.Data.Stream.Parallel as Par
9088import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
9189 (fromPure, fromEffect, repeatMWith, reverse)
9290import qualified Streamly.Internal.Data.Stream.StreamD as D
93− (repeatM, times, foldContinue, map, scanlMAfter', postscanlMAfter'
91+ (repeatM, timesWith, foldAddLazy, map, scanlMAfter', postscanlMAfter'
9492 , postscanlM', take, takeWhile, takeEndBy, drop, findIndices
9593 , fromStreamK, toStreamK, concatMapM, concatMap, foldManyPost, splitOnSeq
96− , zipWithM, zipWith, intersperseM, reverse)
94+ , zipWithM, zipWith, intersperseM, reverse, fold)
9795
9896import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
9997
@@ -231,7 +229,7 @@ repeatMSerial = fromStreamD . D.repeatM
231229--
232230{-# INLINE timesWith #-}
233231timesWith :: (IsStream t, MonadAsync m) => Double -> t m (AbsTime, RelTime64)
234−timesWith g = fromStreamD $ D.times g
232+timesWith g = fromStreamD $ D.timesWith g
235233
236234-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
237235-- granularity @g@ specified in seconds. A low granularity clock is more
@@ -279,14 +277,40 @@ relTimesWith = fmap snd . timesWith
279277-- number of streams to a given fold efficiently with full stream fusion. For
280278-- example, to fold a list of streams on the same sum fold:
281279--
282−-- >>> concatFold = Prelude.foldl Stream.foldContinue Fold.sum
280+-- > concatFold = Prelude.foldl Stream.foldContinue Fold.sum
283281--
284−-- >>> fold f = Fold.extractM . Stream.foldContinue f
282+-- > fold f = Fold.extractM . Stream.foldContinue f
285283--
286284-- /Internal/
287285{-# INLINE foldContinue #-}
288286foldContinue :: Monad m => Fold m a b -> SerialT m a -> Fold m a b
289−foldContinue f s = D.foldContinue f $ IsStream.toStreamD s
287+foldContinue f s = D.foldAddLazy f $ IsStream.toStreamD s
288+
289+-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
290+-- expression strictly at each step. The behavior is similar to 'foldl''. A
291+-- 'Fold' can terminate early without consuming the full stream. See the
292+-- documentation of individual 'Fold's for termination behavior.
293+--
294+-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
295+-- 5050
296+--
297+-- Folds never fail, therefore, they produce a default value even when no input
298+-- is provided. It means we can always fold an empty stream and get a valid
299+-- result. For example:
300+--
301+-- >>> Stream.fold Fold.sum Stream.nil
302+-- 0
303+--
304+-- However, 'foldMany' on an empty stream results in an empty stream.
305+-- Therefore, @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany
306+-- f@.
307+--
308+-- @fold f = Stream.parse (Parser.fromFold f)@
309+--
310+-- @since 0.7.0
311+{-# INLINE fold #-}
312+fold :: Monad m => Fold m a b -> SerialT m a -> m b
313+fold fl strm = D.fold fl $ IsStream.toStreamD strm
290314
291315------------------------------------------------------------------------------
292316-- Transformation
src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+118−29
@@ -25,16 +25,12 @@ module Streamly.Internal.Data.Stream.IsStream.Eliminate {-# DEPRECATED "Please u
2525 -- * Running a 'Fold'
2626 -- See "Streamly.Internal.Data.Fold".
2727 fold
28− , foldBreak
29− , foldContinue
3028
3129 -- * Running a 'Parser'
3230 -- "Streamly.Internal.Data.Parser".
33− , Stream.parse
34− , Stream.parseK
35− , Stream.parseD
36− , Stream.parseBreak
37− , Stream.parseBreakD
31+ , parse
32+ , parseK
33+ , parseD
3834
3935 -- * Stream Deconstruction
4036 -- | foldr and foldl do not provide the remaining stream. 'uncons' is more
@@ -44,8 +40,8 @@ module Streamly.Internal.Data.Stream.IsStream.Eliminate {-# DEPRECATED "Please u
4440 , uncons
4541
4642 -- * Right Folds
47− , Stream.foldrM
48− , Stream.foldr
43+ , foldrM
44+ , foldr
4945
5046 -- * Left Folds
5147 -- Lazy left folds are useful only for reversing the stream
@@ -141,7 +137,7 @@ module Streamly.Internal.Data.Stream.IsStream.Eliminate {-# DEPRECATED "Please u
141137 -- trimming sequences
142138 , stripPrefix
143139 -- , stripInfix
144− , Stream.stripSuffix
140+ , stripSuffix
145141
146142 -- * Deprecated
147143 , foldx
@@ -160,27 +156,29 @@ import Control.Monad.IO.Class (MonadIO(..))
160156import Control.Monad.Trans.Class (MonadTrans(..))
161157import Foreign.Storable (Storable)
162158import Streamly.Internal.Control.Concurrent (MonadAsync)
159+import Streamly.Internal.Data.Parser (Parser (..))
163160import Streamly.Internal.Data.SVar (defState)
164161import Streamly.Internal.Data.Stream.IsStream.Common
165− ( fold, foldBreak, foldContinue, drop, findIndices, reverse, splitOnSeq
162+ ( fold, drop, findIndices, reverse, splitOnSeq
166163 , take , takeWhile, mkParallel)
167164import Streamly.Internal.Data.Stream.IsStream.Type
168− (IsStream, toStreamD, fromStreamD, toStreamD)
165+ (IsStream, fromStreamD, toStreamD)
169166import Streamly.Internal.Data.Stream.Serial (SerialT)
170167import Streamly.Internal.Data.Unboxed (Unbox)
171168
172−import qualified Streamly.Data.Array as A
173−import qualified Streamly.Data.Stream as Stream
169+import qualified Streamly.Internal.Data.Array as A
170+import qualified Streamly.Internal.Data.Stream.Serial as Stream
174171import qualified Streamly.Internal.Data.Fold as FL
172+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
173+import qualified Streamly.Internal.Data.Parser.ParserK.Type as PRK
175174import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
176175import qualified Streamly.Internal.Data.Stream.StreamD as D
177176 (foldr1, foldlT, foldlM', mapM_, null, head, headElse, last, elem
178177 , notElem, all, any, minimum, minimumBy, maximum, maximumBy, the, lookup
179− , find, findM, toListRev, isPrefixOf, isSubsequenceOf, stripPrefix, (!!))
178+ , find, findM, toListRev, isPrefixOf, isSubsequenceOf, stripPrefix, (!!)
179+ , fromStreamK, foldrM, parseD)
180180import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
181181 (uncons, foldlS, tail, init)
182−import qualified Streamly.Internal.Data.Stream as Stream
183− (foldr, toStreamK, parseK, parseD, parseBreakD, foldrM, stripSuffix)
184182import qualified System.IO as IO
185183
186184import Prelude hiding
@@ -216,16 +214,6 @@ import Prelude hiding
216214-- the stream one element at a time, therefore, does not take adavantage of
217215-- stream fusion.
218216--
219−-- 'foldBreak' is a more general way of consuming a stream piecemeal.
220−--
221−-- >>> :{
222−-- uncons xs = do
223−-- r <- Stream.foldBreak Fold.one xs
224−-- return $ case r of
225−-- (Nothing, _) -> Nothing
226−-- (Just h, t) -> Just (h, Stream.fromSerial t)
227−-- :}
228−--
229217-- @since 0.1.0
230218{-# INLINE uncons #-}
231219uncons :: (IsStream t, Monad m) => SerialT m a -> m (Maybe (a, t m a))
@@ -234,6 +222,46 @@ uncons = fmap (fmap (fmap IsStream.fromStream)) . K.uncons . Stream.toStreamK
234222------------------------------------------------------------------------------
235223-- Right Folds
236224------------------------------------------------------------------------------
225+
226+-- | Right associative/lazy pull fold. @foldrM build final stream@ constructs
227+-- an output structure using the step function @build@. @build@ is invoked with
228+-- the next input element and the remaining (lazy) tail of the output
229+-- structure. It builds a lazy output expression using the two. When the "tail
230+-- structure" in the output expression is evaluated it calls @build@ again thus
231+-- lazily consuming the input @stream@ until either the output expression built
232+-- by @build@ is free of the "tail" or the input is exhausted in which case
233+-- @final@ is used as the terminating case for the output structure. For more
234+-- details see the description in the previous section.
235+--
236+-- Example, determine if any element is 'odd' in a stream:
237+--
238+-- >>> Stream.foldrM (\x xs -> if odd x then return True else xs) (return False) $ Stream.fromList (2:4:5:undefined)
239+-- True
240+--
241+-- /Since: 0.7.0 (signature changed)/
242+--
243+-- /Since: 0.2.0 (signature changed)/
244+--
245+-- /Since: 0.1.0/
246+{-# INLINE foldrM #-}
247+foldrM :: Monad m => (a -> m b -> m b) -> m b -> SerialT m a -> m b
248+foldrM step acc (Stream.SerialT m) = D.foldrM step acc $ D.fromStreamK m
249+
250+-- | Right fold, lazy for lazy monads and pure streams, and strict for strict
251+-- monads.
252+--
253+-- Please avoid using this routine in strict monads like IO unless you need a
254+-- strict right fold. This is provided only for use in lazy monads (e.g.
255+-- Identity) or pure streams. Note that with this signature it is not possible
256+-- to implement a lazy foldr when the monad @m@ is strict. In that case it
257+-- would be strict in its accumulator and therefore would necessarily consume
258+-- all its input.
259+--
260+-- @since 0.1.0
261+{-# INLINE foldr #-}
262+foldr :: Monad m => (a -> b -> b) -> b -> SerialT m a -> m b
263+foldr f z = foldrM (\a b -> f a <$> b) (return z)
264+
237265-- XXX This seems to be of limited use as it cannot be used to construct
238266-- recursive structures and for reduction foldl1' is better.
239267--
@@ -336,6 +364,48 @@ foldlM' step begin = D.foldlM' step begin . IsStream.toStreamD
336364runSink :: Monad m => Sink m a -> SerialT m a -> m ()
337365runSink = fold . toFold
338366-}
367+
368+------------------------------------------------------------------------------
369+-- Running a Parser
370+------------------------------------------------------------------------------
371+
372+-- | Parse a stream using the supplied ParserD 'PRD.Parser'.
373+--
374+-- /Internal/
375+--
376+{-# INLINE_NORMAL parseD #-}
377+parseD :: Monad m => PRD.Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
378+parseD p = D.parseD p . toStreamD
379+
380+-- | Parse a stream using the supplied ParserK 'PRK.Parser'.
381+--
382+-- /Internal/
383+{-# INLINE parseK #-}
384+parseK :: Monad m => PRK.Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
385+parseK = parse
386+
387+-- | Parse a stream using the supplied 'Parser'.
388+--
389+-- Unlike folds, parsers may not always result in a valid output, they may
390+-- result in an error. For example:
391+--
392+-- >>> Stream.parse (Parser.takeEQ 1 Fold.drain) Stream.nil
393+-- Left (ParseError "takeEQ: Expecting exactly 1 elements, input terminated on 0")
394+--
395+-- Note:
396+--
397+-- @
398+-- fold f = Stream.parse (Parser.fromFold f)
399+-- @
400+--
401+-- @parse p@ is not the same as @head . parseMany p@ on an empty stream.
402+--
403+-- /Pre-release/
404+--
405+{-# INLINE [3] parse #-}
406+parse :: Monad m => Parser a m b -> SerialT m a -> m (Either PRD.ParseError b)
407+parse = parseD . PRD.fromParserK
408+
339409------------------------------------------------------------------------------
340410-- Specific Fold Functions
341411------------------------------------------------------------------------------
@@ -563,7 +633,7 @@ product = foldl' (*) 1
563633-- /Pre-release/
564634{-# INLINE mconcat #-}
565635mconcat :: (Monad m, Monoid a) => SerialT m a -> m a
566−mconcat = Stream.foldr mappend mempty
636+mconcat = foldr mappend mempty
567637
568638-- |
569639-- @
@@ -743,7 +813,7 @@ toHandle h = go
743813--
744814{-# INLINE toStream #-}
745815toStream :: Monad m => SerialT m a -> m (SerialT n a)
746−toStream = Stream.foldr IsStream.cons IsStream.nil
816+toStream = foldr IsStream.cons IsStream.nil
747817
748818-- | Convert a stream to a pure stream in reverse order.
749819--
@@ -923,6 +993,25 @@ stripPrefix
923993stripPrefix m1 m2 = fmap fromStreamD <$>
924994 D.stripPrefix (toStreamD m1) (toStreamD m2)
925995
996+-- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does
997+-- not end with the given suffix. Returns @Just nil@ when the suffix is the
998+-- same as the stream.
999+--
1000+-- It may be more efficient to convert the stream to an Array and use
1001+-- stripSuffix on that especially if the elements have a Storable or Prim
1002+-- instance.
1003+--
1004+-- See also "Streamly.Internal.Data.Stream.IsStream.Nesting.dropSuffix".
1005+--
1006+-- Space: @O(n)@, buffers the entire input stream as well as the suffix
1007+--
1008+-- /Pre-release/
1009+{-# INLINE stripSuffix #-}
1010+stripSuffix
1011+ :: (Monad m, Eq a)
1012+ => SerialT m a -> SerialT m a -> m (Maybe (SerialT m a))
1013+stripSuffix m1 m2 = fmap reverse <$> stripPrefix (reverse m1) (reverse m2)
1014+
9261015------------------------------------------------------------------------------
9271016-- Comparison
9281017------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/IsStream/Exception.hs+6−6
@@ -34,10 +34,10 @@ import Streamly.Internal.Data.Stream.IsStream.Type
3434
3535import qualified Streamly.Internal.Data.Stream.StreamD.Exception as D
3636 ( before
37− , after_
37+ , afterUnsafe
3838 , onException
39− , bracket_
40− , finally_
39+ , bracketUnsafe
40+ , finallyUnsafe
4141 , ghandle
4242 , handle
4343 )
@@ -86,7 +86,7 @@ before action xs = fromStreamD $ D.before action $ toStreamD xs
8686--
8787{-# INLINE after_ #-}
8888after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
89−after_ action xs = fromStreamD $ D.after_ action $ toStreamD xs
89+after_ action xs = fromStreamD $ D.afterUnsafe action $ toStreamD xs
9090
9191-- | Run the action @m b@ whenever the stream @t m a@ stops normally, or if it
9292-- is garbage collected after a partial lazy evaluation.
@@ -126,7 +126,7 @@ onException action xs = fromStreamD $ D.onException action $ toStreamD xs
126126--
127127{-# INLINE finally_ #-}
128128finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
129−finally_ action xs = fromStreamD $ D.finally_ action $ toStreamD xs
129+finally_ action xs = fromStreamD $ D.finallyUnsafe action $ toStreamD xs
130130
131131-- | Run the action @m b@ whenever the stream @t m a@ stops normally, aborts
132132-- due to an exception or if it is garbage collected after a partial lazy
@@ -161,7 +161,7 @@ finally action xs = bracket (return ()) (const action) (const xs)
161161bracket_ :: (IsStream t, MonadCatch m)
162162 => m b -> (b -> m c) -> (b -> t m a) -> t m a
163163bracket_ bef aft bet = fromStreamD $
164− D.bracket_ bef aft (toStreamD . bet)
164+ D.bracketUnsafe bef aft (toStreamD . bet)
165165
166166-- | Run the alloc action @m b@ with async exceptions disabled but keeping
167167-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+12−12
@@ -179,13 +179,13 @@ import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
179179import qualified Streamly.Internal.Data.Stream.Parallel as Par
180180import qualified Streamly.Internal.Data.Stream.Serial as Serial
181181import qualified Streamly.Internal.Data.Stream.StreamD as D
182− (append, interleave, interleaveSuffix, interleaveInfix, interleaveMin
183− , roundRobin, mergeByM, unfoldMany, unfoldManyInterleave, intersperse
184− , unfoldManyRoundRobin, interpose, interposeSuffix, gintercalate
182+ (append, interleave, interleaveFstSuffix, interleaveFst, interleaveMin
183+ , roundRobin, mergeByM, unfoldMany, unfoldInterleave, intersperse
184+ , unfoldRoundRobin, interpose, interposeSuffix, gintercalate
185185 , gintercalateSuffix, intersperseMSuffix)
186186import qualified Streamly.Internal.Data.Stream.StreamK as K (mergeBy, mergeByM)
187187import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
188− (interleave, serial, concatPairsWith)
188+ (interleave, append, mergeMapWith)
189189import qualified Streamly.Internal.Data.Stream.ZipAsync as ZipAsync
190190
191191import Prelude hiding (concat, concatMap, zipWith)
@@ -250,7 +250,7 @@ infixr 6 `serial`
250250-- @since 0.8.0
251251{-# INLINE serial #-}
252252serial :: IsStream t => t m a -> t m a -> t m a
253−serial m1 m2 = fromStream $ K.serial (toStream m1) (toStream m2)
253+serial m1 m2 = fromStream $ K.append (toStream m1) (toStream m2)
254254
255255-------------------------------------------------------------------------------
256256-- Interleaving
@@ -346,7 +346,7 @@ interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
346346{-# INLINE interleaveSuffix #-}
347347interleaveSuffix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
348348interleaveSuffix m1 m2 =
349− fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
349+ fromStreamD $ D.interleaveFstSuffix (toStreamD m1) (toStreamD m2)
350350
351351-- | Interleaves the outputs of two streams, yielding elements from each stream
352352-- alternately, starting from the first stream and ending at the first stream.
@@ -370,7 +370,7 @@ interleaveSuffix m1 m2 =
370370{-# INLINE interleaveInfix #-}
371371interleaveInfix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
372372interleaveInfix m1 m2 =
373− fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
373+ fromStreamD $ D.interleaveFst (toStreamD m1) (toStreamD m2)
374374
375375-- | Interleaves the outputs of two streams, yielding elements from each stream
376376-- alternately, starting from the first stream. The output stops as soon as any
@@ -835,7 +835,7 @@ unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
835835unfoldManyInterleave ::(IsStream t, Monad m)
836836 => Unfold m a b -> t m a -> t m b
837837unfoldManyInterleave u m =
838− fromStreamD $ D.unfoldManyInterleave u (toStreamD m)
838+ fromStreamD $ D.unfoldInterleave u (toStreamD m)
839839
840840-- | Like 'unfoldMany' but executes the streams in the same way as
841841-- 'roundrobin'.
@@ -845,7 +845,7 @@ unfoldManyInterleave u m =
845845unfoldManyRoundRobin ::(IsStream t, Monad m)
846846 => Unfold m a b -> t m a -> t m b
847847unfoldManyRoundRobin u m =
848− fromStreamD $ D.unfoldManyRoundRobin u (toStreamD m)
848+ fromStreamD $ D.unfoldRoundRobin u (toStreamD m)
849849
850850------------------------------------------------------------------------------
851851-- Combine N Streams - interpose
@@ -863,7 +863,7 @@ unfoldManyRoundRobin u m =
863863interpose :: (IsStream t, Monad m)
864864 => c -> Unfold m b c -> t m b -> t m c
865865interpose x unf str =
866− fromStreamD $ D.interpose (return x) unf (toStreamD str)
866+ fromStreamD $ D.interpose x unf (toStreamD str)
867867
868868-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
869869--
@@ -877,7 +877,7 @@ interpose x unf str =
877877interposeSuffix :: (IsStream t, Monad m)
878878 => c -> Unfold m b c -> t m b -> t m c
879879interposeSuffix x unf str =
880− fromStreamD $ D.interposeSuffix (return x) unf (toStreamD str)
880+ fromStreamD $ D.interposeSuffix x unf (toStreamD str)
881881
882882------------------------------------------------------------------------------
883883-- Combine N Streams - intercalate
@@ -1013,7 +1013,7 @@ concatPairsWith :: IsStream t =>
10131013 -> t m b
10141014concatPairsWith par f m =
10151015 fromStream
1016− $ K.concatPairsWith
1016+ $ K.mergeMapWith
10171017 (\s1 s2 -> toStream $ fromStream s1 `par` fromStream s2)
10181018 (toStream . f)
10191019 (toStream m)
src/Streamly/Internal/Data/Stream/IsStream/Generate.hs+2−3
@@ -102,7 +102,6 @@ import Streamly.Internal.Data.Stream.IsStream.Common
102102import Streamly.Internal.Data.Stream.IsStream.Type
103103 (IsStream (..), fromSerial, consM, fromStreamD)
104104import Streamly.Internal.Data.Stream.Serial (SerialT, WSerialT)
105−import Streamly.Internal.Data.Stream.Zip (ZipSerialM)
106105import Streamly.Internal.Data.Time.Units (AbsTime , RelTime64, addToAbsTime64)
107106import Streamly.Internal.Data.Unboxed (Unbox)
108107
@@ -118,7 +117,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
118117import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
119118 (unfoldr, unfoldrMWith, replicateMWith, fromIndicesMWith, iterateMWith
120119 , mfix, fromFoldable, fromFoldableM)
121−import qualified Streamly.Internal.Data.Stream.Type as Stream (fromStreamK)
120+import qualified Streamly.Internal.Data.Stream.Serial as Stream (fromStreamK)
122121import qualified System.IO as IO
123122
124123import Prelude hiding (iterate, replicate, repeat)
@@ -241,7 +240,7 @@ unfoldrMWSerial f = fromSerial . Serial.unfoldrM f
241240{-# RULES "unfoldrM zipSerial" unfoldrM = unfoldrMZipSerial #-}
242241{-# INLINE_EARLY unfoldrMZipSerial #-}
243242unfoldrMZipSerial :: MonadAsync m =>
244− (b -> m (Maybe (a, b))) -> b -> ZipSerialM m a
243+ (b -> m (Maybe (a, b))) -> b -> IsStream.ZipSerialM m a
245244unfoldrMZipSerial f = fromSerial . Serial.unfoldrM f
246245
247246------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/IsStream/Lift.hs+3−3
@@ -35,7 +35,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
3535import Streamly.Internal.Data.Stream.Serial (SerialT)
3636
3737import qualified Streamly.Internal.Data.Stream.StreamD as D
38− (hoist, liftInner, runReaderT, evalStateT, runStateT)
38+ (morphInner, liftInner, runReaderT, evalStateT, runStateT)
3939
4040------------------------------------------------------------------------------
4141-- Generalize the underlying monad
@@ -48,7 +48,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
4848{-# INLINE hoist #-}
4949hoist :: (Monad m, Monad n)
5050 => (forall x. m x -> n x) -> SerialT m a -> SerialT n a
51−hoist f xs = fromStreamD $ D.hoist f (toStreamD xs)
51+hoist f xs = fromStreamD $ D.morphInner f (toStreamD xs)
5252
5353-- | Generalize the inner monad of the stream from 'Identity' to any monad.
5454--
@@ -56,7 +56,7 @@ hoist f xs = fromStreamD $ D.hoist f (toStreamD xs)
5656--
5757{-# INLINE generally #-}
5858generally :: (IsStream t, Monad m) => t Identity a -> t m a
59−generally xs = fromStreamD $ D.hoist (return . runIdentity) (toStreamD xs)
59+generally xs = fromStreamD $ D.morphInner (return . runIdentity) (toStreamD xs)
6060
6161------------------------------------------------------------------------------
6262-- Add and remove a monad transformer
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+10−8
@@ -176,13 +176,14 @@ import Streamly.Internal.Data.Stream.IsStream.Common
176176 , fromPure)
177177import Streamly.Internal.Data.Stream.IsStream.Type
178178 (IsStream(..), fromStreamD, toStreamD, cons)
179+import Streamly.Internal.Data.Stream.Serial(toStreamK)
179180import Streamly.Internal.Data.Time.Units
180181 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
181182 , toAbsTime)
182183import Streamly.Internal.Data.Unboxed (Unbox)
183184
184185import qualified Data.Heap as H
185−import qualified Streamly.Data.Unfold as Unfold
186+import qualified Streamly.Internal.Data.Unfold as Unfold
186187import qualified Streamly.Internal.Data.Array.Type as A
187188 (arraysOf, read)
188189import qualified Streamly.Internal.Data.Fold as FL
@@ -197,8 +198,8 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
197198 , refoldMany
198199 , foldIterateM
199200 , refoldIterateM
200− , parseMany
201− , parseIterate
201+ , parseManyD
202+ , parseIterateD
202203 , groupsBy
203204 , groupsRollingBy
204205 , wordsBy
@@ -387,7 +388,7 @@ parseMany
387388 -> t m a
388389 -> t m (Either ParseError b)
389390parseMany p m =
390− fromStreamD $ D.parseMany (PRD.fromParserK p) (toStreamD m)
391+ fromStreamD $ D.parseManyD (PRD.fromParserK p) (toStreamD m)
391392
392393-- | Same as parseMany but for StreamD streams.
393394--
@@ -400,7 +401,7 @@ parseManyD
400401 -> t m a
401402 -> t m (Either ParseError b)
402403parseManyD p m =
403− fromStreamD $ D.parseMany p (toStreamD m)
404+ fromStreamD $ D.parseManyD p (toStreamD m)
404405
405406-- | Apply a stream of parsers to an input stream and emit the results in the
406407-- output stream.
@@ -455,7 +456,7 @@ parseIterate
455456 -> t m a
456457 -> t m (Either ParseError b)
457458parseIterate f i m = fromStreamD $
458− D.parseIterate (PRD.fromParserK . f) i (toStreamD m)
459+ D.parseIterateD (PRD.fromParserK . f) i (toStreamD m)
459460
460461------------------------------------------------------------------------------
461462-- Grouping
@@ -846,7 +847,7 @@ splitBySeq
846847 :: (IsStream t, MonadAsync m, Storable a, Unbox a, Enum a, Eq a)
847848 => Array a -> Fold m a b -> t m a -> t m b
848849splitBySeq patt f m =
849− intersperseM (fold f (A.read patt)) $ splitOnSeq patt f m
850+ intersperseM (fold f (fromStreamD $ A.read patt)) $ splitOnSeq patt f m
850851
851852-- | Like 'splitSuffixBy' but the separator is a sequence of elements, instead
852853-- of a predicate for a single element.
@@ -1322,7 +1323,8 @@ classifySessionsByGeneric
13221323 -> t m (Key f, b) -- ^ session key, fold result
13231324classifySessionsByGeneric _ tick reset ejectPred tmout
13241325 (Fold step initial extract) input =
1325− Expand.unfoldMany (Unfold.lmap sessionOutputStream Unfold.fromStream)
1326+ Expand.unfoldMany
1327+ (Unfold.lmap (toStreamK . sessionOutputStream) Unfold.fromStreamK)
13261328 $ scanlMAfter' sstep (return szero) (flush extract)
13271329 $ interjectSuffix tick (return Nothing)
13281330 $ map Just input
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+4−4
@@ -72,8 +72,8 @@ import qualified Data.Map.Strict as Map
7272-- import qualified Streamly.Internal.Data.Array.Generic as Array
7373-- (fromStream, length, read)
7474-- import qualified Streamly.Data.Array.Mut as MA
75−import qualified Streamly.Internal.Data.Fold as Fold
76− (one, last, toStream, toStreamRev)
75+import qualified Streamly.Internal.Data.Fold as Fold (one, last)
76+import qualified Streamly.Internal.Data.Fold.Type as Fold
7777import qualified Streamly.Internal.Data.Parser as Parser
7878 (groupByRollingEither)
7979-- import qualified Streamly.Internal.Data.Stream.IsStream.Lift as Stream
@@ -247,8 +247,8 @@ sortBy cmp =
247247 let p =
248248 Parser.groupByRollingEither
249249 (\x -> (< GT) . cmp x)
250− Fold.toStreamRev
251− Fold.toStream
250+ (fmap fromStream Fold.toStreamKRev)
251+ (fmap fromStream Fold.toStreamK)
252252 in Stream.concatPairsWith (Stream.mergeBy cmp) id
253253 . Stream.rights . Stream.parseMany (fmap (either id id) p)
254254
src/Streamly/Internal/Data/Stream/IsStream/Transform.hs+4−4
@@ -257,8 +257,8 @@ import qualified Streamly.Internal.Data.Stream.Concurrent as Concur
257257import qualified Streamly.Internal.Data.Stream.Parallel as Par
258258import qualified Streamly.Internal.Data.Stream.Serial as Serial
259259import qualified Streamly.Internal.Data.Stream.StreamD as D
260− (transform, foldrT, tap, tapOffsetEvery, mapM, scanOnce
261− , scanMany, postscanOnce, scanlx', scanlM', scanl', postscanl', prescanl'
260+ (transform, foldrT, tap, tapOffsetEvery, mapM, scan
261+ , scanMany, postscan, scanlx', scanlM', scanl', postscanl', prescanl'
262262 , prescanlM', scanl1M', scanl1', filter, filterM, uniq, deleteBy, takeWhileM
263263 , dropWhile, dropWhileM, insertBy, intersperse
264264 , intersperseM_, intersperseMSuffix, intersperseMSuffix_
@@ -631,7 +631,7 @@ trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD
631631-- @since 0.7.0
632632{-# INLINE scan #-}
633633scan :: (IsStream t, Monad m) => Fold m a b -> t m a -> t m b
634−scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
634+scan fld m = fromStreamD $ D.scan fld $ toStreamD m
635635
636636-- | Like 'scan' but restarts scanning afresh when the scanning fold
637637-- terminates.
@@ -659,7 +659,7 @@ scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m
659659-- @since 0.7.0
660660{-# INLINE postscan #-}
661661postscan :: (IsStream t, Monad m) => Fold m a b -> t m a -> t m b
662−postscan fld = fromStreamD . D.postscanOnce fld . toStreamD
662+postscan fld = fromStreamD . D.postscan fld . toStreamD
663663
664664------------------------------------------------------------------------------
665665-- Scanning - Transformation by Folding
src/Streamly/Internal/Data/Stream/IsStream/Type.hs+18−8
@@ -20,6 +20,8 @@ module Streamly.Internal.Data.Stream.IsStream.Type {-# DEPRECATED "Please use \"
2020 -- * Type Conversion
2121 , fromStreamD
2222 , toStreamD
23+ , toStreamK
24+ , fromStreamK
2325 , adapt
2426 , toConsK
2527
@@ -124,9 +126,9 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
124126import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
125127 (Stream(..), cons, fromEffect
126128 , nil, fromPure, bindWith, drain
127− , fromFoldable, consM, nilM, repeat)
128−import qualified Streamly.Data.Stream as Stream
129−import qualified Streamly.Internal.Data.Stream as Stream
129+ , fromFoldable, nilM, repeat)
130+import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
131+import qualified Streamly.Internal.Data.Stream.Serial as Stream
130132 (fromStreamK, toStreamK)
131133import qualified Streamly.Internal.Data.Stream.Zip as Zip
132134import qualified Streamly.Internal.Data.Stream.ZipAsync as ZipAsync
@@ -201,6 +203,14 @@ class
201203-- Type adapting combinators
202204-------------------------------------------------------------------------------
203205
206+{-# INLINE toStreamK #-}
207+toStreamK :: IsStream t => t m a -> StreamK.StreamK m a
208+toStreamK = toStream
209+
210+{-# INLINE fromStreamK #-}
211+fromStreamK :: IsStream t => StreamK.StreamK m a -> t m a
212+fromStreamK = fromStream
213+
204214-- XXX Move/reset the State here by reconstructing the stream with cleared
205215-- state. Can we make sure we do not do that when t1 = t2? If we do this then
206216-- we do not need to do that explicitly using svarStyle. It would act as
@@ -443,7 +453,7 @@ foldStream st yld sng stp m =
443453fromSerial :: IsStream t => SerialT m a -> t m a
444454fromSerial = adapt
445455
446−instance IsStream Stream.Stream where
456+instance IsStream SerialT where
447457 toStream = Stream.toStreamK
448458 fromStream = Stream.fromStreamK
449459
@@ -574,8 +584,8 @@ instance IsStream ParallelT where
574584-------------------------------------------------------------------------------
575585
576586consMZip :: Monad m => m a -> ZipSerialM m a -> ZipSerialM m a
577−consMZip m (Zip.ZipStream r) =
578− Zip.ZipStream $ Stream.fromStreamK $ K.consM m (Stream.toStreamK r)
587+consMZip m (Zip.ZipSerialM r) =
588+ Zip.ZipSerialM $ StreamK.consM m r
579589
580590-- | Fix the type of a polymorphic stream as 'ZipSerialM'.
581591--
@@ -585,8 +595,8 @@ consMZip m (Zip.ZipStream r) =
585595fromZipSerial :: IsStream t => ZipSerialM m a -> t m a
586596fromZipSerial = adapt
587597instance IsStream ZipSerialM where
588− toStream = Stream.toStreamK . Zip.unZipStream
589− fromStream = Zip.ZipStream . Stream.fromStreamK
598+ toStream = Zip.getZipSerialM
599+ fromStream = Zip.ZipSerialM
590600
591601 {-# INLINE consM #-}
592602 {-# SPECIALIZE consM :: IO a -> ZipSerialM IO a -> ZipSerialM IO a #-}
src/Streamly/Internal/Data/Stream/MkType.hs+7−12
@@ -215,6 +215,7 @@ derivOrdIdent _Type =
215215 (singleton <$> [t|Ord $(varT _a)|])
216216 (appT (conT _Ord) (foldl1 appT [conT _Type, conT _Identity, varT _a]))
217217
218+{-
218219derivTraversableIdent :: Name -> Q Dec
219220derivTraversableIdent _Type =
220221 standaloneDerivD
@@ -222,6 +223,7 @@ derivTraversableIdent _Type =
222223 (appT
223224 (conT _Traversable)
224225 (foldl1 appT [conT _Type, conT _Identity]))
226+-}
225227
226228showInstance :: Name -> Q Dec
227229showInstance _Type =
@@ -503,7 +505,7 @@ flattenDec (ma:mas) = do
503505-- >>> putStrLn $ pprint expr
504506-- newtype ZipStream m a
505507-- = ZipStream (Stream.Stream m a)
506−-- deriving (Semigroup, Monoid, Foldable)
508+-- deriving Foldable
507509-- mkZipStream :: Stream.Stream m a -> ZipStream m a
508510-- mkZipStream = ZipStream
509511-- unZipStream :: ZipStream m a -> Stream.Stream m a
@@ -513,7 +515,6 @@ flattenDec (ma:mas) = do
513515-- GHC.Types.Char => IsString (ZipStream Identity a)
514516-- deriving instance GHC.Classes.Eq a => Eq (ZipStream Identity a)
515517-- deriving instance GHC.Classes.Ord a => Ord (ZipStream Identity a)
516−-- deriving instance Traversable (ZipStream Identity)
517518-- instance Show a => Show (ZipStream Identity a)
518519-- where {-# INLINE show #-}
519520-- show (ZipStream strm) = show strm
@@ -535,17 +536,14 @@ mkZipType
535536 -> Q [Dec]
536537mkZipType dtNameStr apOpStr isConcurrent =
537538 flattenDec
538− [ typeDec dtNameStr
539− $ if not isConcurrent
540− then [_Semigroup, _Monoid, _Foldable]
541− else []
539+ [ typeDec dtNameStr [_Foldable | not isConcurrent]
542540 , sequence
543541 $ if not isConcurrent
544542 then [ derivIsListIdent _Type
545543 , derivIsStringIdent _Type
546544 , derivEqIdent _Type
547545 , derivOrdIdent _Type
548− , derivTraversableIdent _Type
546+ -- , derivTraversableIdent _Type
549547 , showInstance _Type
550548 , readInstance _Type
551549 ]
@@ -607,17 +605,14 @@ mkCrossType
607605 -> Q [Dec]
608606mkCrossType dtNameStr bindOpStr isConcurrent =
609607 flattenDec
610− [ typeDec dtNameStr
611− $ if not isConcurrent
612− then [_Semigroup, _Monoid, _Foldable]
613− else []
608+ [ typeDec dtNameStr [_Foldable | not isConcurrent]
614609 , sequence
615610 $ if not isConcurrent
616611 then [ derivIsListIdent _Type
617612 , derivIsStringIdent _Type
618613 , derivEqIdent _Type
619614 , derivOrdIdent _Type
620− , derivTraversableIdent _Type
615+ -- , derivTraversableIdent _Type
621616 , showInstance _Type
622617 , readInstance _Type
623618 ]
src/Streamly/Internal/Data/Stream/Parallel.hs+6−96
@@ -1,4 +1,5 @@
11{-# LANGUAGE UndecidableInstances #-}
2+{-# OPTIONS_GHC -fno-warn-deprecations #-}
23
34-- |
45-- Module : Streamly.Internal.Data.Stream.Parallel
@@ -11,7 +12,7 @@
1112--
1213-- To run examples in this module:
1314--
14−-- >>> import qualified Streamly.Data.Stream as Stream
15+-- >>> import qualified Streamly.Prelude as Stream
1516-- >>> import Control.Concurrent (threadDelay)
1617-- >>> :{
1718-- delay n = do
@@ -42,15 +43,10 @@ module Streamly.Internal.Data.Stream.Parallel {-# DEPRECATED "Please use \"Strea
4243
4344 -- * Callbacks
4445 , newCallbackStream
45−
46− -- * Combinators
47− , interjectSuffix
48− , takeInterval
49− , dropInterval
5046 )
5147where
5248
53−import Control.Concurrent (myThreadId, takeMVar, threadDelay)
49+import Control.Concurrent (myThreadId, takeMVar)
5450import Control.Monad (when)
5551import Control.Monad.Base (MonadBase(..), liftBaseDefault)
5652import Control.Monad.Catch (MonadThrow, throwM)
@@ -61,12 +57,11 @@ import Control.Monad.State.Class (MonadState(..))
6157import Control.Monad.Trans.Class (MonadTrans(lift))
6258import Data.Functor (void)
6359import Data.IORef (readIORef, writeIORef)
64−import Data.Maybe (fromJust, isNothing)
60+import Data.Maybe (fromJust)
6561
6662import Streamly.Data.Fold (Fold)
6763import Streamly.Internal.Control.Concurrent (MonadAsync)
6864import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
69−import Streamly.Internal.Data.Stream.Type (Stream)
7065
7166import qualified Data.Set as Set
7267import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
@@ -76,8 +71,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
7671 (Stream(..), mapM, toStreamK, fromStreamK)
7772import qualified Streamly.Internal.Data.Stream.SVar.Generate as SVar
7873import qualified Streamly.Internal.Data.Stream.SVar.Eliminate as SVar
79−import qualified Streamly.Internal.Data.Stream as Stream
80− (catMaybes, dropWhile, fromStreamK, repeat, sequence, takeWhile, toStreamK)
74+import qualified Streamly.Internal.Data.Stream.Serial as Stream
8175
8276import Streamly.Internal.Data.SVar
8377import Prelude hiding (map)
@@ -87,7 +81,7 @@ import Prelude hiding (map)
8781
8882--
8983-- $setup
90−-- >>> import qualified Streamly.Data.Stream as Stream
84+-- >>> import qualified Streamly.Prelude as Stream
9185-- >>> import Control.Concurrent (threadDelay)
9286-- >>> :{
9387-- delay n = do
@@ -538,87 +532,3 @@ newCallbackStream = do
538532 -- XXX we can return an SVar and then the consumer can unfold from the
539533 -- SVar?
540534 return (callback, D.toStreamK (SVar.fromSVarD sv))
541−
542−------------------------------------------------------------------------------
543−-- Combinators
544−------------------------------------------------------------------------------
545−
546−{-# INLINE parallelFst #-}
547−parallelFst :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
548−parallelFst m1 m2 =
549− Stream.fromStreamK
550− $ parallelFstK (Stream.toStreamK m1) (Stream.toStreamK m2)
551−
552−-- | Intersperse a monadic action into the input stream after every @n@
553−-- seconds.
554−--
555−-- >>> import qualified Streamly.Data.Fold as Fold
556−-- >>> import qualified Streamly.Internal.Data.Stream.Parallel as Parallel
557−-- >>> Stream.fold Fold.drain $ Parallel.interjectSuffix 1.05 (putChar ',') $ Stream.mapM (\x -> threadDelay 1000000 >> putChar x) $ Stream.fromList "hello"
558−-- h,e,l,l,o
559−--
560−-- /Pre-release/
561−{-# INLINE interjectSuffix #-}
562−interjectSuffix :: MonadAsync m => Double -> m a -> Stream m a -> Stream m a
563−interjectSuffix n f xs = xs `parallelFst` repeatM timed
564− where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
565− repeatM = Stream.sequence . Stream.repeat
566−
567−-- XXX Notes from D.takeByTime (which was removed)
568−-- XXX using getTime in the loop can be pretty expensive especially for
569−-- computations where iterations are lightweight. We have the following
570−-- options:
571−--
572−-- 1) Run a timeout thread updating a flag asynchronously and check that
573−-- flag here, that way we can have a cheap termination check.
574−--
575−-- 2) Use COARSE clock to get time with lower resolution but more efficiently.
576−--
577−-- 3) Use rdtscp/rdtsc to get time directly from the processor, compute the
578−-- termination value of rdtsc in the beginning and then in each iteration just
579−-- get rdtsc and check if we should terminate.
580−
581−
582−-- | @takeInterval duration@ yields stream elements upto specified time
583−-- @duration@ in seconds. The duration starts when the stream is evaluated for
584−-- the first time, before the first element is yielded. The time duration is
585−-- checked before generating each element, if the duration has expired the
586−-- stream stops.
587−--
588−-- The total time taken in executing the stream is guaranteed to be /at least/
589−-- @duration@, however, because the duration is checked before generating an
590−-- element, the upper bound is indeterminate and depends on the time taken in
591−-- generating and processing the last element.
592−--
593−-- No element is yielded if the duration is zero. At least one element is
594−-- yielded if the duration is non-zero.
595−--
596−-- /Pre-release/
597−--
598−{-# INLINE takeInterval #-}
599−takeInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
600−takeInterval d =
601− Stream.catMaybes
602− . Stream.takeWhile isNothing
603− . interjectSuffix d (return Nothing) . fmap Just
604−
605−-- | @dropInterval duration@ drops stream elements until specified @duration@ in
606−-- seconds has passed. The duration begins when the stream is evaluated for the
607−-- first time. The time duration is checked /after/ generating a stream element,
608−-- the element is yielded if the duration has expired otherwise it is dropped.
609−--
610−-- The time elapsed before starting to generate the first element is /at most/
611−-- @duration@, however, because the duration expiry is checked after the
612−-- element is generated, the lower bound is indeterminate and depends on the
613−-- time taken in generating an element.
614−--
615−-- All elements are yielded if the duration is zero.
616−--
617−-- /Pre-release/
618−--
619−{-# INLINE dropInterval #-}
620−dropInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
621−dropInterval d =
622− Stream.catMaybes
623− . Stream.dropWhile isNothing
624− . interjectSuffix d (return Nothing) . fmap Just
src/Streamly/Internal/Data/Stream/SVar/Eliminate.hs+5−4
@@ -1,3 +1,4 @@
1+{-# OPTIONS_GHC -fno-warn-deprecations #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.SVar.Eliminate
34-- Copyright : (c) 2017 Composewell Technologies
@@ -36,14 +37,14 @@ import Streamly.Internal.Control.ForkLifted (doFork)
3637import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
3738import Streamly.Internal.Data.Fold.SVar (write, writeLimited)
3839import Streamly.Internal.Data.Fold.Type (Fold(..))
39−import Streamly.Data.Stream (Stream)
40+import Streamly.Internal.Data.Stream.Serial (SerialT)
4041import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
4142
4243import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
4344 (Stream(..), Step(..), fold)
4445import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
4546 (Stream(..), mkStream, foldStream, foldStreamShared, nilM)
46−import qualified Streamly.Internal.Data.Stream.Type as Stream
47+import qualified Streamly.Internal.Data.Stream.Serial as Stream
4748 (fromStreamK, toStreamK)
4849
4950import Streamly.Internal.Data.SVar
@@ -210,7 +211,7 @@ fromProducer sv = K.mkStream $ \st yld sng stp -> do
210211--
211212{-# INLINE newFoldSVar #-}
212213newFoldSVar :: MonadAsync m
213− => State K.Stream m a -> (Stream m a -> m b) -> m (SVar K.Stream m a)
214+ => State K.Stream m a -> (SerialT m a -> m b) -> m (SVar K.Stream m a)
214215newFoldSVar stt f = do
215216 -- Buffer size for the SVar is derived from the current state
216217 sv <- newParallelVar StopAny (adaptState stt)
@@ -372,7 +373,7 @@ pushToFold sv a = do
372373--
373374{-# INLINE teeToSVar #-}
374375teeToSVar :: MonadAsync m =>
375− SVar K.Stream m a -> Stream m a -> Stream m a
376+ SVar K.Stream m a -> SerialT m a -> SerialT m a
376377teeToSVar svr m = Stream.fromStreamK $ K.mkStream $ \st yld sng stp -> do
377378 K.foldStreamShared st yld sng stp (go False $ Stream.toStreamK m)
378379
src/Streamly/Internal/Data/Stream/SVar/Generate.hs+5−4
@@ -1,3 +1,4 @@
1+{-# OPTIONS_GHC -fno-warn-deprecations #-}
12#ifdef __HADDOCK_VERSION__
23#undef INSPECTION
34#endif
@@ -38,7 +39,7 @@ import Control.Monad.IO.Class (MonadIO(liftIO))
3839import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
3940import Data.Maybe (isNothing)
4041import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
41−import Streamly.Internal.Data.Stream.Type (Stream)
42+import Streamly.Internal.Data.Stream.Serial (SerialT)
4243import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
4344import System.Mem (performMajorGC)
4445
@@ -46,7 +47,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
4647 (Stream(..), Step(..))
4748import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
4849 (Stream(..), foldStreamShared, mkStream, foldStream)
49−import qualified Streamly.Internal.Data.Stream.Type as Stream (fromStreamK)
50+import qualified Streamly.Internal.Data.Stream.Serial as Stream (fromStreamK)
5051
5152import Streamly.Internal.Data.SVar
5253
@@ -90,7 +91,7 @@ import Test.Inspection (inspect, hasNoTypeClassesExcept)
9091-- XXX this errors out for Parallel/Ahead SVars
9192-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
9293-- be read back from the SVar using 'fromSVar'.
93−toSVar :: MonadAsync m => SVar Stream m a -> Stream m a -> m ()
94+toSVar :: MonadAsync m => SVar SerialT m a -> SerialT m a -> m ()
9495toSVar sv m = do
9596 runIn <- askRunInIO
9697 liftIO $ enqueue sv (runIn, m)
@@ -185,7 +186,7 @@ inspect $ hasNoTypeClassesExcept 'fromStreamVar
185186-- combinators.
186187--
187188{-# INLINE fromSVar #-}
188−fromSVar :: MonadAsync m => SVar K.Stream m a -> Stream m a
189+fromSVar :: MonadAsync m => SVar K.Stream m a -> SerialT m a
189190fromSVar sv =
190191 Stream.fromStreamK $ K.mkStream $ \st yld sng stp -> do
191192 ref <- liftIO $ newIORef ()
src/Streamly/Internal/Data/Stream/Serial.hs+172−101
@@ -12,13 +12,14 @@
1212--
1313-- To run examples in this module:
1414--
15−-- >>> import qualified Streamly.Data.Stream as Stream
16−-- >>> import qualified Streamly.Prelude as IsStream
15+-- >>> import qualified Streamly.Prelude as Stream
1716--
1817module Streamly.Internal.Data.Stream.Serial {-# DEPRECATED "Please use \"Streamly.Internal.Data.Stream\" from streamly-core package instead." #-}
1918 (
2019 -- * Serial appending stream
21− SerialT
20+ SerialT(..)
21+ , toStreamK
22+ , fromStreamK
2223 , Serial
2324 , serial
2425
@@ -31,10 +32,10 @@ module Streamly.Internal.Data.Stream.Serial {-# DEPRECATED "Please use \"Streaml
3132 , consMWSerial
3233
3334 -- * Construction
34− , Stream.cons
35− , Stream.consM
36− , Stream.repeat
37− , Stream.unfoldrM
35+ , cons
36+ , consM
37+ , repeat
38+ , unfoldrM
3839 , fromList
3940
4041 -- * Elimination
@@ -42,7 +43,7 @@ module Streamly.Internal.Data.Stream.Serial {-# DEPRECATED "Please use \"Streaml
4243
4344 -- * Transformation
4445 , map
45− , Stream.mapM
46+ , mapM
4647 )
4748where
4849
@@ -64,18 +65,13 @@ import Text.Read
6465 , readListPrecDefault)
6566import Streamly.Internal.BaseCompat ((#.))
6667import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
67−import Streamly.Data.Stream (Stream)
68+import Streamly.Internal.Data.Stream.StreamK.Type (Stream)
6869
69−import qualified Streamly.Data.Fold as Fold
70−import qualified Streamly.Data.Stream as Stream
71−import qualified Streamly.Internal.Data.Stream as Stream
72− (toStreamK, fromStreamK, crossApply, crossApplySnd, crossApplyFst)
7370import qualified Streamly.Internal.Data.Stream.Common as P
74−import qualified Streamly.Internal.Data.Stream.StreamD as D
75− (fromStreamK, toStreamK, mapM)
71+import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
72+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
73+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
7674import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
77− (Stream, mkStream, foldStream, cons, consM, nil, concatMapWith, fromPure
78− , bindWith, interleave, interleaveFst, interleaveMin, serial, fromEffect)
7975
8076import Prelude hiding (map, mapM, repeat, filter)
8177
@@ -84,7 +80,6 @@ import Prelude hiding (map, mapM, repeat, filter)
8480
8581-- $setup
8682-- >>> :set -fno-warn-deprecations
87−-- >>> import qualified Streamly.Data.Stream as Stream
8883-- >>> import qualified Streamly.Prelude as IsStream
8984
9085{-# INLINABLE withLocal #-}
@@ -96,41 +91,99 @@ withLocal f m =
9691 in K.foldStream st yieldk single (local f stp) m
9792
9893------------------------------------------------------------------------------
99−-- Applicative - orphan instances
94+-- SerialT
10095------------------------------------------------------------------------------
10196
102−-- Note: we need to define all the typeclass operations because we want to
103−-- INLINE them.
104−instance Monad m => Applicative (Stream m) where
105− {-# INLINE pure #-}
106− pure = Stream.fromStreamK . K.fromPure
97+-- | For 'SerialT' streams:
98+--
99+-- @
100+-- (<>) = 'Streamly.Prelude.serial' -- 'Semigroup'
101+-- (>>=) = flip . 'Streamly.Prelude.concatMapWith' 'Streamly.Prelude.serial' -- 'Monad'
102+-- @
103+--
104+-- A single 'Monad' bind behaves like a @for@ loop:
105+--
106+-- >>> :{
107+-- IsStream.toList $ do
108+-- x <- IsStream.fromList [1,2] -- foreach x in stream
109+-- return x
110+-- :}
111+-- [1,2]
112+--
113+-- Nested monad binds behave like nested @for@ loops:
114+--
115+-- >>> :{
116+-- IsStream.toList $ do
117+-- x <- IsStream.fromList [1,2] -- foreach x in stream
118+-- y <- IsStream.fromList [3,4] -- foreach y in stream
119+-- return (x, y)
120+-- :}
121+-- [(1,3),(1,4),(2,3),(2,4)]
122+--
123+-- /Since: 0.2.0 ("Streamly")/
124+--
125+-- @since 0.8.0
126+newtype SerialT m a = SerialT {getSerialT :: Stream m a}
127+ -- XXX when deriving do we inherit an INLINE?
128+ deriving (Semigroup, Monoid)
107129
108− {-# INLINE (<*>) #-}
109− (<*>) = Stream.crossApply
110− -- (<*>) = K.apSerial
130+-- | A serial IO stream of elements of type @a@. See 'SerialT' documentation
131+-- for more details.
132+--
133+-- /Since: 0.2.0 ("Streamly")/
134+--
135+-- @since 0.8.0
136+type Serial = SerialT IO
111137
112− {-# INLINE liftA2 #-}
113− liftA2 f x = (<*>) (fmap f x)
138+toStreamK :: SerialT m a -> Stream m a
139+toStreamK = getSerialT
114140
115− {-# INLINE (*>) #-}
116− (*>) = Stream.crossApplySnd
117− -- (*>) = K.apSerialDiscardFst
141+fromStreamK :: Stream m a -> SerialT m a
142+fromStreamK = SerialT
118143
119− {-# INLINE (<*) #-}
120− (<*) = Stream.crossApplyFst
121− -- (<*) = K.apSerialDiscardSnd
144+------------------------------------------------------------------------------
145+-- Generation
146+------------------------------------------------------------------------------
147+
148+infixr 5 `cons`
149+
150+{-# INLINE cons #-}
151+cons :: a -> SerialT m a -> SerialT m a
152+cons x (SerialT ms) = SerialT $ K.cons x ms
153+
154+infixr 5 `consM`
155+
156+{-# INLINE consM #-}
157+{-# SPECIALIZE consM :: IO a -> SerialT IO a -> SerialT IO a #-}
158+consM :: Monad m => m a -> SerialT m a -> SerialT m a
159+consM m (SerialT ms) = SerialT $ K.consM m ms
160+
161+-- |
162+-- Generate an infinite stream by repeating a pure value.
163+--
164+{-# INLINE_NORMAL repeat #-}
165+repeat :: Monad m => a -> SerialT m a
166+repeat = SerialT . D.toStreamK . D.repeat
122167
123168------------------------------------------------------------------------------
124−-- Monad - orphan instances
169+-- Combining
125170------------------------------------------------------------------------------
126171
127−instance Monad m => Monad (Stream m) where
172+{-# INLINE serial #-}
173+serial :: SerialT m a -> SerialT m a -> SerialT m a
174+serial = (<>)
175+
176+------------------------------------------------------------------------------
177+-- Monad
178+------------------------------------------------------------------------------
179+
180+instance Monad m => Monad (SerialT m) where
128181 return = pure
129182
130183 -- Benchmarks better with StreamD bind and pure:
131184 -- toList, filterAllout, *>, *<, >> (~2x)
132185 --
133− -- pure = Stream . D.fromStreamD . D.fromPure
186+ -- pure = SerialT . D.fromStreamD . D.fromPure
134187 -- m >>= f = D.fromStreamD $ D.concatMap (D.toStreamD . f) (D.toStreamD m)
135188
136189 -- Benchmarks better with CPS bind and pure:
@@ -138,75 +191,22 @@ instance Monad m => Monad (Stream m) where
138191 -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
139192 --
140193 {-# INLINE (>>=) #-}
141− (>>=) m f =
142− Stream.fromStreamK
143− $ K.bindWith K.serial (Stream.toStreamK m) (Stream.toStreamK . f)
194+ (>>=) (SerialT m) f = SerialT $ K.bindWith K.append m (getSerialT . f)
144195
145196 {-# INLINE (>>) #-}
146197 (>>) = (*>)
147198
148−------------------------------------------------------------------------------
149−-- Transformers - orphan instances
150−------------------------------------------------------------------------------
151−
152−instance (MonadIO m) => MonadIO (Stream m) where
153− liftIO = lift . liftIO
154−
155−instance (MonadThrow m) => MonadThrow (Stream m) where
156− throwM = lift . throwM
157−
158−instance MonadTrans Stream where
199+instance MonadTrans SerialT where
159200 {-# INLINE lift #-}
160− lift = Stream.fromEffect
161−
162−instance (MonadReader r m) => MonadReader r (Stream m) where
163− ask = lift ask
164−
165− local f m = Stream.fromStreamK $ withLocal f (Stream.toStreamK m)
166−
167−instance (MonadState s m) => MonadState s (Stream m) where
168− {-# INLINE get #-}
169− get = lift get
170−
171− {-# INLINE put #-}
172− put x = lift (put x)
173−
174− {-# INLINE state #-}
175− state k = lift (state k)
201+ lift = SerialT . K.fromEffect
176202
177203------------------------------------------------------------------------------
178−-- NFData - orphan instances
179−------------------------------------------------------------------------------
180−
181−instance NFData a => NFData (Stream Identity a) where
182− {-# INLINE rnf #-}
183− rnf xs = runIdentity $ Stream.fold (Fold.foldl' (\_ x -> rnf x) ()) xs
184−
185−instance NFData1 (Stream Identity) where
186− {-# INLINE liftRnf #-}
187− liftRnf f xs = runIdentity $ Stream.fold (Fold.foldl' (\_ x -> f x) ()) xs
188−
189−------------------------------------------------------------------------------
190−-- SerialT
191−------------------------------------------------------------------------------
192−
193−type SerialT = Stream
194−
195−-- | A serial IO stream of elements of type @a@. See 'SerialT' documentation
196−-- for more details.
197−--
198−-- /Since: 0.2.0 ("Streamly")/
199−--
200−-- @since 0.8.0
201−type Serial = SerialT IO
202−
203−------------------------------------------------------------------------------
204−-- Combining
204+-- Other instances
205205------------------------------------------------------------------------------
206206
207−{-# INLINE serial #-}
208−serial :: SerialT m a -> SerialT m a -> SerialT m a
209−serial = (<>)
207+{-# INLINE mapM #-}
208+mapM :: Monad m => (a -> m b) -> SerialT m a -> SerialT m b
209+mapM f (SerialT m) = SerialT $ D.toStreamK $ D.mapM f $ D.fromStreamK m
210210
211211-- |
212212-- @
@@ -223,7 +223,49 @@ serial = (<>)
223223-- @since 0.4.0
224224{-# INLINE map #-}
225225map :: Monad m => (a -> b) -> SerialT m a -> SerialT m b
226−map f = Stream.mapM (return . f)
226+map f = mapM (return . f)
227+
228+{-# INLINE apSerial #-}
229+apSerial :: Monad m => SerialT m (a -> b) -> SerialT m a -> SerialT m b
230+apSerial (SerialT m1) (SerialT m2) =
231+ SerialT $ D.toStreamK $ D.fromStreamK m1 `D.crossApply` D.fromStreamK m2
232+
233+{-# INLINE apSequence #-}
234+apSequence :: Monad m => SerialT m a -> SerialT m b -> SerialT m b
235+apSequence (SerialT m1) (SerialT m2) =
236+ SerialT $ D.toStreamK $ D.fromStreamK m1 `D.crossApplySnd` D.fromStreamK m2
237+
238+{-# INLINE apDiscardSnd #-}
239+apDiscardSnd :: Monad m => SerialT m a -> SerialT m b -> SerialT m a
240+apDiscardSnd (SerialT m1) (SerialT m2) =
241+ SerialT $ D.toStreamK $ D.fromStreamK m1 `D.crossApplyFst` D.fromStreamK m2
242+
243+-- Note: we need to define all the typeclass operations because we want to
244+-- INLINE them.
245+instance Monad m => Applicative (SerialT m) where
246+ {-# INLINE pure #-}
247+ pure = SerialT . K.fromPure
248+
249+ {-# INLINE (<*>) #-}
250+ (<*>) = apSerial
251+ -- (<*>) = K.apSerial
252+
253+ {-# INLINE liftA2 #-}
254+ liftA2 f x = (<*>) (fmap f x)
255+
256+ {-# INLINE (*>) #-}
257+ (*>) = apSequence
258+ -- (*>) = K.apSerialDiscardFst
259+
260+ {-# INLINE (<*) #-}
261+ (<*) = apDiscardSnd
262+ -- (<*) = K.apSerialDiscardSnd
263+
264+MONAD_COMMON_INSTANCES(SerialT,)
265+LIST_INSTANCES(SerialT)
266+NFDATA1_INSTANCE(SerialT)
267+FOLDABLE_INSTANCE(SerialT)
268+TRAVERSABLE_INSTANCE(SerialT)
227269
228270------------------------------------------------------------------------------
229271-- WSerialT
@@ -267,12 +309,12 @@ map f = Stream.mapM (return . f)
267309-- [(1,3),(2,3),(1,4),(2,4)]
268310--
269311-- The @W@ in the name stands for @wide@ or breadth wise scheduling in
270−-- contrast to the depth wise scheduling behavior of 'Stream'.
312+-- contrast to the depth wise scheduling behavior of 'SerialT'.
271313--
272314-- /Since: 0.2.0 ("Streamly")/
273315--
274316-- @since 0.8.0
275−newtype WSerialT m a = WSerialT {getWSerialT :: K.Stream m a}
317+newtype WSerialT m a = WSerialT {getWSerialT :: Stream m a}
276318
277319instance MonadTrans WSerialT where
278320 {-# INLINE lift #-}
@@ -370,3 +412,32 @@ LIST_INSTANCES(WSerialT)
370412NFDATA1_INSTANCE(WSerialT)
371413FOLDABLE_INSTANCE(WSerialT)
372414TRAVERSABLE_INSTANCE(WSerialT)
415+
416+------------------------------------------------------------------------------
417+-- Construction
418+------------------------------------------------------------------------------
419+
420+-- | Build a stream by unfolding a /monadic/ step function starting from a
421+-- seed. The step function returns the next element in the stream and the next
422+-- seed value. When it is done it returns 'Nothing' and the stream ends. For
423+-- example,
424+--
425+-- @
426+-- let f b =
427+-- if b > 3
428+-- then return Nothing
429+-- else print b >> return (Just (b, b + 1))
430+-- in drain $ unfoldrM f 0
431+-- @
432+-- @
433+-- 0
434+-- 1
435+-- 2
436+-- 3
437+-- @
438+--
439+-- /Pre-release/
440+--
441+{-# INLINE unfoldrM #-}
442+unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> SerialT m a
443+unfoldrM step seed = SerialT $ D.toStreamK (D.unfoldrM step seed)
src/Streamly/Internal/Data/Stream/Time.hs+3−3
@@ -62,7 +62,7 @@ import Data.Proxy (Proxy(..))
6262import Streamly.Data.Fold (Fold)
6363import Streamly.Internal.Data.Fold.Type (Fold (..))
6464import Streamly.Internal.Data.IsMap (IsMap(..))
65−import Streamly.Internal.Data.Stream.Type (Stream)
65+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
6666import Streamly.Internal.Data.Time.Units
6767 ( AbsTime
6868 , MilliSecond64(..)
@@ -78,7 +78,7 @@ import qualified Streamly.Data.Stream as Stream
7878import qualified Streamly.Data.Unfold as Unfold
7979import qualified Streamly.Internal.Data.Fold as Fold (Step(..))
8080import qualified Streamly.Internal.Data.IsMap as IsMap
81−import qualified Streamly.Internal.Data.Stream as Stream
81+import qualified Streamly.Internal.Data.Stream.StreamD as Stream
8282 ( scanlMAfter'
8383 , timeIndexed
8484 , timestamped
@@ -392,7 +392,7 @@ ejectEntry :: (Monad m, IsMap f) =>
392392 -> m (heap, f entry, Stream m (Key f, b), Int)
393393ejectEntry extract hp mp out cnt acc key = do
394394 sess <- extract acc
395− let out1 = (key, sess) `Stream.cons` out
395+ let out1 = Stream.cons (key, sess) out
396396 let mp1 = IsMap.mapDelete key mp
397397 return (hp, mp1, out1, cnt - 1)
398398
src/Streamly/Internal/Data/Stream/Zip.hsnew file+135
@@ -0,0 +1,135 @@
1+{-# LANGUAGE UndecidableInstances #-}
2+{-# OPTIONS_GHC -Wno-deprecations #-}
3+
4+-- |
5+-- Module : Streamly.Internal.Data.Stream.Zip
6+-- Copyright : (c) 2017 Composewell Technologies
7+--
8+-- License : BSD3
9+-- Maintainer : streamly@composewell.com
10+-- Stability : experimental
11+-- Portability : GHC
12+--
13+-- To run examples in this module:
14+--
15+-- >>> import qualified Streamly.Prelude as Stream
16+--
17+module Streamly.Internal.Data.Stream.Zip
18+ (
19+ ZipSerialM (..)
20+ , ZipSerial
21+ , consMZip
22+ , zipWithK
23+ , zipWithMK
24+
25+ -- * Deprecated
26+ , ZipStream
27+ )
28+where
29+
30+import Control.Applicative (liftA2)
31+import Control.DeepSeq (NFData(..))
32+#if MIN_VERSION_deepseq(1,4,3)
33+import Control.DeepSeq (NFData1(..))
34+#endif
35+import Data.Foldable (Foldable(foldl'), fold)
36+import Data.Functor.Identity (Identity(..), runIdentity)
37+import Data.Maybe (fromMaybe)
38+import Data.Semigroup (Endo(..))
39+#if __GLASGOW_HASKELL__ < 808
40+import Data.Semigroup (Semigroup(..))
41+#endif
42+import GHC.Exts (IsList(..), IsString(..), oneShot)
43+import Text.Read
44+ ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
45+ , readListPrecDefault)
46+import Streamly.Internal.BaseCompat ((#.))
47+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
48+import Streamly.Internal.Data.Stream.Serial (SerialT(..))
49+import Streamly.Internal.Data.Stream.StreamK.Type (Stream)
50+
51+import qualified Streamly.Internal.Data.Stream.Common as P
52+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
53+import qualified Streamly.Internal.Data.Stream.StreamD as D
54+import qualified Streamly.Internal.Data.Stream.Serial as Serial
55+
56+import Prelude hiding (map, repeat, zipWith)
57+
58+#include "Instances.hs"
59+
60+-- $setup
61+-- >>> import qualified Streamly.Prelude as Stream
62+-- >>> import Control.Concurrent (threadDelay)
63+-- >>> :{
64+-- delay n = do
65+-- threadDelay (n * 1000000) -- sleep for n seconds
66+-- putStrLn (show n ++ " sec") -- print "n sec"
67+-- return n -- IO Int
68+-- :}
69+
70+{-# INLINE zipWithMK #-}
71+zipWithMK :: Monad m =>
72+ (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
73+zipWithMK f m1 m2 =
74+ D.toStreamK $ D.zipWithM f (D.fromStreamK m1) (D.fromStreamK m2)
75+
76+{-# INLINE zipWithK #-}
77+zipWithK :: Monad m
78+ => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
79+zipWithK f = zipWithMK (\a b -> return (f a b))
80+
81+------------------------------------------------------------------------------
82+-- Serially Zipping Streams
83+------------------------------------------------------------------------------
84+
85+-- | For 'ZipSerialM' streams:
86+--
87+-- @
88+-- (<>) = 'Streamly.Prelude.serial'
89+-- (<*>) = 'Streamly.Prelude.serial.zipWith' id
90+-- @
91+--
92+-- Applicative evaluates the streams being zipped serially:
93+--
94+-- >>> s1 = Stream.fromFoldable [1, 2]
95+-- >>> s2 = Stream.fromFoldable [3, 4]
96+-- >>> s3 = Stream.fromFoldable [5, 6]
97+-- >>> Stream.toList $ Stream.fromZipSerial $ (,,) <$> s1 <*> s2 <*> s3
98+-- [(1,3,5),(2,4,6)]
99+--
100+-- /Since: 0.2.0 ("Streamly")/
101+--
102+-- @since 0.8.0
103+newtype ZipSerialM m a = ZipSerialM {getZipSerialM :: Stream m a}
104+ deriving (Semigroup, Monoid)
105+
106+-- |
107+-- @since 0.1.0
108+{-# DEPRECATED ZipStream "Please use 'ZipSerialM' instead." #-}
109+type ZipStream = ZipSerialM
110+
111+-- | An IO stream whose applicative instance zips streams serially.
112+--
113+-- /Since: 0.2.0 ("Streamly")/
114+--
115+-- @since 0.8.0
116+type ZipSerial = ZipSerialM IO
117+
118+consMZip :: Monad m => m a -> ZipSerialM m a -> ZipSerialM m a
119+consMZip m (ZipSerialM r) = ZipSerialM $ K.consM m r
120+
121+LIST_INSTANCES(ZipSerialM)
122+NFDATA1_INSTANCE(ZipSerialM)
123+
124+instance Monad m => Functor (ZipSerialM m) where
125+ {-# INLINE fmap #-}
126+ fmap f (ZipSerialM m) = ZipSerialM $ getSerialT $ fmap f (SerialT m)
127+
128+instance Monad m => Applicative (ZipSerialM m) where
129+ pure = ZipSerialM . getSerialT . Serial.repeat
130+
131+ {-# INLINE (<*>) #-}
132+ ZipSerialM m1 <*> ZipSerialM m2 = ZipSerialM $ zipWithK id m1 m2
133+
134+FOLDABLE_INSTANCE(ZipSerialM)
135+TRAVERSABLE_INSTANCE(ZipSerialM)
src/Streamly/Internal/Data/Stream/Zip/Concurrent.hs+2−2
@@ -18,10 +18,10 @@ module Streamly.Internal.Data.Stream.Zip.Concurrent
1818 )
1919where
2020
21−import Streamly.Internal.Data.Stream (Stream)
21+import Streamly.Internal.Data.Stream.StreamD (Stream)
2222import Streamly.Internal.Data.Stream.Concurrent (MonadAsync, parZipWith)
2323
24−import qualified Streamly.Internal.Data.Stream as Stream (repeat)
24+import qualified Streamly.Internal.Data.Stream.StreamD as Stream (repeat)
2525import Prelude hiding (map, repeat, zipWith)
2626
2727-- $setup
src/Streamly/Internal/Data/Stream/ZipAsync.hs+1−1
@@ -31,7 +31,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D (fromStreamK)
3131import qualified Streamly.Internal.Data.Stream.Serial as Serial
3232import qualified Streamly.Internal.Data.Stream.SVar.Eliminate as SVar
3333import qualified Streamly.Internal.Data.Stream.SVar.Generate as SVar
34−import qualified Streamly.Internal.Data.Stream as Stream
34+import qualified Streamly.Internal.Data.Stream.Serial as Stream
3535 (fromStreamK, toStreamK)
3636import Streamly.Internal.Data.SVar
3737
src/Streamly/Internal/FileSystem/Event.hs+1−1
@@ -48,7 +48,7 @@ where
4848import Data.List.NonEmpty (NonEmpty)
4949import Data.Word (Word8)
5050import Streamly.Internal.Data.Array (Array)
51−import Streamly.Internal.Data.Stream (Stream)
51+import Streamly.Internal.Data.Stream.StreamD (Stream)
5252
5353#if defined(CABAL_OS_DARWIN)
5454import Streamly.Internal.FileSystem.Event.Darwin (Event)
src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2
@@ -171,7 +171,7 @@ import Foreign.Storable (peek, peekByteOff, sizeOf)
171171import GHC.IO.Device (IODeviceType(Stream))
172172import GHC.IO.FD (fdFD, mkFD)
173173import GHC.IO.Handle.FD (mkHandleFromFD)
174−import Streamly.Internal.Data.Stream (Stream)
174+import Streamly.Internal.Data.Stream.StreamD (Stream)
175175import Streamly.Internal.Data.Parser (Parser)
176176import Streamly.Internal.Data.Array.Type (Array(..), byteLength)
177177import System.Directory (doesDirectoryExist)
@@ -184,7 +184,7 @@ import qualified Streamly.Internal.Data.Array as A
184184import qualified Streamly.Data.Fold as FL
185185import qualified Streamly.Internal.Data.Parser as PR
186186 (takeEQ, fromEffect, fromFold)
187−import qualified Streamly.Internal.Data.Stream as S
187+import qualified Streamly.Internal.Data.Stream.StreamD as S
188188import qualified Streamly.Internal.FileSystem.Dir as Dir
189189import qualified Streamly.Internal.FileSystem.Handle as FH
190190import qualified Streamly.Internal.Unicode.Stream as U
src/Streamly/Internal/FileSystem/FD.hs+4−4
@@ -131,7 +131,7 @@ import qualified GHC.IO.Device as RawIO
131131import Streamly.Internal.Data.Array.Type
132132 (Array(..), byteLength, unsafeFreeze, asPtrUnsafe)
133133import Streamly.Internal.System.IO (defaultChunkSize)
134−import Streamly.Internal.Data.Stream.Type (Stream)
134+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
135135#if !defined(mingw32_HOST_OS)
136136{-
137137import Streamly.Internal.Data.Stream.IsStream.Type (toStreamD)
@@ -148,7 +148,7 @@ import qualified Streamly.Data.Fold as FL
148148import qualified Streamly.Internal.Data.Array.Mut as MArray
149149 (Array(..), newPinnedBytes, asPtrUnsafe)
150150import qualified Streamly.Internal.Data.Stream.Chunked as AS
151−import qualified Streamly.Internal.Data.Stream as S
151+import qualified Streamly.Internal.Data.Stream.StreamD as S
152152import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
153153 (Stream(..), Step(..))
154154import qualified Streamly.Internal.Data.Stream.StreamK.Type as K (mkStream)
@@ -296,7 +296,7 @@ _readArraysOfUpto size h = S.fromStreamK go
296296{-# INLINE_NORMAL readArraysOfUpto #-}
297297readArraysOfUpto :: (MonadIO m)
298298 => Int -> Handle -> Stream m (Array Word8)
299−readArraysOfUpto size h = S.fromStreamD (D.Stream step ())
299+readArraysOfUpto size h = D.Stream step ()
300300 where
301301 {-# INLINE_LATE step #-}
302302 step _ _ = do
@@ -357,7 +357,7 @@ read = AS.concat . readArrays
357357-- @since 0.7.0
358358{-# INLINE writeArrays #-}
359359writeArrays :: (MonadIO m, Unbox a) => Handle -> Stream m (Array a) -> m ()
360−writeArrays h = S.fold (FL.drainBy (liftIO . writeArray h))
360+writeArrays h = S.fold (FL.drainMapM (liftIO . writeArray h))
361361
362362-- | Write a stream of arrays to a handle after coalescing them in chunks of
363363-- specified size. The chunk size is only a maximum and the actual writes could
src/Streamly/Internal/Network/Inet/TCP.hs+9−4
@@ -110,6 +110,7 @@ import Streamly.Internal.Data.Array.Type (Array(..), writeNUnsafe)
110110import Streamly.Internal.Data.Fold.Type (Fold(..))
111111import Streamly.Data.Stream (Stream)
112112import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
113+import Streamly.Internal.Data.Unboxed (Unbox)
113114import Streamly.Internal.Data.Unfold.Type (Unfold(..))
114115import Streamly.Internal.Network.Socket (SockSpec(..), accept, acceptor)
115116import Streamly.Internal.System.IO (defaultChunkSize)
@@ -120,10 +121,10 @@ import qualified Network.Socket as Net
120121import qualified Streamly.Data.Array as A
121122import qualified Streamly.Data.Fold as FL
122123import qualified Streamly.Data.Unfold as UF
124+import qualified Streamly.Internal.Data.Array.Type as A
123125import qualified Streamly.Internal.Data.Unfold as UF (first, bracketIO)
124−import qualified Streamly.Internal.Data.Stream.Chunked as AS
125126import qualified Streamly.Internal.Data.Fold.Type as FL (Step(..), reduce)
126−import qualified Streamly.Internal.Data.Stream as S
127+import qualified Streamly.Internal.Data.Stream.StreamD as S
127128import qualified Streamly.Internal.Data.Stream.Exception.Lifted as S
128129import qualified Streamly.Internal.Network.Socket as ISK
129130
@@ -314,13 +315,17 @@ reader :: (MonadCatch m, MonadAsync m)
314315 => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Word8
315316reader = UF.many A.reader (usingConnection ISK.chunkReader)
316317
318+{-# INLINE concatChunks #-}
319+concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
320+concatChunks = S.unfoldMany A.reader
321+
317322-- | Read a stream from the supplied IPv4 host address and port number.
318323--
319324-- /Pre-release/
320325{-# INLINE read #-}
321326read :: (MonadCatch m, MonadAsync m)
322327 => (Word8, Word8, Word8, Word8) -> PortNumber -> Stream m Word8
323−read addr port = AS.concat $ withConnection addr port ISK.readChunks
328+read addr port = concatChunks $ withConnection addr port ISK.readChunks
324329
325330-------------------------------------------------------------------------------
326331-- Writing
@@ -380,7 +385,7 @@ putBytesWithBufferOf
380385 -> PortNumber
381386 -> Stream m Word8
382387 -> m ()
383−putBytesWithBufferOf n addr port m = putChunks addr port $ AS.arraysOf n m
388+putBytesWithBufferOf n addr port m = putChunks addr port $ A.arraysOf n m
384389
385390-- | Like 'write' but provides control over the write buffer. Output will
386391-- be written to the IO device as soon as we collect the specified number of
src/Streamly/Internal/Network/Socket.hs+10−7
@@ -89,7 +89,7 @@ import qualified Network.Socket as Net
8989import Streamly.Internal.Data.Array.Type (Array(..))
9090import Streamly.Internal.Data.Stream.Chunked (lpackArraysChunksOf)
9191import Streamly.Internal.Data.Fold (Fold)
92−import Streamly.Internal.Data.Stream.Type (Stream)
92+import Streamly.Internal.Data.Stream.StreamD.Type (Stream)
9393import Streamly.Internal.Data.Unfold.Type (Unfold(..))
9494-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
9595import Streamly.Internal.System.IO (defaultChunkSize)
@@ -97,11 +97,10 @@ import Streamly.Internal.System.IO (defaultChunkSize)
9797import qualified Streamly.Data.Array as A (reader, length, writeN)
9898import qualified Streamly.Data.Fold as FL
9999import qualified Streamly.Internal.Data.Array.Type as A
100− (unsafeFreeze, asPtrUnsafe, byteLength, writeNUnsafe)
100+ (unsafeFreeze, asPtrUnsafe, byteLength, writeNUnsafe, arraysOf)
101101import qualified Streamly.Internal.Data.Array.Mut as MArray
102102 (Array(..), newPinnedBytes, asPtrUnsafe)
103−import qualified Streamly.Internal.Data.Stream.Chunked as AS
104−import qualified Streamly.Internal.Data.Stream as S
103+import qualified Streamly.Internal.Data.Stream.StreamD as S
105104import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
106105 (Stream(..), Step(..))
107106import qualified Streamly.Data.Unfold as UF
@@ -345,7 +344,7 @@ _readChunksUptoWith f size h = S.fromStreamK go
345344{-# INLINE_NORMAL readChunksWith #-}
346345readChunksWith :: MonadIO m => Int -> Socket -> Stream m (Array Word8)
347346-- readChunksWith = _readChunksUptoWith readChunk
348−readChunksWith size h = S.fromStreamD (D.Stream step ())
347+readChunksWith size h = D.Stream step ()
349348 where
350349 {-# INLINE_LATE step #-}
351350 step _ _ = do
@@ -403,12 +402,16 @@ chunkReader = UF.first defaultChunkSize chunkReaderWith
403402-- Read File to Stream
404403-------------------------------------------------------------------------------
405404
405+{-# INLINE concatChunks #-}
406+concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
407+concatChunks = S.unfoldMany A.reader
408+
406409-- | Generate a byte stream from a socket using a buffer of the given size.
407410--
408411-- /Pre-release/
409412{-# INLINE readWith #-}
410413readWith :: MonadIO m => Int -> Socket -> Stream m Word8
411−readWith size = AS.concat . readChunksWith size
414+readWith size = concatChunks . readChunksWith size
412415
413416-- | Generate a byte stream from a socket.
414417--
@@ -492,7 +495,7 @@ writeChunksWithBufferOf = writeChunksWith
492495--
493496{-# INLINE putBytesWith #-}
494497putBytesWith :: MonadIO m => Int -> Socket -> Stream m Word8 -> m ()
495−putBytesWith n h m = putChunks h $ AS.arraysOf n m
498+putBytesWith n h m = putChunks h $ A.arraysOf n m
496499
497500-- | Write a byte stream to a socket. Accumulates the input in chunks of
498501-- specified number of bytes before writing.
src/Streamly/Internal/Unicode/Char.hs+2−3
@@ -35,10 +35,9 @@ where
3535
3636import Data.Char (isAsciiUpper, isAsciiLower, chr, ord)
3737import Unicode.Char (DecomposeMode(..))
38−import Streamly.Internal.Data.Stream (fromStreamD, toStreamD)
3938import Streamly.Internal.Data.Stream.StreamD (Stream(..), Step (..))
4039
41−import qualified Streamly.Internal.Data.Stream as Stream (Stream)
40+import qualified Streamly.Internal.Data.Stream.StreamD as Stream (Stream)
4241
4342import qualified Unicode.Char as Char
4443
@@ -324,4 +323,4 @@ normalize ::
324323 => NormalizationMode
325324 -> Stream.Stream m Char
326325 -> Stream.Stream m Char
327−normalize mode = fromStreamD . normalizeD mode . toStreamD
326+normalize = normalizeD
src/Streamly/Prelude.hs+6
@@ -905,6 +905,12 @@ module Streamly.Prelude
905905 -- * Type Synonyms
906906 , MonadAsync
907907
908+ -- * Converting from/to Stream/StreamK types
909+ , fromStream
910+ , toStream
911+ , fromStreamK
912+ , toStreamK
913+
908914 -- * Stream Type Adapters
909915 -- $adapters
910916 , IsStream ()
streamly.cabal+1
@@ -419,6 +419,7 @@ library
419419 , Streamly.Internal.Data.Stream.SVar.Eliminate
420420
421421 , Streamly.Internal.Data.Stream.Serial
422+ , Streamly.Internal.Data.Stream.Zip
422423 , Streamly.Internal.Data.Stream.Async
423424 , Streamly.Internal.Data.Stream.Parallel
424425 , Streamly.Internal.Data.Stream.Ahead
targets/Targets.hs+89−74
@@ -3,6 +3,10 @@ module Targets
33 )
44where
55
6+-- Special tags
7+-- noTest
8+-- noBench
9+-- testDevOnly
610targets :: [(String, [String])]
711targets =
812 [ -- Base streams
@@ -19,10 +23,14 @@ targets =
1923 ]
2024 )
2125
26+{-
27+ -- XXX Need devOnly flag support in BenchRunner
2228 , ("Data.Stream.ToStreamK",
2329 [ "noTest"
30+ , "devOnly"
2431 ]
2532 )
33+-}
2634
2735 -- Streams
2836 , ("Data.Stream",
@@ -33,6 +41,12 @@ targets =
3341 , "noTest"
3442 ]
3543 )
44+ , ("Data.Stream.StreamDK",
45+ [ "prelude_serial_grp"
46+ , "infinite_grp"
47+ , "noTest"
48+ ]
49+ )
3650 , ("Data.Stream.Concurrent",
3751 [ "prelude_concurrent_grp"
3852 , "infinite_grp"
@@ -66,78 +80,79 @@ targets =
6680 , "infinite_grp"
6781 ]
6882 )
69− , ("Prelude.Serial",
70− [ "prelude_serial_grp"
71− , "infinite_grp"
72− , "serial_wserial_cmp"
73− , "noBench"
74− ]
75− )
76− , ("Prelude.Top",
77− [ "prelude_serial_grp"
78− , "infinite_grp"
79− , "noBench"
80− ]
81− )
82− , ("Prelude.WSerial",
83− [ "prelude_serial_grp"
84− , "infinite_grp"
85− , "serial_wserial_cmp"
86− ]
87− )
88− , ("Prelude.Merge",
89− [ "prelude_serial_grp"
90− , "infinite_grp"
91− , "noTest"
92− ]
93− )
94− , ("Prelude.ZipSerial",
95− [ "prelude_serial_grp"
96− , "infinite_grp"
97− ]
98− )
99− , ("Prelude.Async",
100− [ "prelude_concurrent_grp"
101− , "infinite_grp"
102− , "concurrent_cmp"
103− , "serial_async_cmp"
104− ]
105− )
106− , ("Prelude.WAsync",
107− [ "prelude_concurrent_grp"
108− , "infinite_grp"
109− , "concurrent_cmp"
110− ]
111− )
112− , ("Prelude.Ahead",
113− [ "prelude_concurrent_grp"
114− , "infinite_grp"
115− , "concurrent_cmp"
116− ]
117− )
118− , ("Prelude.Parallel",
119− [ "prelude_concurrent_grp"
120− , "infinite_grp"
121− , "concurrent_cmp"
122− ]
123− )
124− , ("Prelude.ZipAsync",
125− [ "prelude_concurrent_grp"
126− , "infinite_grp"
127− ]
128− )
129− , ("Prelude.Concurrent", [ "prelude_other_grp" ])
130− , ("Prelude.Rate",
131− [ "prelude_other_grp"
132− , "infinite_grp"
133− , "testDevOnly"
134− ]
135− )
136− , ("Prelude.Adaptive",
137− [ "prelude_other_grp"
138− , "noTest"
139− ]
140− )
83+ -- Enabled only when use-prelude flag is set
84+ -- , ("Prelude.Serial",
85+ -- [ "prelude_serial_grp"
86+ -- , "infinite_grp"
87+ -- , "serial_wserial_cmp"
88+ -- , "noBench"
89+ -- ]
90+ -- )
91+ -- , ("Prelude.Top",
92+ -- [ "prelude_serial_grp"
93+ -- , "infinite_grp"
94+ -- , "noBench"
95+ -- ]
96+ -- )
97+ -- , ("Prelude.WSerial",
98+ -- [ "prelude_serial_grp"
99+ -- , "infinite_grp"
100+ -- , "serial_wserial_cmp"
101+ -- ]
102+ -- )
103+ -- , ("Prelude.Merge",
104+ -- [ "prelude_serial_grp"
105+ -- , "infinite_grp"
106+ -- , "noTest"
107+ -- ]
108+ -- )
109+ -- , ("Prelude.ZipSerial",
110+ -- [ "prelude_serial_grp"
111+ -- , "infinite_grp"
112+ -- ]
113+ -- )
114+ -- , ("Prelude.Async",
115+ -- [ "prelude_concurrent_grp"
116+ -- , "infinite_grp"
117+ -- , "concurrent_cmp"
118+ -- , "serial_async_cmp"
119+ -- ]
120+ -- )
121+ -- , ("Prelude.WAsync",
122+ -- [ "prelude_concurrent_grp"
123+ -- , "infinite_grp"
124+ -- , "concurrent_cmp"
125+ -- ]
126+ -- )
127+ -- , ("Prelude.Ahead",
128+ -- [ "prelude_concurrent_grp"
129+ -- , "infinite_grp"
130+ -- , "concurrent_cmp"
131+ -- ]
132+ -- )
133+ -- , ("Prelude.Parallel",
134+ -- [ "prelude_concurrent_grp"
135+ -- , "infinite_grp"
136+ -- , "concurrent_cmp"
137+ -- ]
138+ -- )
139+ -- , ("Prelude.ZipAsync",
140+ -- [ "prelude_concurrent_grp"
141+ -- , "infinite_grp"
142+ -- ]
143+ -- )
144+ -- , ("Prelude.Concurrent", [ "prelude_other_grp" ])
145+ -- , ("Prelude.Rate",
146+ -- [ "prelude_other_grp"
147+ -- , "infinite_grp"
148+ -- , "testDevOnly"
149+ -- ]
150+ -- )
151+ -- , ("Prelude.Adaptive",
152+ -- [ "prelude_other_grp"
153+ -- , "noTest"
154+ -- ]
155+ -- )
141156
142157 -- Arrays
143158 , ("Data.Array.Generic",
@@ -185,8 +200,8 @@ targets =
185200 , ("Unicode.Char", ["testDevOnly"])
186201
187202 -- test only, no benchmarks
188− , ("Prelude", ["prelude_other_grp", "noBench"])
189− , ("Prelude.Fold", ["prelude_other_grp", "noBench"])
203+ -- , ("Prelude", ["prelude_other_grp", "noBench"])
204+ -- , ("Prelude.Fold", ["prelude_other_grp", "noBench"])
190205 , ("FileSystem.Event", ["noBench"])
191206 , ("Network.Socket", ["noBench"])
192207 , ("Network.Inet.TCP", ["noBench"])