on master help sign in
Some updates to tutorial
19ac788Harendra Kumar
src/Streamly/Tutorial.hs+187−168
@@ -6,41 +6,27 @@
6-- License : BSD3
6-- License : BSD3
7-- Maintainer : harendra.kumar@gmail.com
7-- Maintainer : harendra.kumar@gmail.com
8--
8--
9−-- Streamly combines streaming, logic programming, concurrency and reactive
9+-- Streamly, short for stream concurrently, combines the essence of streaming
10−-- programming under a single abstraction with a concise API, giving
10+-- and concurrency in functional programming. You can write concurrent as well
11−-- programmers a simple yet powerful tool for wide variety of applications.
11+-- as non-concurrent applications with streamly. Streaming enables writing
12−--
12+-- modular, composable and scalable applications with ease and concurrency
13−-- The 'AsyncT' monad operates on streams. Multiple streams of the same type
13+-- allows you to make them scale and perform well.
14−-- can be summed using different sum style operators. For example, '<>' appends
14+-- Streamly enables writing concurrent applications without being aware of
15−-- streams serially, '<=>' interleaves streams, '<|' combines streams in
16−-- parallel and '<|>' combines streams in a fairly parallel manner. Fold
17−-- utilities are provided to choose a fold style and an operator to fold a
18−-- container in a desired manner producing an AsyncT stream.
19−--
20−-- Streams of the same or different types can be zipped or transformed using
21−-- applicative or monadic composition. The monadic composition is exactly the
22−-- same as a list transformer except that it can be concurrent. It can be
23−-- likened to nested loops in the imperative programming parlance. In addition
24−-- to the regular monadic bind operator '>>=', other operators are provided to
25−-- compose streams in alternate manners. For example, '>->' interleaves the
26−-- iterations of a loop, '>>|' runs iterations in parallel and '>|>' runs all
27−-- iterations in fairly parallel manner.
28−--
29−-- With streamly you can write concurrent and streaming applications without
30−-- being aware of threads or synchronization. No explicit thread control is
15+-- threads or synchronization. No explicit thread control is needed, where
31−-- needed, where applicable the concurrency rate is automatically controlled
16+-- applicable the concurrency rate is automatically controlled based on the
32−-- based on the demand by the consumer. However, combinators are provided to
17+-- demand by the consumer. However, combinators are provided to fine tune the
33−-- fine tune the concurrency control. Streaming and concurrency allows
18+-- concurrency control.
34−-- expressing reactive applications conveniently. See 'Streamly.Examples' for a
19+-- Streaming and concurrency together enable expressing reactive applications
35−-- simple SDL based FRP example.
20+-- conveniently. See 'Streamly.Examples' for a simple SDL based FRP example.
36−--
21+--
22+-- In this tutorial we will go over the basic concepts and how to use the
37−-- For examples and other ways to use the library see 'Streamly.Examples' as
23+-- library. For examples and other ways to use the library see
38−-- well.
24+-- the module @Streamly.Examples@ as well.
39−--
25+
40module Streamly.Tutorial
26module Streamly.Tutorial
41 (
27 (
42− -- * AsyncT Monad Transformer
28+ -- * Streams
43− -- $transformer
29+ -- $streams
44
30
45 -- ** Generating Streams
31 -- ** Generating Streams
46 -- $generating
32 -- $generating
@@ -51,16 +37,16 @@ module Streamly.Tutorial
51 -- * Semigroup Style Composition
37 -- * Semigroup Style Composition
52 -- $semigroup
38 -- $semigroup
53
39
54− -- ** Serial composition (Append)
40+ -- ** Serial composition ('<>')
55 -- $serial
41 -- $serial
56
42
57− -- ** Interleaved composition
43+ -- ** Interleaved composition ('<=>')
58 -- $interleaved
44 -- $interleaved
59
45
60− -- ** Parallel composition
46+ -- ** Async composition ('<|')
61 -- $parallel
47 -- $parallel
62
48
63− -- ** Fairly Parallel composition
49+ -- ** Parallel composition ('<|>')
64 -- $fairParallel
50 -- $fairParallel
65
51
66 -- ** Custom composition
52 -- ** Custom composition
@@ -81,6 +67,12 @@ module Streamly.Tutorial
81 -- ** Monad
67 -- ** Monad
82 -- $listt
68 -- $listt
83
69
70+ -- * Alternate Monadic Compositions
71+ -- $monadicAlternatives
72+
73+ -- ** Serial Iterations
74+ -- $regularSerial
75+
84 -- ** Interleaved Iterations
76 -- ** Interleaved Iterations
85 -- $interleavedNesting
77 -- $interleavedNesting
86
78
@@ -116,7 +108,7 @@ module Streamly.Tutorial
116 )
108 )
117where
109where
118
110
119−import Streamly.Core
111+import Streamly
120import Streamly.Prelude
112import Streamly.Prelude
121-- import Streamly.Examples
113-- import Streamly.Examples
122import Data.Semigroup
114import Data.Semigroup
@@ -125,108 +117,124 @@ import Control.Monad
125import Control.Monad.IO.Class (MonadIO(..))
117import Control.Monad.IO.Class (MonadIO(..))
126import Control.Monad.Trans.Class (MonadTrans (lift))
118import Control.Monad.Trans.Class (MonadTrans (lift))
127
119
128−-- $transformer
120+-- $streams
129--
121--
122+-- Streamly provides many different stream types depending on the desired
123+-- composition style. The simplest type is 'StreamT'. 'StreamT' is a monad
130−-- 'AsyncT' is a monad transformer, the type 'AsyncT m a' represents a stream
124+-- transformer, the type @StreamT m a@ represents a stream of values of type
131−-- of values of type 'a' in the underlying monad 'm'. In this section we
125+-- 'a' in some underlying monad 'm'. For example, @StreamT IO Int@ is a stream
132−-- describe how to create new values of type 'AsyncT' in the first place and
126+-- of 'Int' in 'IO' monad.
133−-- how to eliminate or run a composed 'AsyncT' computation. Later sections
134−-- discuss how to compose existing AsyncT streams or computations to create
135−-- composite AsyncT streams.
136
127
137-- $generating
128-- $generating
138--
129--
139-- Pure values can be placed into the stream type using 'return' or 'pure'.
130-- Pure values can be placed into the stream type using 'return' or 'pure'.
140-- Effects in the IO monad can be lifted to the stream type using the 'liftIO'
131-- Effects in the IO monad can be lifted to the stream type using the 'liftIO'
141-- combinator. In a transformer stack we can lift actions from the lower monad
132-- combinator. In a transformer stack we can lift actions from the lower monad
142−-- using the 'lift' combinator.
133+-- using the 'lift' combinator. Some examples of streams with a single element:
143--
134--
144-- @
135-- @
145−-- return 1
136+-- return 1 :: StreamT IO Int
146-- @
137-- @
147-- @
138-- @
148−-- liftIO $ putStrLn "Hello world!"
139+-- liftIO $ putStrLn "Hello world!" :: StreamT IO ()
140+-- @
141+--
142+-- We can combine streams using '<>' to create streams of many elements:
143+--
144+-- @
145+-- return 1 <> return 2 <> return 3 :: StreamT IO Int
149-- @
146-- @
150
147
151-- $eliminating
148-- $eliminating
152--
149--
153−-- 'runStreaming' runs composed actions in a streaming monad, lowering the type
150+-- 'runStreamT' runs a composed 'StreamT' computation, lowering the type into
154−-- into the underlying monad and discarding the values generated by the stream;
151+-- the underlying monad and discarding the result stream:
152+--
153+-- @
154+-- import Streamly
155+--
156+-- main = runStreamT $ liftIO $ putStrLn "Hello world!"
157+-- @
158+--
155−-- 'toList' collects the resulting stream in a list. 'uncons' can be used
159+-- 'toList' runs a stream computation and collects the result stream in a list
156−-- to deconstruct a stream into head and tail. Before you run a stream you need
160+-- in the underlying monad. 'toList' is a polymorphic function that works on
157−-- to express the type of the stream by using one of the streaming type
161+-- multiple stream types belonging to the class 'Streaming'. Therefore, before
162+-- you run a stream you need to tell how you want to interpret the stream by
158−-- combinators ('serially', 'asyncly', 'parallely' etc.).
163+-- using one of the stream type combinators ('serially', 'asyncly', 'parallely'
164+-- etc.). The combinator 'serially' is equivalent to annotating the type as @::
165+-- StreamT@.
159--
166--
160-- @
167-- @
161-- import Streamly
168-- import Streamly
162--
169--
163-- main = do
170-- main = do
164−-- runStreaming $ serially $ liftIO $ putStrLn "Hello world!"
165-- xs \<- toList $ serially $ return 1 <> return 2
171-- xs \<- toList $ serially $ return 1 <> return 2
166-- print xs
172-- print xs
167-- @
173-- @
174+--
175+-- For other ways to eliminate a stream see the module @Streamly.Prelude@.
168
176
169-- $semigroup
177-- $semigroup
170−-- AsyncT streams of the same type can be combined into a composite stream in
178+-- Streams of the same type can be combined into a composite stream in many
171−-- many different ways using semigroup style binary composition operators
179+-- different ways using one of the semigroup style binary composition operators
172−-- ('<>', '<=>', '<|', '<|>', 'mplus').
180+-- i.e. '<>', '<=>', '<|', '<|>', 'mplus'. These operators work on all stream
173−-- We will use the following utility functions to illustrate these operators in
181+-- types ('StreamT', 'AsyncT' etc.) uniformly.
174−-- the following sections. The @traced@ function prints the 'threadId' of the
182+--
175−-- thread in which the computation runs and the @timer@ function adds a delay
183+-- To illustrate the concurrent aspects, we will use the following @delay@
176−-- specified by the argument in seconds and then prints the argument.
184+-- function to introduce a delay specified in seconds.
177--
185--
178-- @
186-- @
179-- import Streamly
187-- import Streamly
180-- import Control.Concurrent
188-- import Control.Concurrent
181--
189--
182−-- traced m = liftIO (myThreadId >>= print) >> m
183−--
184−-- timer n = liftIO $ do
190+-- delay n = liftIO $ do
185-- threadDelay (n * 1000000)
191-- threadDelay (n * 1000000)
186-- tid \<- myThreadId
192-- tid \<- myThreadId
187−-- putStrLn (show tid ++ ": Timer " ++ show n)
193+-- putStrLn (show tid ++ ": Delay " ++ show n)
188-- @
194-- @
189
195
190-- $serial
196-- $serial
191−-- The '<>' operator composes multiple AsyncT streams in series i.e. the next
197+-- As we have already seen, the '<>' operator composes multiple streams in
192−-- event happens after the previous one is completed. The following example
198+-- series i.e. the next element is yielded only after the previous one has been
199+-- processed completely. The following example prints the sequence 3, 2, 1 and
193−-- prints the sequence 3, 2, 1 and takes a total of 6 seconds:
200+-- takes a total of 6 seconds because everything is serial:
194--
201--
195-- @
202-- @
196−-- main = runStreaming $ serially $ timer 3 <> timer 2 <> timer 1
203+-- main = runStreamT $ delay 3 <> delay 2 <> delay 1
197-- @
204-- @
198-- @
205-- @
199−-- ThreadId 36: Timer 3
206+-- ThreadId 36: Delay 3
200−-- ThreadId 36: Timer 2
207+-- ThreadId 36: Delay 2
201−-- ThreadId 36: Timer 1
208+-- ThreadId 36: Delay 1
202-- @
209-- @
203
210
204-- $interleaved
211-- $interleaved
205−-- The '<=>' operator interleaves the two computations i.e. it yields one item
212+-- The '<=>' operator interleaves the two computations i.e. it yields one
206−-- from the first stream and then one item from the second stream. The
213+-- element from the first stream and then one element from the second stream,
207−-- following example should print the sequence 1, 3, 2, 4:
214+-- and so on. The following example prints the sequence 1, 3, 2, 4 and takes a
215+-- total of 10 seconds because everything is serial:
208--
216--
209-- @
217-- @
210−-- main = runStreaming $ serially $ (timer 1 <> timer 2) \<=> (timer 3 <> timer 4)
218+-- main = runStreamT $ (delay 1 <> delay 2) \<=> (delay 3 <> delay 4)
211-- @
219-- @
212-- @
220-- @
213−-- ThreadId 36: Timer 1
221+-- ThreadId 36: Delay 1
214−-- ThreadId 36: Timer 3
222+-- ThreadId 36: Delay 3
215−-- ThreadId 36: Timer 2
223+-- ThreadId 36: Delay 2
216−-- ThreadId 36: Timer 4
224+-- ThreadId 36: Delay 4
217-- @
225-- @
218
226
219-- $parallel
227-- $parallel
220--
228--
221−-- The '<|' operator runs computations in parallel, preferring the first
229+-- The '<|' operator can run computations concurrently, preferring the first
222−-- computation over the second. The second computation is run in parallel with
230+-- computation over the second. The second computation is run concurrently with
223-- the first only if the first computation is not producing enough output to
231-- the first only if the first computation is not producing enough output to
224-- keep the consumer busy otherwise the second computation is run serially
232-- keep the consumer busy otherwise the second computation is run serially
225−-- after the first. We maintain a pool of minimum number of threads, for
233+-- after the previous one. The number of concurrent threads is adapted
226−-- running the computations, that is big enough to match the pull rate of the
227−-- consumer of the stream.
234+-- dynamically based on the pull rate of the consumer of the stream.
235+--
228-- Note that the left bias of the operator '<|' is suggested by its shape.
236-- Note that the left bias of the operator '<|' is suggested by its shape.
229−-- It is also half cut '<|>' which is fairly parallel in contrast.
237+-- It is also an unbalanced version of the fairly parallel operator '<|>'.
230--
238--
231-- The following example runs all the parallel computations in a single thread
239-- The following example runs all the parallel computations in a single thread
232-- one after another, because none of them blocks. Note that if the consumer
240-- one after another, because none of them blocks. Note that if the consumer
@@ -234,7 +242,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
234-- computation to keep up even if none of them blocks:
242-- computation to keep up even if none of them blocks:
235--
243--
236-- @
244-- @
237−-- main = runStreaming $ serially $ traced (sqrt 9) <| traced (sqrt 16) <| traced (sqrt 25)
245+-- main = runStreamT $ traced (sqrt 9) <| traced (sqrt 16) <| traced (sqrt 25)
238-- @
246-- @
239-- @
247-- @
240-- ThreadId 40
248-- ThreadId 40
@@ -246,18 +254,19 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
246-- next one in a separate thread and so on:
254-- next one in a separate thread and so on:
247--
255--
248-- @
256-- @
249−-- main = runStreaming $ serially $ timer 3 <| timer 2 <| timer 1
257+-- main = runStreamT $ delay 3 <| delay 2 <| delay 1
250-- @
258-- @
251-- @
259-- @
252−-- ThreadId 42: Timer 1
260+-- ThreadId 42: Delay 1
253−-- ThreadId 41: Timer 2
261+-- ThreadId 41: Delay 2
254−-- ThreadId 40: Timer 3
262+-- ThreadId 40: Delay 3
255-- @
263-- @
256--
264--
257−-- When we have a hierarchy of computations this operator follows DFS style:
265+-- When we have a tree of computations composed using this operator, the tree
266+-- is traversed in DFS style because it always prefers the left computation:
258--
267--
259-- @
268-- @
260−-- main = runStreaming $ serially $ (p 1 <| p 2) <| (p 3 <| p 4)
269+-- main = runStreamT $ (p 1 <| p 2) <| (p 3 <| p 4)
261-- where p = liftIO . print
270-- where p = liftIO . print
262-- @
271-- @
263-- @
272-- @
@@ -267,15 +276,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
267-- 4
276-- 4
268-- @
277-- @
269--
278--
270−-- Note that since this operator is left biased it should not be used when the
279+-- Note that since this operator is left biased it cannot be used when the
271−-- computations have timers that are relative to each other because all
280+-- composed computations have timers that are relative to each other because
272−-- computations may not be started at the same time and therefore timers in all
281+-- all computations may not be started at the same time and therefore timers in
273−-- of them will not start at the same time. When relative timing among all
282+-- all of them may not start at the same time. When relative timing among all
274-- computations is important or when we need to start all computations at once
283-- computations is important or when we need to start all computations at once
275−-- for some reason use '<|>' instead. However, '<|' is useful in situations
284+-- for some reason '<|>' must be used instead. However, '<|' is useful in
276−-- when we want to optimally utilize the resources and we know that the
285+-- situations when we want to optimally utilize the resources and we know that
277−-- computations can run in parallel but we do not care if they actually run in
286+-- the computations can run in parallel but we do not care if they actually run
278−-- parallel or not, that decision is left to the scheduler. Also, note that
287+-- in parallel or not, that decision is left to the scheduler. Also, note that
279-- this operator can be used to fold infinite containers in contrast to '<|>',
288-- this operator can be used to fold infinite containers in contrast to '<|>',
280-- because it does not require us to run all of them at the same time.
289-- because it does not require us to run all of them at the same time.
281
290
@@ -283,9 +292,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
283--
292--
284-- The 'Alternative' composition operator '<|>', like '<|', runs the composed
293-- The 'Alternative' composition operator '<|>', like '<|', runs the composed
285-- computations in parallel. However, unlike '<|' it runs all of the
294-- computations in parallel. However, unlike '<|' it runs all of the
286−-- computations in fairly parallel manner using a round robin scheduling. Note
295+-- computations in fairly parallel manner using a round robin scheduling
287−-- that this should not be used on infinite containers, as it will lead to an
296+-- mechanism. Note that this should not be used on infinite containers, as it
288−-- infinite scheduling queue.
297+-- will lead to an infinite sized scheduling queue.
289--
298--
290-- The following example sends a query to three search engines and prints the
299-- The following example sends a query to three search engines and prints the
291-- name of the search engine as a response arrives:
300-- name of the search engine as a response arrives:
@@ -294,7 +303,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
294-- import Streamly
303-- import Streamly
295-- import Network.HTTP.Simple
304-- import Network.HTTP.Simple
296--
305--
297−-- main = runStreaming $ serially $ google \<|> bing \<|> duckduckgo
306+-- main = runStreamT $ google \<|> bing \<|> duckduckgo
298-- where
307-- where
299-- google = get "https://www.google.com/search?q=haskell"
308-- google = get "https://www.google.com/search?q=haskell"
300-- bing = get "https://www.bing.com/search?q=haskell"
309-- bing = get "https://www.bing.com/search?q=haskell"
@@ -305,22 +314,24 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
305-- $custom
314-- $custom
306--
315--
307-- The 'async' API can be used to create references to asynchronously running
316-- The 'async' API can be used to create references to asynchronously running
308−-- 'AsyncT' computations. We can then use 'uncons' to explore the
317+-- stream computations. We can then use 'uncons' to explore the streams
309−-- streams arbitrarily and then recompose them to create a new 'AsyncT' stream.
318+-- arbitrarily and then recompose individual elements to create a new stream.
310−-- This way we can dynamically decide which stream to explore at any given time.
319+-- This way we can dynamically decide which stream to explore at any given
311−-- Take an example of a merge sort of two sorted streams. We need to keep
320+-- time. Take an example of a merge sort of two sorted streams. We need to
312−-- consuming items from the stream which has the lowest item in the sort order.
321+-- keep consuming items from the stream which has the lowest item in the sort
313−-- This can be achieved using async references to streams. See
322+-- order. This can be achieved using async references to streams. See
314-- 'Streamly.Examples.MergeSortedStreams'.
323-- 'Streamly.Examples.MergeSortedStreams'.
315
324
316-- $monoid
325-- $monoid
317--
326--
318−-- Containers can be folded using a suitable identity ('mempty',
327+-- Each of the semigroup compositions described has an identity that can be
319−-- 'empty', 'mzero') and one of the binary operators mentioned earlier. Some
328+-- used to fold a possibly empty container. An empty stream is represented by
329+-- 'nil' which can be represented in various standard forms as 'mempty',
330+-- 'empty' or 'mzero'.
320−-- fold utilities are provided by the library for convenience:
331+-- Some fold utilities are also provided by the library for convenience:
321--
332--
322−-- * 'foldWith' folds a 'Foldable' container of AsyncT computations using the
333+-- * 'foldWith' folds a 'Foldable' container of stream computations using the
323−-- given function.
334+-- given composition operator.
324-- * 'foldMapWith' folds like foldWith but also maps a function before folding.
335-- * 'foldMapWith' folds like foldWith but also maps a function before folding.
325-- * 'forEachWith' is like foldMapwith but the container argument comes before
336-- * 'forEachWith' is like foldMapwith but the container argument comes before
326-- the function argument.
337-- the function argument.
@@ -329,7 +340,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
329-- import Streamly
340-- import Streamly
330--
341--
331-- main = do
342-- main = do
332−-- runStreaming $ serially $ liftIO $ putStrLn "Hello world!"
343+-- runStreaming . serially $ liftIO $ putStrLn "Hello world!"
333-- xs \<- toList . serially $ return 1 <> return 2; print xs
344-- xs \<- toList . serially $ return 1 <> return 2; print xs
334-- xs \<- toList . serially $ foldWith (<>) (map return [1..10]); print xs
345-- xs \<- toList . serially $ foldWith (<>) (map return [1..10]); print xs
335-- xs \<- toList . serially $ foldMapWith (<>) return [1..10]; print xs
346-- xs \<- toList . serially $ foldMapWith (<>) return [1..10]; print xs
@@ -338,40 +349,40 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
338
349
339-- $transforming
350-- $transforming
340--
351--
341−-- The previous section discussed ways to compose streams without any
352+-- The previous section discussed ways to merge the elements of two streams
342−-- transformation using sum style operators. In this section we will explore
353+-- without doing any transformation on them. In this section we will explore
343−-- how to transform streams using 'Functor', 'Applicative' or 'Monad'
354+-- how to transform streams using 'Functor', 'Applicative' or 'Monad' style
344−-- style compositions. The applicative and monad composition of 'AsyncT' is
355+-- compositions. The applicative and monad composition of all 'Streaming' types
345−-- exactly the same as a list transformer.
356+-- behave exactly the same way as a list transformer. For simplicity of
346−-- For simplicity of illustration we are using streams of
357+-- illustration we are using streams of pure values in the following examples.
347−-- pure values in the following examples. However, the real application of
358+-- However, the real application of streams arises when these streams are
348−-- AsyncT arises when these streams are generated using monadic actions. In the
359+-- generated using monadic actions. In the pure stream cases 'StreamT' is
349−-- pure stream cases AsyncT is equivalent to the list monad.
360+-- equivalent to the list monad.
350
361
351-- $functor
362-- $functor
352--
363--
353−-- The simplest way to transform a stream is by mapping a function on
364+-- The simplest transformation on a stream is mapping a function on
354-- all elements of the stream using 'fmap'.
365-- all elements of the stream using 'fmap'.
355--
366--
356-- @
367-- @
357-- import Streamly
368-- import Streamly
358--
369--
359-- main = do
370-- main = do
360−-- let nums = foldMapWith (<>) return [1..10]
371+-- let nums = each [1..10]
361-- let strings = fmap show nums
372-- let strings = fmap show nums
362-- (toList $ serially $ fmap putStrLn strings) >>= sequence
373-- (toList $ serially $ fmap putStrLn strings) >>= sequence
363-- @
374-- @
364
375
365-- $applicative
376-- $applicative
366--
377--
367−-- You can compose two or more streams using a function in 'Applicative' manner
368−-- as well. In functional programmer's parlance the applicative implements
378+-- In functional programmer's parlance the 'Applicative' instance of
369−-- non-determinism, exploring all possible combination of choices from both the
379+-- 'Streaming' types implement non-determinism, exploring all possible
380+-- combination of choices from both the streams. From an imperative
370−-- streams. From an imperative programmer's point of view the applicative
381+-- programmer's point of view the applicative behaves like nested loops i.e.
371−-- behaves like nested loops i.e. for each element in the first argument and
382+-- for each element in the first argument and for each element in the second
372−-- for each element in the second argument apply the function. Note that the
383+-- argument apply the body of the loop. Note that this behavior of applicative
373−-- behavior of applicative instance of 'AsyncT' is exactly the same as that of
384+-- instance of all 'Streaming' types is exactly the same as that of a list
374−-- a list transformer.
385+-- transformer.
375--
386--
376-- The following example prints all combinations of size, color and shape:
387-- The following example prints all combinations of size, color and shape:
377--
388--
@@ -380,18 +391,17 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
380--
391--
381-- where
392-- where
382--
393--
383−-- toStream xs = foldMapWith (<>) return xs
384−-- sizes = toStream [1, 2, 3]
394+-- sizes = each [1, 2, 3]
385−-- colors = toStream ["red", "green", "blue"]
395+-- colors = each ["red", "green", "blue"]
386−-- shapes = toStream ["triangle", "square", "circle"]
396+-- shapes = each ["triangle", "square", "circle"]
387-- @
397-- @
388
398
389-- $listt
399-- $listt
390--
400--
391−-- The monadic composition is similar to Applicative composition and just like
401+-- The monadic composition of 'Streaming' types is similar to 'Applicative'
402+-- composition and just the same as that of list transformer. It is like nested
392−-- a list transformer. It is like nested loops in the imperative parlance. The
403+-- loops in the imperative parlance. The monadic continuation is executed for
393−-- monadic continuation is performed for each element in a stream.
404+-- each element in a stream.
394−--
395-- For example the following program repeatedly reads strings from standard
405-- For example the following program repeatedly reads strings from standard
396-- input and echoes them to the standard output. This is like one infinite
406-- input and echoes them to the standard output. This is like one infinite
397-- loop:
407-- loop:
@@ -400,17 +410,28 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
400-- import Streamly
410-- import Streamly
401-- import Data.Semigroup (cycle1)
411-- import Data.Semigroup (cycle1)
402--
412--
403−-- main = runStreaming $ serially $ do
413+-- main = runStreamT $ do
404-- str <- cycle1 (liftIO getLine)
414-- str <- cycle1 (liftIO getLine)
405-- liftIO $ putStrLn str
415-- liftIO $ putStrLn str
406-- @
416-- @
417+
418+-- $monadicAlternatives
419+--
420+-- Just like we saw multiple non-concurrent and concurrent variants of sum
421+-- style compositions earlier, monadic composition also has multiple variants
422+-- each of which exactly corresponds to one of the sum style composition
423+-- variant.
407--
424--
408−-- This example is equivalent to three nested loops, the first one for sizes,
425+-- We will use the following polymorphic code which is agnostic of a specific
409−-- the second one for colors and the third one for shapes. It prints all
426+-- 'Streaming' type to illustrate various variants of monadic stream
410−-- combinations of size, color and shape.
427+-- composition. This code can be interpreted in many different ways depending
428+-- on the actual type used.
411--
429--
412-- @
430-- @
413−-- main = runStreaming $ serially $ do
431+-- import Streamly
432+--
433+-- composed :: Streaming t => t m a
434+-- composed = do
414-- sz <- sizes
435-- sz <- sizes
415-- cl <- colors
436-- cl <- colors
416-- sh <- shapes
437-- sh <- shapes
@@ -418,30 +439,28 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
418--
439--
419-- where
440-- where
420--
441--
421−-- toStream xs = foldMapWith (<>) return xs
422−-- sizes = toStream [1, 2, 3]
442+-- sizes = each [1, 2, 3]
423−-- colors = toStream ["red", "green", "blue"]
443+-- colors = each ["red", "green", "blue"]
424−-- shapes = toStream ["triangle", "square", "circle"]
444+-- shapes = each ["triangle", "square", "circle"]
425-- @
445-- @
426−--
427
446
428−-- $interleavedNesting
447+-- $regularSerial
429--
448--
430−-- @
449+-- When we interpret the code as 'StreamT' we get the regular list transformer
431−-- import Streamly
450+-- like serial composition. The following is equivalent to three nested loops,
451+-- the first one for sizes, the second one for colors and the third one for
452+-- shapes. For each size we iterate each color and for each color we iterate
453+-- each shape.
432--
454--
455+-- @
433−-- main = runStreaming $ interleaved $
456+-- main = runStreaming $ serially $ composed
434−-- sz <- sizes
457+-- @
435−-- cl <- colors
436−-- sh <- shapes
437−-- liftIO $ putStrLn $ show (sz, cl, sh)
438--
458--
439−-- where
459+
460+-- $interleavedNesting
440--
461--
441−-- toStream xs = foldMapWith (<>) return xs
462+-- @
442−-- sizes = toStream [1, 2, 3]
463+-- main = runStreaming $ interleaved $ composed
443−-- colors = toStream ["red", "green", "blue"]
444−-- shapes = toStream ["triangle", "square", "circle"]
445-- @
464-- @
446
465
447-- $concurrentNesting
466-- $concurrentNesting
streamly.cabal+1−1
@@ -6,7 +6,7 @@ description: Streamly unifies concurrency and streaming in a single monad
6 streams, a monadic product composition as well as the standard pipelined
6 streams, a monadic product composition as well as the standard pipelined
7 composition provided by streaming libraries. A natural extension of regular
7 composition provided by streaming libraries. A natural extension of regular
8 monadic composition to streaming and concurrency makes it intuitive and
8 monadic composition to streaming and concurrency makes it intuitive and
9− concise with almost unviversal application. You can write concurrent or
9+ concise with almost universal application. You can write concurrent or
10 non-concurrent applications using simple IO, logic programming, streaming IO
10 non-concurrent applications using simple IO, logic programming, streaming IO
11 or reactive programming (FRP) using the same API. You can also think about it
11 or reactive programming (FRP) using the same API. You can also think about it
12 as representing concurrent and composable state machines in imperative terms.
12 as representing concurrent and composable state machines in imperative terms.