@@ -9,6 +9,9 @@
9-- Stability : experimental
9-- Stability : experimental
10-- Portability : GHC
10-- Portability : GHC
12+-- To run examples in this module:
14+-- >>> import qualified Streamly.Prelude as Stream
13module Streamly.Internal.Data.Stream.Serial
16module Streamly.Internal.Data.Stream.Serial
@@ -84,61 +87,41 @@ import Prelude hiding (map, mapM, errorWithoutStackTrace)
85------------------------------------------------------------------------------
88------------------------------------------------------------------------------
87−-- | The 'Semigroup' operation for 'SerialT' behaves like a regular append
90+-- | The 'Semigroup' operation @a <> b@ (same as 'Streamly.Prelude.serial')
88−-- operation. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
91+-- appends stream @b@ to stream @a@:
89−-- first until it exhausts and then stream @b@ is evaluated. In other words,
90−-- the elements of stream @b@ are appended to the elements of stream @a@. This
91−-- operation can be used to fold an infinite lazy container of streams.
95−-- import qualified "Streamly.Prelude" as S
97−-- main = (S.toList . 'serially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
93+-- >>> stream1 = Stream.fromList [1,2]
98−-- @
94+-- >>> stream2 = Stream.fromList [3,4]
99−-- @
95+-- >>> Stream.toList $ stream1 <> stream2
100-- [1,2,3,4]
96-- [1,2,3,4]
103−-- The 'Monad' instance runs the /monadic continuation/ for each
98+-- This operation can be used to fold an infinite lazy container of streams.
104−-- element of the stream, serially.
99+-- Since the ordering of elements is important in serial streams, this
100+-- operation is not commutative i.e. a <> b and b <> a are not the same.
106−-- @
102+-- Monad bind is same as @flip . concatMapWith serial@. A single 'Monad' bind
103+-- behaves like a @for@ loop:
107−-- main = S.drain . 'serially' $ do
106+-- Stream.drain $ do
108−-- x <- return 1 \<\> return 2
107+-- x <- Stream.fromList [1,2] -- foreach x in stream
109−-- S.yieldM $ print x
108+-- Stream.yieldM $ print x
116−-- 'SerialT' nests streams serially in a depth first manner.
113+-- Nested monad binds behave like nested @for@ loops:
119−-- main = S.drain . 'serially' $ do
116+-- Stream.drain $ do
120−-- x <- return 1 \<\> return 2
117+-- x <- Stream.fromList [1,2] -- foreach x in stream
121−-- y <- return 3 \<\> return 4
118+-- y <- Stream.fromList [3,4] -- foreach y in stream
122−-- S.yieldM $ print (x, y)
119+-- Stream.yieldM $ print (x, y)
131−-- We call the monadic code being run for each element of the stream a monadic
132−-- continuation. In imperative paradigm we can think of this composition as
133−-- nested @for@ loops and the monadic continuation is the body of the loop. The
134−-- loop iterates for all elements of the stream.
136−-- Note that the behavior and semantics of 'SerialT', including 'Semigroup'
137−-- and 'Monad' instances are exactly like Haskell lists except that 'SerialT'
138−-- can contain effectful actions while lists are pure.
140−-- In the code above, the 'serially' combinator can be omitted as the default
141−-- stream type is 'SerialT'.
143-- /Since: 0.2.0 ("Streamly")/
126-- /Since: 0.2.0 ("Streamly")/
@@ -275,12 +258,13 @@ TRAVERSABLE_INSTANCE(SerialT)
275-- WSerialT
258-- WSerialT
276------------------------------------------------------------------------------
259------------------------------------------------------------------------------
278−-- | The 'Semigroup' operation for 'WSerialT' interleaves the elements from the
261+-- | The 'Semigroup' operation @a <> b@ (same as 'Streamly.Prelude.wSerial')
279−-- two streams. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
262+-- interleaves stream @a@ and stream @b@:
280−-- first to produce the first element of the combined stream and then stream
263+--
281−-- @b@ is evaluated to produce the next element of the combined stream, and
264+-- >>> stream1 = Stream.fromList [1,2]
282−-- then we go back to evaluating stream @a@ and so on. In other words, the
265+-- >>> stream2 = Stream.fromList [3,4]
283−-- elements of stream @a@ are interleaved with the elements of stream @b@.
266+-- >>> Stream.toList $ Stream.wSerially $ stream1 <> stream2
285-- Note that evaluation of @a <> b <> c@ does not schedule @a@, @b@ and @c@
269-- Note that evaluation of @a <> b <> c@ does not schedule @a@, @b@ and @c@
286-- with equal priority. This expression is equivalent to @a <> (b <> c)@,
270-- with equal priority. This expression is equivalent to @a <> (b <> c)@,
@@ -290,6 +274,9 @@ TRAVERSABLE_INSTANCE(SerialT)
290-- leftmost stream gets the same scheduling priority as the rest of the
274-- leftmost stream gets the same scheduling priority as the rest of the
291-- streams taken together. The same is true for each subexpression on the right.
275-- streams taken together. The same is true for each subexpression on the right.
277+-- This also means that this operation is associative only if we disregard the
278+-- ordering of elements in the resulting stream.
293-- Note that this operation cannot be used to fold a container of infinite
280-- Note that this operation cannot be used to fold a container of infinite
294-- streams as the state that it needs to maintain is proportional to the number
281-- streams as the state that it needs to maintain is proportional to the number
295-- of streams.
282-- of streams.
@@ -297,32 +284,34 @@ TRAVERSABLE_INSTANCE(SerialT)
297-- The @W@ in the name stands for @wide@ or breadth wise scheduling in
284-- The @W@ in the name stands for @wide@ or breadth wise scheduling in
298-- contrast to the depth wise scheduling behavior of 'SerialT'.
285-- contrast to the depth wise scheduling behavior of 'SerialT'.
300−-- @
287+-- Monad bind is same as @flip . concatMapWith wSerial@. A single 'Monad' bind
301−-- import Streamly
288+-- behaves like a @for@ loop:
302−-- import qualified "Streamly.Prelude" as S
304−-- main = (S.toList . 'wSerially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
310−-- Similarly, the 'Monad' instance interleaves the iterations of the
290+-- >>> :{
311−-- inner and the outer loop, nesting loops in a breadth first manner.
291+-- Stream.drain $ Stream.wSerially $ do
292+-- x <- Stream.fromList [1,2] -- foreach x in stream
293+-- Stream.yieldM $ print x
298+-- Nested monad binds behave like interleaved nested @for@ loops:
315−-- main = S.drain . 'wSerially' $ do
301+-- Stream.drain $ Stream.wSerially $ do
316−-- x <- return 1 \<\> return 2
302+-- x <- Stream.fromList [1,2] -- foreach x in stream
317−-- y <- return 3 \<\> return 4
303+-- y <- Stream.fromList [3,4] -- foreach y in stream
318−-- S.yieldM $ print (x, y)
304+-- Stream.yieldM $ print (x, y)
311+-- It is a result of interleaving all the nested iterations of @1@ and all the
312+-- nested iterations of @2@:
314+-- > Stream.fromList [(1,3),(1,4)] `wSerial` Stream.fromList [(2,3),(2,4)]
327-- /Since: 0.2.0 ("Streamly")/
316-- /Since: 0.2.0 ("Streamly")/