@@ -18,8 +18,6 @@
1818--
1919module Streamly.Streams
2020 (
21− -- * Product Style Composition
22− -- $product
2321 Streaming (..)
2422 , MonadAsync
2523
@@ -99,48 +97,6 @@ import Data.Semigroup (Semigroup(..))
9997import Prelude hiding (drop, take, zipWith)
10098import Streamly.Core
10199
102−-- $product
103−--
104−-- Different styles of streams represented by different types dictate the
105−-- manner in which a stream is processed in monadic or applicative composition.
106−-- Each type differs in the processing order of the elements or processing
107−-- parallelism. The types have the same underlying representation and
108−-- therefore can be converted back and forth easily to switch the composition
109−-- style at any point.
110−--
111−-- 'StreamT' and 'InterleavedT' process the streams serially but differ in the
112−-- order in which the elements of the streams are processed. 'StreamT' goes
113−-- depth first that is it combine one element of the first stream to every
114−-- element of the second stream before it comes back to process the second
115−-- element of the first stream. 'InterleavedT' takes turns when processing
116−-- elements from each stream and therefore all iterations finish at the same
117−-- time rather than one iteration at a time as in the case of 'StreamT'.
118−-- Depending on the cache behavior or IO behavior 'InterleavedT' might be
119−-- slightly more efficient than 'StreamT' or vice-versa.
120−--
121−-- 'AsyncT' and 'ParallelT' can process the individual elements of the streams
122−-- in parallel. 'AsyncT' is the parallel version of 'StreamT', it follows the
123−-- order of 'StreamT', processing multiple elements of a stream concurrently.
124−-- 'ParallelT' follows the order of 'InterleavedT', processing and interleaving
125−-- both streams concurrently. Also, note that 'fmap' on these streams would
126−-- also work in a concurrent fashion i.e. multiple elements in a stream or
127−-- multiple streams can be mapped concurrently.
128−--
129−-- Zipping types have a different style of composition altogether, they zip the
130−-- streams rather than multiplying them. These types provide only Applicative
131−-- composition and no Monad instances. 'ZipStream' zips serially i.e. it
132−-- produces one element from each stream in a serial manner and then zips the
133−-- two items. 'ZipAsync' on the other hand zips in parallel, it produces one
134−-- element from each stream concurrently and then zips the two.
135−--
136−-- It may help develop a better understanding if we think in terms of stream
137−-- generation (production) side parallelism versus stream processing
138−-- (consumption) side parallelism. The sum style operators (e.g. '<|' or '<|>')
139−-- generate streams in parallel and then combine the results into a unified
140−-- stream. On the other hand the product style composition (e.g. 'AsyncT' or
141−-- 'ParallelT' or 'ZipAsync') dictate how the elements of a stream are
142−-- processed when it is consumed.
143−
144100------------------------------------------------------------------------------
145101-- Types that can behave as a Stream
146102------------------------------------------------------------------------------
@@ -231,12 +187,19 @@ async m = do
231187 return $ fromSVar sv
232188
233189------------------------------------------------------------------------------
234−-- StreamT iterates serially in the Monad and Applicative compositions
190+-- StreamT
235191------------------------------------------------------------------------------
236192
237−-- | Note that 'StreamT' style composition works well operationally for
238−-- infinite streams. However if the second stream is infinite we will never
239−-- come back to the second iteration of the first stream.
193+-- | The 'Monad' instance of 'StreamT' combines two streams serially in the
194+-- most straightforward manner, just like the nested 'for' loops in imperative
195+-- paradigm. It moves to the next element in the first stream only after the
196+-- first element has been combined with every element in the second stream. In
197+-- other words, 'StreamT' nests loops in a depth first manner.
198+--
199+-- Note that 'StreamT' style composition works well operationally even when the
200+-- streams being composed are infinite. However, if the second stream is
201+-- infinite we will never come back to the second iteration of the first
202+-- stream.
240203
241204newtype StreamT m a = StreamT {getStreamT :: Stream m a}
242205 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
@@ -261,8 +224,6 @@ instance Streaming StreamT where
261224-- Monad
262225------------------------------------------------------------------------------
263226
264−-- | Execute a monadic action sequentially for each element in the 'AsyncT'
265−-- stream, i.e. an iteration finishes completely before the next one starts.
266227instance Monad m => Monad (StreamT m) where
267228 return = pure
268229 (StreamT (Stream m)) >>= f = StreamT $ Stream $ \_ stp yld ->
@@ -336,13 +297,16 @@ instance (Monad m, Floating a) => Floating (StreamT m a) where
336297 logBase = liftA2 logBase
337298
338299------------------------------------------------------------------------------
339−-- InterleavedT interleaves the iterations in the Monad and Applicative
340−-- compositions.
300+-- InterleavedT
341301------------------------------------------------------------------------------
342302
343−-- | Note that 'InterleavedT' style composition cannot work well operationally
344−-- on infinite streams. It will keep accumulating the elements of the stream
345−-- resulting in continuous increase in space consumption.
303+-- | The 'Monad' instance of 'InterleavedT' fairly interleaves the iterations
304+-- of the inner and the outer loop, nesting loops in a breadth first manner.
305+--
306+-- Note that 'InterleavedT' style composition cannot work well operationally on
307+-- infinite streams. Since the next iteration of the outer loop is started even
308+-- before the first one has finsihed, it will keep accumulating space resulting
309+-- in a continuous increase in space consumption.
346310--
347311newtype InterleavedT m a = InterleavedT {getInterleavedT :: Stream m a}
348312 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
@@ -358,8 +322,6 @@ instance Streaming InterleavedT where
358322 toStream = getInterleavedT
359323 fromStream = InterleavedT
360324
361−-- | Execute a monadic action for each element in the stream, in a fairly
362−-- interleaved manner i.e. iterations yield alternately.
363325instance Monad m => Monad (InterleavedT m) where
364326 return = pure
365327 (InterleavedT (Stream m)) >>= f = InterleavedT $ Stream $ \_ stp yld ->
@@ -434,16 +396,18 @@ instance (Monad m, Floating a) => Floating (InterleavedT m a) where
434396 logBase = liftA2 logBase
435397
436398------------------------------------------------------------------------------
437−-- AsyncT runs the iterations asynchronously in the Monad and Applicative
438−-- compositions. More iterations are started in parallel on demand. It can work
439−-- with infinite streams.
399+-- AsyncT
440400------------------------------------------------------------------------------
441401
442−-- | Just like 'StreamT', 'AsyncT' style composition works well operationally
443−-- for infinite streams. However, if the second stream is infinite we will
444−-- never be able to finish the first stream.
402+-- | 'AsyncT' is the concurrent equivalent of 'StreamT'. The type 'AsyncT'
403+-- follows the same composition scheme as 'StreamT' except that it can run
404+-- multiple iterations concurrently. More concurrent iterations are started
405+-- only if the previous iterations are not able to produce enough output for
406+-- the consumer.
445407--
446−-- Note that 'fmap' on 'AsyncT' would map on multiple elements concurrently.
408+-- Just like 'StreamT', 'AsyncT' style composition works well operationally
409+-- for infinite streams. However, if the second stream is infinite we will
410+-- never be able to start the second iteration of the first stream.
447411
448412newtype AsyncT m a = AsyncT {getAsyncT :: Stream m a}
449413 deriving (Semigroup, Monoid, MonadTrans)
@@ -476,9 +440,6 @@ parbind par m f = go m
476440 yield a (Just r) = run $ f a `par` (go r)
477441 in g Nothing stp yield
478442
479−-- | Execute a monadic action for each element in the stream, running
480−-- iterations in parallel, but giving preference to iterations started
481−-- earlier.
482443instance MonadAsync m => Monad (AsyncT m) where
483444 return = pure
484445 (AsyncT m) >>= f = AsyncT $ parbind par m g
@@ -548,17 +509,20 @@ instance (MonadAsync m, Floating a) => Floating (AsyncT m a) where
548509 logBase = liftA2 logBase
549510
550511------------------------------------------------------------------------------
551−-- ParallelT runs all iterations in parallel in the Monad and Applicative
552−-- compositions. Iterations are scheduled in a round-robin fashion.
512+-- ParallelT
553513------------------------------------------------------------------------------
554514
555−-- | Note that, just like 'InterleavedT', 'ParallelT' style composition cannot
556−-- work well operationally on infinite streams. It will keep accumulating the
515+-- | 'ParallelT' is the concurrent equivalent of 'InterleavedT'. It follows the
516+-- same composition scheme as 'InterleavedT' i.e. fairly interleaving the inner
517+-- and outer loops in a round robin fashion except that it can run multiple
518+-- iterations concurrently.
519+--
520+-- Note that, just like 'InterleavedT', 'ParallelT' style composition cannot
521+-- work well operationally on infinite streams. Because earlier iterations do
522+-- not finish before the new ones are started, it will keep on accumulating the
557523-- elements of the stream resulting in continuous increase in space
558524-- consumption.
559525--
560−-- Note that 'fmap' on 'ParallelT' would map on both streams concurrently.
561−--
562526newtype ParallelT m a = ParallelT {getParallelT :: Stream m a}
563527 deriving (Semigroup, Monoid, MonadTrans)
564528
@@ -575,9 +539,6 @@ instance Streaming ParallelT where
575539 toStream = getParallelT
576540 fromStream = ParallelT
577541
578−-- | Execute a monadic action for each element in the stream, running
579−-- iterations in a fairly parallel manner, i.e. all iterations are equally
580−-- likely to run.
581542instance MonadAsync m => Monad (ParallelT m) where
582543 return = pure
583544 (ParallelT m) >>= f = ParallelT $ parbind par m g
@@ -870,7 +831,8 @@ infixr 5 <=>
870831-- | Sequential interleaved composition, in contrast to '<>' this operator
871832-- fairly interleaves the two streams instead of appending them; yielding one
872833-- element from each stream alternately. Unlike '<>' it cannot be used to fold
873−-- an infinite container of streams.
834+-- an infinite container of streams. This operator corresponds to the
835+-- 'InterleavedT' type for product style composition.
874836{-# INLINE (<=>) #-}
875837(<=>) :: Streaming t => t m a -> t m a -> t m a
876838m1 <=> m2 = fromStream $ interleave (toStream m1) (toStream m2)
@@ -880,7 +842,8 @@ m1 <=> m2 = fromStream $ interleave (toStream m1) (toStream m2)
880842-- them. It keeps yielding from the stream on the left as long as it can. If
881843-- the left stream blocks or cannot keep up with the pace of the consumer it
882844-- can yield from the stream on the right in parallel. Unlike '<|>' it can be
883−-- used to fold infinite containers of streams.
845+-- used to fold infinite containers of streams. This operator corresponds to
846+-- the 'AsyncT' type for product style composition.
884847{-# INLINE (<|) #-}
885848(<|) :: (Streaming t, MonadAsync m) => t m a -> t m a -> t m a
886849m1 <| m2 = fromStream $ parLeft (toStream m1) (toStream m2)