on master help sign in
Make some doc updates to Stream and StreamK modules
b7a2e95Harendra Kumar
core/src/Streamly/Data/Stream.hs+69−62
@@ -8,15 +8,20 @@
8-- Stability : released
8-- Stability : released
9-- Portability : GHC
9-- Portability : GHC
10--
10--
11−-- Fast, composable stream producers with ability to terminate, supporting
11+-- Streams represented as state machines, that fuse together when composed
12−-- stream fusion.
12+-- statically, eliminating function calls or intermediate constructor
13+-- allocations - generating tight, efficient loops. Suitable for high
14+-- performance looping operations.
15+--
16+-- If you need to call these operations recursively in a loop (i.e. composed
17+-- dynamically) then it is recommended to use the continuation passing style
18+-- (CPS) stream operations from the "Streamly.Data.StreamK" module. 'Stream'
19+-- and 'StreamK' types are interconvertible. See more details in the
20+-- documentation below regarding 'Stream' vs 'StreamK'.
13--
21--
14-- Please refer to "Streamly.Internal.Data.Stream" for more functions that have
22-- Please refer to "Streamly.Internal.Data.Stream" for more functions that have
15-- not yet been released.
23-- not yet been released.
16--
24--
17−-- For continuation passing style (CPS) stream type, please refer to
18−-- the "Streamly.Data.StreamK" module.
19−--
20-- Checkout the <https://github.com/composewell/streamly-examples>
25-- Checkout the <https://github.com/composewell/streamly-examples>
21-- repository for many more real world examples of stream programming.
26-- repository for many more real world examples of stream programming.
22
27
@@ -49,11 +54,12 @@ module Streamly.Data.Stream
49 -- >>> fromFoldableM = Stream.sequence . fromFoldable
54 -- >>> fromFoldableM = Stream.sequence . fromFoldable
50
55
51 -- ** Primitives
56 -- ** Primitives
52− -- | A fused 'Stream' is never constructed using 'cons', we always convert
57+ -- | A fused 'Stream' is never constructed using these primitives, they are
53− -- other type of containers like list into streams, or generate it using
58+ -- typically generated by converting containers like list into streams, or
54− -- custom functions provided in this module. The 'cons' primitive has a
59+ -- generated using custom functions provided in this module. The 'cons'
55− -- rare use in fusing a small number of elements. On the other hand, it is
60+ -- primitive in this module has a rare use in fusing a small number of
61+ -- elements. On the other hand, it is common to construct 'StreamK' stream
56− -- common to construct 'StreamK' stream using the 'StreamK.cons' primitive.
62+ -- using the StreamK.'StreamK.cons' primitive.
57 , nil
63 , nil
58 , nilM
64 , nilM
59 , cons
65 , cons
@@ -358,32 +364,31 @@ module Streamly.Data.Stream
358 -- , elemIndices
364 -- , elemIndices
359
365
360 -- * Combining Two Streams
366 -- * Combining Two Streams
367+ -- | Note that these operations are suitable for statically fusing a few
368+ -- streams, they have a quadratic O(n^2) time complexity wrt to the number
369+ -- of streams. If you want to compose many streams dynamically using binary
370+ -- combining operations see the corresponding operations in
371+ -- "Streamly.Data.StreamK".
372+ --
373+ -- When fusing more than two streams it is more efficient if the binary
374+ -- operations are composed as a balanced tree rather than a right
375+ -- associative or left associative one e.g.:
376+ --
377+ -- >>> s1 = Stream.fromList [1,2] `Stream.append` Stream.fromList [3,4]
378+ -- >>> s2 = Stream.fromList [4,5] `Stream.append` Stream.fromList [6,7]
379+ -- >>> s = s1 `Stream.append` s2
380+
361 -- ** Appending
381 -- ** Appending
362 , append
382 , append
363
383
364 -- ** Interleaving
384 -- ** Interleaving
365− -- | When interleaving more than two streams you may want to interleave
366− -- them pairwise creating a balanced binary merge tree.
367 , interleave
385 , interleave
368
386
369 -- ** Merging
387 -- ** Merging
370− -- | When merging more than two streams you may want to merging them
371− -- pairwise creating a balanced binary merge tree.
372− --
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
388 , mergeBy
378 , mergeByM
389 , mergeByM
379
390
380 -- ** Zipping
391 -- ** Zipping
381− -- | When zipping more than two streams you may want to zip them
382− -- pairwise creating a balanced binary tree.
383− --
384− -- Zipping of @n@ streams can be performed by combining the streams pair
385− -- wise using 'mergeMapWith' with O(n * log n) time complexity. If used
386− -- with 'concatMapWith' it will have O(n^2) performance.
387 , zipWith
392 , zipWith
388 , zipWithM
393 , zipWithM
389 -- , ZipStream (..)
394 -- , ZipStream (..)
@@ -573,11 +578,11 @@ import qualified Streamly.Internal.Data.Stream.Chunked as ArrayStream
573--
578--
574-- == Stream Fusion
579-- == Stream Fusion
575--
580--
576−-- The fused 'Stream' type employs stream fusion for C-like performance when
581+-- The fused 'Stream' type in this module employs stream fusion for C-like
577−-- looping over data. It represents a stream source or transformation by
582+-- performance when looping over data. It represents the stream as a state
578−-- defining a state machine with explicit state, and a step function working on
583+-- machine using an explicit state, and a step function working on the state. A
579−-- the state. A typical stream operation consumes elements from the previous
584+-- typical stream operation consumes elements from the previous state machine
580−-- state machine in the pipeline, transforms them and yields new values for the
585+-- in a stream pipeline, transforms the elements and yields new values for the
581-- next stage to consume. The stream operations are modular and represent a
586-- next stage to consume. The stream operations are modular and represent a
582-- single task, they have no knowledge of previous or next operation on the
587-- single task, they have no knowledge of previous or next operation on the
583-- elements.
588-- elements.
@@ -586,7 +591,7 @@ import qualified Streamly.Internal.Data.Stream.Chunked as ArrayStream
586-- transformation operations and a stream consumer. All these operations taken
591-- transformation operations and a stream consumer. All these operations taken
587-- together form a closed loop processing the stream elements. Elements are
592-- together form a closed loop processing the stream elements. Elements are
588-- transferred between stages using a boxed data constructor. However, all the
593-- transferred between stages using a boxed data constructor. However, all the
589−-- stages of the pipeline are fused together by GHC, eliminating the
594+-- stages of the pipeline are fused together by GHC, eliminating the boxing of
590-- intermediate constructors, and thus forming a tight C like loop without any
595-- intermediate constructors, and thus forming a tight C like loop without any
591-- boxed data being used in the loop.
596-- boxed data being used in the loop.
592--
597--
@@ -601,43 +606,45 @@ import qualified Streamly.Internal.Data.Stream.Chunked as ArrayStream
601--
606--
602-- == Stream vs StreamK
607-- == Stream vs StreamK
603--
608--
604−-- The fused stream model avoids constructor allocations or function call
609+-- The fused stream model avoids constructor allocations and function call
605−-- overheads. However, the stream is represented as a state machine and to
610+-- overheads. However, the stream is represented as a state machine, and to
606−-- generate elements it has to navigate the decision tree of the state machine.
611+-- generate stream elements it has to navigate the decision tree of the state
607−-- Moreover, the state machine is cranked for each element in the stream. This
612+-- machine. Moreover, the state machine is cranked for each element in the
608−-- performs extremely well when the number of states are limited. The state
613+-- stream. This performs extremely well when the number of states are limited.
609−-- machine starts getting expensive as the number of states increase. For
614+-- The state machine starts getting expensive as the number of states increase.
610−-- example, generating a million element stream from a list requires a single
615+-- For example, generating a stream from a list requires a single state and is
611−-- state and is very efficient. However, using fused 'cons' to generate a
616+-- very efficient, even if it has millions of elements. However, using 'cons'
612−-- million element stream would be a disaster.
617+-- to construct a million element stream would be a disaster.
613--
618--
614-- A typical worst case scenario for fused stream model is a large number of
619-- A typical worst case scenario for fused stream model is a large number of
615-- `cons` or `append` operations. A few static `cons` or `append` operations
620-- `cons` or `append` operations. A few static `cons` or `append` operations
616−-- are very fast and much faster than a CPS style stream. However, if we
621+-- are very fast and much faster than a CPS style stream because CPS involves a
622+-- function call for each element whereas fused stream involves a few
623+-- conditional branches in the state machine. However, constructing a large
617−-- construct a large stream using `cons` it introduces as many states in the
624+-- stream using `cons` introduces as many states in the state machine as the
618−-- state machine as the number of elements. If we compose the `cons` as a
625+-- number of elements. If we compose `cons` as a balanced binary tree it will
619−-- binary tree it will take @n * log n@ time to navigate the tree, and @n * n@
626+-- take @n * log n@ time to navigate the tree, and @n * n@ if it is a right
620−-- if it is a right associative composition.
627+-- associative composition.
621−--
628+--
622−-- For quadratic cases of fused stream, after a certain threshold the CPS
623−-- stream would perform much better and exhibit linear performance behavior.
624-- Operations like 'cons' or 'append'; are typically recursively called to
629-- Operations like 'cons' or 'append'; are typically recursively called to
625-- construct a lazy infinite stream. For such use cases the CPS style 'StreamK'
630-- construct a lazy infinite stream. For such use cases the CPS style 'StreamK'
626−-- type is provided. CPS streams do not have a state machine that needs to be
631+-- should be used. CPS streams do not have a state machine that needs to be
627-- cranked for each element, past state has no effect on the future element
632-- cranked for each element, past state has no effect on the future element
628−-- processing. However, it incurs a function call overhead for each operation
633+-- processing. However, CPS incurs a function call overhead for each element
629−-- for each element, which could be very large overhead compared to fused state
634+-- processed, the overhead could be large compared to a fused state machine
630−-- machines even if it has many states and cranks it for each element. But in
635+-- even if it has many states. However, because of its linear performance
631−-- some cases scales tip in favor of the CPS stream. In those cases even though
636+-- characterstics, after a certain threshold of stream compositions the CPS
632−-- CPS has a large constant overhead, it has a linear performance rather than
637+-- stream would perform much better than the quadratic fused stream operations.
633−-- quadratic.
634−--
638+--
635−-- As a general guideline, if you have to use 'cons' or 'append' or operations
639+-- As a general guideline, you need to use 'StreamK' when you have to use
636−-- of similar nature, at a large scale, then 'StreamK' should be used. When you
640+-- 'cons', 'append' or other operations having quadratic complexity at a large
637−-- need to compose the stream dynamically or recursively, then 'StreamK' should
641+-- scale. Typically, in such cases you need to compose the stream recursively,
642+-- by calling an operation in a loop. The decision to compose the stream is
643+-- taken at run time rather than statically at compile time.
644+--
638−-- be used. Typically you would use a dynamically generated 'StreamK' with
645+-- Typically you would compose a 'StreamK' of chunks of data so that the
639−-- chunks of data which can then be processed by statically fused stream
646+-- StreamK overhead is not high, and then process the chunks using 'Stream' by
640−-- pipeline operations.
647+-- using statically fused stream pipeline operations on the chunks.
641--
648--
642-- 'Stream' and 'StreamK' types can be interconverted. See
649-- 'Stream' and 'StreamK' types can be interconverted. See
643-- "Streamly.Data.StreamK" module for conversion operations.
650-- "Streamly.Data.StreamK" module for conversion operations.
core/src/Streamly/Data/StreamK.hs+35−18
@@ -8,18 +8,30 @@
8-- Stability : released
8-- Stability : released
9-- Portability : GHC
9-- Portability : GHC
10--
10--
11−-- Streams using Continuation Passing Style (CPS). See the @Stream vs StreamK@
11+-- Streams represented as chains of functions calls using Continuation Passing
12+-- Style (CPS), suitable for dynamically composing potentially large number of
13+-- streams.
14+--
15+-- Unlike the statically fused operations in "Streamly.Data.Stream", StreamK
16+-- operations are less efficient, involving a function call overhead for each
17+-- element, but they exhibit linear O(n) time complexity wrt to the number of
18+-- stream compositions. Therefore, they are suitable for dynamically composing
19+-- streams e.g. appending potentially infinite streams in recursive loops.
20+-- While fused streams can be used to efficiently process elements as small as
21+-- a single byte, CPS streams are typically used on bigger chunks of data to
22+-- avoid the larger overhead per element. For more details See the @Stream vs
12−-- section in the "Streamly.Data.Stream" module to know when to use this
23+-- StreamK@ section in the "Streamly.Data.Stream" module.
13−-- module.
24+--
14−--
25+-- In addition to the combinators in this module, you can use operations from
26+-- "Streamly.Data.Stream" for StreamK as well by converting StreamK to Stream
27+-- ('toStream'), and vice-versa ('fromStream'). Please refer to
15−-- Please refer to "Streamly.Internal.Data.StreamK" for more functions
28+-- "Streamly.Internal.Data.StreamK" for more functions that have not yet been
16−-- that have not yet been released.
29+-- released.
17−--
30+--
18−-- Some useful idioms:
31+-- For documentation see the corresponding combinators in
19−--
32+-- "Streamly.Data.Stream". Documentation has been omitted in this module unless
20−-- >>> concatFoldableWith f = Prelude.foldr f StreamK.nil
33+-- there is a difference worth mentioning or if the combinator does not exist
21−-- >>> concatMapFoldableWith f g = Prelude.foldr (f . g) StreamK.nil
34+-- in "Streamly.Data.Stream".
22−-- >>> concatForFoldableWith f xs g = Prelude.foldr (f . g) StreamK.nil xs
23
35
24-- Notes:
36-- Notes:
25--
37--
@@ -93,6 +105,11 @@ module Streamly.Data.StreamK
93 , take
105 , take
94
106
95 -- * Combining Two Streams
107 -- * Combining Two Streams
108+ -- | Unlike the operations in "Streamly.Data.Stream", these operations can
109+ -- be used to dynamically compose large number of streams e.g. using the
110+ -- 'concatMapWith' and 'mergeMapWith' operations. They have a linear O(n)
111+ -- time complexity wrt to the number of streams being composed.
112+
96 -- ** Appending
113 -- ** Appending
97 , append
114 , append
98
115
@@ -116,6 +133,12 @@ module Streamly.Data.StreamK
116 -- , CrossStreamK (..)
133 -- , CrossStreamK (..)
117
134
118 -- * Stream of streams
135 -- * Stream of streams
136+ -- | Some useful idioms:
137+ --
138+ -- >>> concatFoldableWith f = Prelude.foldr f StreamK.nil
139+ -- >>> concatMapFoldableWith f g = Prelude.foldr (f . g) StreamK.nil
140+ -- >>> concatForFoldableWith f xs g = Prelude.foldr (f . g) StreamK.nil xs
141+ --
119 , concatEffect
142 , concatEffect
120 -- , concatMap
143 -- , concatMap
121 , concatMapWith
144 , concatMapWith
@@ -168,9 +191,3 @@ import Prelude hiding (reverse, zipWith, mapM, dropWhile, take)
168-- 1
191-- 1
169-- 2
192-- 2
170-- [1,2]
193-- [1,2]
171−--
172−-- == Exception Handling
173−--
174−-- There are no native exception handling operations in the StreamK module,
175−-- please convert to 'Stream' type and use exception handling operations from
176−-- "Streamly.Data.Stream".
core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+22−10
@@ -156,16 +156,6 @@ nil = Stream (\_ _ -> pure Stop) ()
156
156
157-- XXX implement in terms of consM?
157-- XXX implement in terms of consM?
158-- cons x = consM (return x)
158-- cons x = consM (return x)
159−
160−-- | WARNING! You almost always need StreamK.'Streamly.Data.StreamK.cons'
161−-- instead.
162−--
163−-- Fuse a pure value at the head of an existing stream::
164−--
165−-- >>> s = 1 `Stream.cons` Stream.fromList [2,3]
166−-- >>> Stream.toList s
167−-- [1,2,3]
168−--
169-- From an implementation perspective, StreamK.cons translates into a
159-- From an implementation perspective, StreamK.cons translates into a
170-- functional call whereas fused cons translates into a conditional branch
160-- functional call whereas fused cons translates into a conditional branch
171-- (jump). However, the overhead of the function call in StreamK.cons only
161-- (jump). However, the overhead of the function call in StreamK.cons only
@@ -173,6 +163,28 @@ nil = Stream (\_ _ -> pure Stop) ()
173-- incurred for each subsequent element in the stream. As a result,
163-- incurred for each subsequent element in the stream. As a result,
174-- StreamK.cons has a time complexity of O(n), while fused cons has a time
164-- StreamK.cons has a time complexity of O(n), while fused cons has a time
175-- complexity of O(n^2), where @n@ represents the number of 'cons' used.
165-- complexity of O(n^2), where @n@ represents the number of 'cons' used.
166+
167+-- When composing a few elements together statically, a balanced tree composed
168+-- using 'cons' and 'append' is more efficient than a right associated one
169+-- composed using 'cons':
170+--
171+-- >>> s1 = 1 `Stream.cons` 2 `Stream.cons` Stream.nil
172+-- >>> s2 = 2 `Stream.cons` 3 `Stream.cons` Stream.nil
173+-- >>> s = s1 `Stream.append` s2
174+--
175+-- However, generating a stream using a case statement or indexing into a
176+-- static literal array would be the best. Check if the case statement
177+-- translates to a look up table or a binary search.
178+
179+-- | WARNING! O(n^2) time complexity wrt number of elements. Use the O(n)
180+-- complexity StreamK.'Streamly.Data.StreamK.cons' unless you want to
181+-- statically fuse just a few elements.
182+--
183+-- Fuse a pure value at the head of an existing stream::
184+--
185+-- >>> s = 1 `Stream.cons` Stream.fromList [2,3]
186+-- >>> Stream.toList s
187+-- [1,2,3]
176--
188--
177-- Definition:
189-- Definition:
178--
190--
core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+24−21
@@ -211,14 +211,6 @@ import Prelude hiding (concatMap, mapM, zipWith)
211
211
212data AppendState s1 s2 = AppendFirst s1 | AppendSecond s2
212data AppendState s1 s2 = AppendFirst s1 | AppendSecond s2
213
213
214−-- | Fuses two streams sequentially, yielding all elements from the first
215−-- stream, and then all elements from the second stream.
216−--
217−-- >>> s1 = Stream.fromList [1,2]
218−-- >>> s2 = Stream.fromList [3,4]
219−-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
220−-- [1,2,3,4]
221−--
222-- From an implementation perspective, StreamK.'Streamly.Data.StreamK.append'
214-- From an implementation perspective, StreamK.'Streamly.Data.StreamK.append'
223-- translates into a function call whereas Stream.'append' translates into a
215-- translates into a function call whereas Stream.'append' translates into a
224-- conditional branch (jump). However, the overhead of the function call in
216-- conditional branch (jump). However, the overhead of the function call in
@@ -227,10 +219,18 @@ data AppendState s1 s2 = AppendFirst s1 | AppendSecond s2
227-- result, StreamK.append has a linear time complexity of O(n), while fused
219-- result, StreamK.append has a linear time complexity of O(n), while fused
228-- append has a quadratic time complexity of O(n^2), where @n@ represents the
220-- append has a quadratic time complexity of O(n^2), where @n@ represents the
229-- number of 'append's used.
221-- number of 'append's used.
222+
223+-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for
224+-- statically fusing a small number of streams. Use the O(n) complexity
225+-- StreamK.'Streamly.Data.StreamK.append' otherwise.
230--
226--
231−-- This function should only be used to statically fuse a stream with another
227+-- Fuses two streams sequentially, yielding all elements from the first
232−-- stream. Do not use it recursively or where it cannot be inlined. Use
228+-- stream, and then all elements from the second stream.
233−-- StreamK.'Streamly.Data.StreamK.append' for those cases.
229+--
230+-- >>> s1 = Stream.fromList [1,2]
231+-- >>> s2 = Stream.fromList [3,4]
232+-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
233+-- [1,2,3,4]
234--
234--
235{-# INLINE_NORMAL append #-}
235{-# INLINE_NORMAL append #-}
236append :: Monad m => Stream m a -> Stream m a -> Stream m a
236append :: Monad m => Stream m a -> Stream m a -> Stream m a
@@ -261,14 +261,13 @@ append (Stream step1 state1) (Stream step2 state2) =
261data InterleaveState s1 s2 = InterleaveFirst s1 s2 | InterleaveSecond s1 s2
261data InterleaveState s1 s2 = InterleaveFirst s1 s2 | InterleaveSecond s1 s2
262 | InterleaveSecondOnly s2 | InterleaveFirstOnly s1
262 | InterleaveSecondOnly s2 | InterleaveFirstOnly s1
263
263
264−-- | Interleaves two streams, yielding one element from each stream
264+-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for
265−-- alternately. When one stream stops the rest of the other stream is used in
265+-- statically fusing a small number of streams. Use the O(n) complexity
266−-- the output stream.
266+-- StreamK.'Streamly.Data.StreamK.interleave' otherwise.
267--
267--
268−-- When joining many streams in a left associative manner earlier streams will
268+-- Interleaves two streams, yielding one element from each stream alternately.
269−-- get exponential priority than the ones joining later. Because of exponential
269+-- When one stream stops the rest of the other stream is used in the output
270−-- weighting it can be used with 'concatMapWith' even on a large number of
270+-- stream.
271−-- streams.
272--
271--
273{-# INLINE_NORMAL interleave #-}
272{-# INLINE_NORMAL interleave #-}
274interleave :: Monad m => Stream m a -> Stream m a -> Stream m a
273interleave :: Monad m => Stream m a -> Stream m a -> Stream m a
@@ -507,7 +506,7 @@ roundRobin (Stream step1 state1) (Stream step2 state2) =
507
506
508-- | Like 'mergeBy' but with a monadic comparison function.
507-- | Like 'mergeBy' but with a monadic comparison function.
509--
508--
510−-- Merge two streams randomly:
509+-- Example, to merge two streams randomly:
511--
510--
512-- @
511-- @
513-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
512-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
@@ -515,7 +514,7 @@ roundRobin (Stream step1 state1) (Stream step2 state2) =
515-- [2,1,2,2,2,1,1,1]
514-- [2,1,2,2,2,1,1,1]
516-- @
515-- @
517--
516--
518−-- Merge two streams in a proportion of 2:1:
517+-- Example, merge two streams in a proportion of 2:1:
519--
518--
520-- >>> :{
519-- >>> :{
521-- do
520-- do
@@ -573,7 +572,11 @@ mergeByM cmp (Stream stepa ta) (Stream stepb tb) =
573
572
574 step _ (Nothing, Nothing, Nothing, Nothing) = return Stop
573 step _ (Nothing, Nothing, Nothing, Nothing) = return Stop
575
574
575+-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for
576+-- statically fusing a small number of streams. Use the O(n) complexity
577+-- StreamK.'Streamly.Data.StreamK.mergeBy' otherwise.
578+--
576−-- | Merge two streams using a comparison function. The head elements of both
579+-- Merge two streams using a comparison function. The head elements of both
577-- the streams are compared and the smaller of the two elements is emitted, if
580-- the streams are compared and the smaller of the two elements is emitted, if
578-- both elements are equal then the element from the first stream is used
581-- both elements are equal then the element from the first stream is used
579-- first.
582-- first.
core/src/Streamly/Internal/Data/Stream/StreamD/Type.hs+5−1
@@ -1105,7 +1105,11 @@ zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)
1105{-# RULES "zipWithM xs xs"
1105{-# RULES "zipWithM xs xs"
1106 forall f xs. zipWithM @Identity f xs xs = mapM (\x -> f x x) xs #-}
1106 forall f xs. zipWithM @Identity f xs xs = mapM (\x -> f x x) xs #-}
1107
1107
1108+-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for
1109+-- statically fusing a small number of streams. Use the O(n) complexity
1110+-- StreamK.'Streamly.Data.StreamK.zipWith' otherwise.
1111+--
1108−-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
1112+-- Stream @a@ is evaluated first, followed by stream @b@, the resulting
1109-- elements @a@ and @b@ are then zipped using the supplied zip function and the
1113-- elements @a@ and @b@ are then zipped using the supplied zip function and the
1110-- result @c@ is yielded to the consumer.
1114-- result @c@ is yielded to the consumer.
1111--
1115--
core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs+7−24
@@ -305,7 +305,7 @@ nil = mkStream $ \_ _ _ stp -> stp
305nilM :: Applicative m => m b -> StreamK m a
305nilM :: Applicative m => m b -> StreamK m a
306nilM m = mkStream $ \_ _ _ stp -> m *> stp
306nilM m = mkStream $ \_ _ _ stp -> m *> stp
307
307
308−-- | Create a singleton stream from a pure value.
308+-- Create a singleton stream from a pure value.
309--
309--
310-- >>> fromPure a = a `StreamK.cons` StreamK.nil
310-- >>> fromPure a = a `StreamK.cons` StreamK.nil
311-- >>> fromPure = pure
311-- >>> fromPure = pure
@@ -315,7 +315,7 @@ nilM m = mkStream $ \_ _ _ stp -> m *> stp
315fromPure :: a -> StreamK m a
315fromPure :: a -> StreamK m a
316fromPure a = mkStream $ \_ _ single _ -> single a
316fromPure a = mkStream $ \_ _ single _ -> single a
317
317
318−-- | Create a singleton stream from a monadic action.
318+-- Create a singleton stream from a monadic action.
319--
319--
320-- >>> fromEffect m = m `StreamK.consM` StreamK.nil
320-- >>> fromEffect m = m `StreamK.consM` StreamK.nil
321--
321--
@@ -825,7 +825,7 @@ foldl' step begin = foldlx' step begin id
825-- an optimization opportunity that we can exploit.
825-- an optimization opportunity that we can exploit.
826-- drain = foldrM (\_ xs -> return () >> xs) (return ())
826-- drain = foldrM (\_ xs -> return () >> xs) (return ())
827
827
828−-- |
828+--
829-- > drain = foldl' (\_ _ -> ()) ()
829-- > drain = foldl' (\_ _ -> ()) ()
830-- > drain = mapM_ (\_ -> return ())
830-- > drain = mapM_ (\_ -> return ())
831{-# INLINE drain #-}
831{-# INLINE drain #-}
@@ -856,18 +856,6 @@ null m =
856
856
857infixr 6 `append`
857infixr 6 `append`
858
858
859−-- | Appends two streams sequentially, yielding all elements from the first
860−-- stream, and then all elements from the second stream.
861−--
862−-- >>> s1 = StreamK.fromStream $ Stream.fromList [1,2]
863−-- >>> s2 = StreamK.fromStream $ Stream.fromList [3,4]
864−-- >>> Stream.fold Fold.toList $ StreamK.toStream $ s1 `StreamK.append` s2
865−-- [1,2,3,4]
866−--
867−-- This has O(n) append performance where @n@ is the number of streams. It can
868−-- be used to efficiently fold an infinite lazy container of streams using
869−-- 'concatMapWith' et. al.
870−--
871{-# INLINE append #-}
859{-# INLINE append #-}
872append :: StreamK m a -> StreamK m a -> StreamK m a
860append :: StreamK m a -> StreamK m a -> StreamK m a
873-- XXX This doubles the time of toNullAp benchmark, may not be fusing properly
861-- XXX This doubles the time of toNullAp benchmark, may not be fusing properly
@@ -1633,14 +1621,10 @@ infixr 6 `interleave`
1633-- variants of positional interleaving, e.g. run first stream for m seconds and
1621-- variants of positional interleaving, e.g. run first stream for m seconds and
1634-- run the second stream for n seconds.
1622-- run the second stream for n seconds.
1635
1623
1636−-- | Interleaves two streams, yielding one element from each stream
1637−-- alternately. When one stream stops the rest of the other stream is used in
1638−-- the output stream.
1639−--
1640−-- When joining many streams in a left associative manner earlier streams will
1624+-- | Note: When joining many streams in a left associative manner earlier
1641−-- get exponential priority than the ones joining later. Because of exponential
1625+-- streams will get exponential priority than the ones joining later. Because
1626+-- of exponentially high weighting of left streams it can be used with
1642−-- weighting it can be used with 'concatMapWith' even on a large number of
1627+-- 'concatMapWith' even on a large number of streams.
1643−-- streams.
1644--
1628--
1645{-# INLINE interleave #-}
1629{-# INLINE interleave #-}
1646interleave :: StreamK m a -> StreamK m a -> StreamK m a
1630interleave :: StreamK m a -> StreamK m a -> StreamK m a
@@ -1965,7 +1949,6 @@ before action stream =
1965 mkStream $ \st yld sng stp ->
1949 mkStream $ \st yld sng stp ->
1966 action >> foldStreamShared st yld sng stp stream
1950 action >> foldStreamShared st yld sng stp stream
1967
1951
1968−-- | concat . fromEffect
1969{-# INLINE concatEffect #-}
1952{-# INLINE concatEffect #-}
1970concatEffect :: Monad m => m (StreamK m a) -> StreamK m a
1953concatEffect :: Monad m => m (StreamK m a) -> StreamK m a
1971concatEffect action =
1954concatEffect action =
core/src/Streamly/Internal/Data/StreamK.hs+25−10
@@ -233,10 +233,21 @@ import Streamly.Internal.Data.Parser (ParseError(..))
233
233
234#include "DocTestDataStreamK.hs"
234#include "DocTestDataStreamK.hs"
235
235
236+-- | Convert a fused 'Stream' to 'StreamK'.
237+--
238+-- For example:
239+--
240+-- >>> s1 = StreamK.fromStream $ Stream.fromList [1,2]
241+-- >>> s2 = StreamK.fromStream $ Stream.fromList [3,4]
242+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ s1 `StreamK.append` s2
243+-- [1,2,3,4]
244+--
236{-# INLINE fromStream #-}
245{-# INLINE fromStream #-}
237fromStream :: Monad m => Stream.Stream m a -> StreamK m a
246fromStream :: Monad m => Stream.Stream m a -> StreamK m a
238fromStream = Stream.toStreamK
247fromStream = Stream.toStreamK
239
248
249+-- | Convert a 'StreamK' to a fused 'Stream'.
250+--
240{-# INLINE toStream #-}
251{-# INLINE toStream #-}
241toStream :: Applicative m => StreamK m a -> Stream.Stream m a
252toStream :: Applicative m => StreamK m a -> Stream.Stream m a
242toStream = Stream.fromStreamK
253toStream = Stream.fromStreamK
@@ -932,8 +943,9 @@ mapMaybe f = go
932-- Exception Handling
943-- Exception Handling
933-------------------------------------------------------------------------------
944-------------------------------------------------------------------------------
934
945
935−-- | Like 'Streamly.Data.Stream.handle' but with one significant difference,
946+-- | Like Streamly.Data.Stream.'Streamly.Data.Stream.handle' but with one
936−-- this function observes exceptions from the consumer of the stream as well.
947+-- significant difference, this function observes exceptions from the consumer
948+-- of the stream as well.
937--
949--
938-- You can also convert 'StreamK' to 'Stream' and use exception handling from
950-- You can also convert 'StreamK' to 'Stream' and use exception handling from
939-- 'Stream' module:
951-- 'Stream' module:
@@ -969,10 +981,10 @@ handle f stream = go stream
969-- the entire chain can be invoked when the stream ends voluntarily or if
981-- the entire chain can be invoked when the stream ends voluntarily or if
970-- someone decides to abandon the stream.
982-- someone decides to abandon the stream.
971
983
972−-- | Like 'Streamly.Data.Stream.bracketIO' but with one significant difference,
984+-- | Like Streamly.Data.Stream.'Streamly.Data.Stream.bracketIO' but with one
973−-- this function observes exceptions from the consumer of the stream as well.
985+-- significant difference, this function observes exceptions from the consumer
974−-- Therefore, it cleans up the resource promptly when the consumer encounters
986+-- of the stream as well. Therefore, it cleans up the resource promptly when
975−-- an exception.
987+-- the consumer encounters an exception.
976--
988--
977-- You can also convert 'StreamK' to 'Stream' and use resource handling from
989-- You can also convert 'StreamK' to 'Stream' and use resource handling from
978-- 'Stream' module:
990-- 'Stream' module:
@@ -1013,14 +1025,13 @@ bracketIO bef aft bet =
1013-- Serial Zipping
1025-- Serial Zipping
1014------------------------------------------------------------------------------
1026------------------------------------------------------------------------------
1015
1027
1016−-- | Zip two streams serially using a pure zipping function.
1028+-- | Zipping of @n@ streams can be performed by combining the streams pair
1017−--
1029+-- wise using 'mergeMapWith' with O(n * log n) time complexity. If used
1030+-- with 'concatMapWith' it will have O(n^2) performance.
1018{-# INLINE zipWith #-}
1031{-# INLINE zipWith #-}
1019zipWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c
1032zipWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c
1020zipWith f = zipWithM (\a b -> return (f a b))
1033zipWith f = zipWithM (\a b -> return (f a b))
1021
1034
1022−-- | Zip two streams serially using a monadic zipping function.
1023−--
1024{-# INLINE zipWithM #-}
1035{-# INLINE zipWithM #-}
1025zipWithM :: Monad m =>
1036zipWithM :: Monad m =>
1026 (a -> b -> m c) -> StreamK m a -> StreamK m b -> StreamK m c
1037 (a -> b -> m c) -> StreamK m a -> StreamK m b -> StreamK m c
@@ -1110,6 +1121,10 @@ mergeByM cmp = go
1110 _ -> yld x (goY rx y my)
1121 _ -> yld x (goY rx y my)
1111 in foldStream st yield single stop mx
1122 in foldStream st yield single stop mx
1112
1123
1124+-- | Merging of @n@ streams can be performed by combining the streams pair
1125+-- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used
1126+-- with 'concatMapWith' it will have O(n^2) performance.
1127+--
1113{-# INLINE mergeBy #-}
1128{-# INLINE mergeBy #-}
1114mergeBy :: (a -> a -> Ordering) -> StreamK m a -> StreamK m a -> StreamK m a
1129mergeBy :: (a -> a -> Ordering) -> StreamK m a -> StreamK m a -> StreamK m a
1115-- XXX GHC: This has slightly worse performance than replacing "r <- cmp x y"
1130-- XXX GHC: This has slightly worse performance than replacing "r <- cmp x y"