@@ -6,40 +6,41 @@
6-- License : BSD3
6-- License : BSD3
7-- Maintainer : harendra.kumar@gmail.com
7-- Maintainer : harendra.kumar@gmail.com
9−-- Streamly, short for stream concurrently, combines the essence of
9+-- Streamly is a general computing framework based on streaming IO. The IO
10−-- non-determinism, streaming and concurrency in functional programming.
10+-- monad and pure lists are a special case of streamly. On one hand, streamly
11−-- Concurrent and non-concurrent applications are almost indistinguishable,
11+-- extends the lists of pure values to lists of monadic actions, on the other
12−-- concurrency capability does not at all impact the performance of
12+-- hand it extends the IO monad with concurrrent non-determinism. In simple
13−-- non-concurrent case.
13+-- imperative terms we can say that streamly extends the IO monad with @for@
14+-- loops and nested @for@ loops with concurrency support. You can understand
15+-- this analogy better once you can go through this tutorial.
14−-- Streaming enables writing modular, composable and scalable applications with
17+-- Streaming in general enables writing modular, composable and scalable
15−-- ease and concurrency allows you to make them scale and perform well.
18+-- applications with ease, and concurrency allows you to make them scale and
16−-- Streamly enables writing concurrent applications without being aware of
19+-- perform well. Streamly enables writing scalable concurrent applications
17−-- threads or synchronization. No explicit thread control is needed, where
20+-- without being aware of threads or synchronization. No explicit thread
18−-- applicable the concurrency rate is automatically controlled based on the
21+-- control is needed, where applicable the concurrency rate is automatically
19−-- demand by the consumer. However, combinators are provided to fine tune the
22+-- controlled based on the demand by the consumer. However, combinators can be
20−-- concurrency control.
23+-- used to fine tune the concurrency control.
21-- Streaming and concurrency together enable expressing reactive applications
25-- Streaming and concurrency together enable expressing reactive applications
26+-- conveniently. See the @CirclingSquare@ example in the examples directory for
22−-- conveniently. See "Streamly.Examples" for a simple SDL based FRP example.
27+-- a simple SDL based FRP example. To summarize, streamly provides a unified
23−--
28+-- computing framework for streaming, non-determinism and functional reactive
24−-- Streamly streams are very much like the Haskell lists and most of the
29+-- programming in an elegant and simple API that is a natural extension of pure
25−-- functions that work on lists have a counterpart that works on streams.
30+-- lists to monadic streams.
26−-- However, streamly streams can be generated, consumed or combined
31+--
27−-- concurrently. In this tutorial we will go over the basic concepts and how to
32+-- In this tutorial we will go over the basic concepts and how to
28−-- use the library. The documentation of @Streamly@ module has more details on
33+-- use the library. See the last section for further reading resources.
29−-- core APIs. For more APIs for constructing, folding, filtering, mapping and
30−-- zipping etc. see the documentation of "Streamly.Prelude" module. For
31−-- examples and other ways to use the library see the module
32−-- "Streamly.Examples" as well.
34module Streamly.Tutorial
35module Streamly.Tutorial
36 -- * Streams
37 -- * Streams
37 -- $streams
38 -- $streams
39− -- ** Generating Streams
40+ -- * Generating Streams
40 -- $generating
41 -- $generating
42− -- ** Eliminating Streams
43+ -- * Eliminating Streams
43 -- $eliminating
44 -- $eliminating
45 -- * Transforming Streams
46 -- * Transforming Streams
@@ -125,6 +126,9 @@ module Streamly.Tutorial
126 -- * Comparison with Existing Packages
127 -- * Comparison with Existing Packages
127 -- $comparison
128 -- $comparison
130+ -- * Where to go next?
@@ -138,12 +142,43 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
139-- $streams
143-- $streams
141−-- Streamly provides many different stream types depending on the desired
145+-- The way a list represents a sequence of pure values, a stream represents a
142−-- composition style. The simplest type is 'Stream', @Stream a@ represents a
146+-- sequence of monadic actions. The monadic stream API offered by Streamly is
143−-- stream of elements of type @a@ in IO monad. Streams are very much like lists
147+-- very close to the Haskell "Prelude" pure lists' API, it can be considered as a
148+-- natural extension of lists to monadic actions. Streamly streams provide
149+-- concurrent composition and merging of streams. It can be considered as a
150+-- concurrent list transformer. In contrast to the "Prelude" lists, merging or
151+-- appending streams of arbitrary length is scalable and inexpensive.
153+-- The basic stream type is 'Stream', it represents a sequence of IO actions,
154+-- and is a 'Monad'. The type 'StreamT' is a monad transformer that can
155+-- represent a sequence of actions in an arbitrary monad. The type 'Stream' is
156+-- in fact a synonym for @StreamT IO@. There are a few more types similar to
157+-- 'StreamT' that all represent streams and differ only in the 'Semigroup',
158+-- 'Applicative' and 'Monad' compositions of the streams. 'Stream' and
159+-- 'Costream' types compose serially whereas 'Coparallel' and 'Parallel' types
160+-- compose concurrently. All these types can be freely inter-converted using
161+-- type combinators without any cost. You can freely switch to any type of
162+-- composition at any point in the program. When no type annotation or
163+-- explicit stream type combinators are used, the default stream type is
164+-- inferred as 'Stream'.
166+-- The 'Stream' monad is almost a drop in replacement for the 'IO' monad, IO
167+-- monad is a special case of the 'Stream' monad; IO monad represents a single
168+-- IO action whereas the 'Stream' monad represents a sequence of IO actions.
169+-- The only change you need to make to go from 'IO' to 'Stream' is to use
170+-- 'runStream' to run the monad and to prefix the IO actions with 'once'. You
171+-- can also use 'liftIO' instead of 'once'. In fact, if you use liftIO you can
172+-- switch from 'Stream' to IO monad by simply removing the 'runStream'
173+-- function; no other changes are needed unless you have used some stream
174+-- specific composition or combinators.
176+-- Similarly, the 'Stream' type is almost a drop in replacement for pure lists,
177+-- pure lists are a special case of monadic streams. If you use 'nil' in place
178+-- of '[]' and '|:' in place ':' you can replace a list with a 'Stream'. The
144−-- the only difference is that they are lists of monadic actions instead of
179+-- only difference is that the elements must be monadic type and to operate on
145−-- pure values. We can call them monadic lists. You can apply all the
180+-- the streams we must use the corresponding functions from "Streamly.Prelude"
146−-- constructs related to lists to monadic streams as well.
181+-- instead of using the base "Prelude".
148-- $monadtransformers
183-- $monadtransformers
@@ -161,22 +196,31 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
161-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
196-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
164−-- > runStream $ liftIO $ putStrLn "Hello world!"
199+-- > 'runStream' $ liftIO $ putStrLn "Hello world!"
165-- Hello world!
200-- Hello world!
166−-- > runStream $ lift $ putStrLn "Hello world!"
201+-- > 'runStream' $ lift $ putStrLn "Hello world!"
167-- Hello world!
202-- Hello world!
171-- $generating
206-- $generating
208+-- We will assume the following imports in this tutorial. Go ahead, fire up a
209+-- GHCi session and import these lines to start playing.
212+-- > import "Streamly"
213+-- > import "Streamly.Prelude" (|:)
214+-- > import qualified "Streamly.Prelude" as S
173-- 'nil' represents an empty stream and 'consM' or its operator form '|:' adds
217-- 'nil' represents an empty stream and 'consM' or its operator form '|:' adds
174-- a monadic action at the head of the stream.
218-- a monadic action at the head of the stream.
177−-- > toList nil
221+-- > S.'toList' S.'nil'
179−-- > toList $ getLine |: getLine |: nil
223+-- > S.'toList' $ 'getLine' |: 'getLine' |: S.'nil'
182-- ["hello","world"]
226-- ["hello","world"]
@@ -186,9 +230,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
186-- singleton stream from a monadic action use 'once'.
230-- singleton stream from a monadic action use 'once'.
189−-- > toList $ pure 1
233+-- > S.'toList' $ 'pure' 1
191−-- > toList $ once getLine
235+-- > S.'toList' $ S.'once' 'getLine'
193-- ["hello"]
237-- ["hello"]
@@ -197,9 +241,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
197-- 'fromFoldable' which is equivalent to a fold using 'cons' and 'nil':
241-- 'fromFoldable' which is equivalent to a fold using 'cons' and 'nil':
200−-- > toList $ fromFoldable [1..3]
244+-- > S.'toList' $ S.'fromFoldable' [1..3]
201-- [1,2,3]
245-- [1,2,3]
202−-- > toList $ Prelude.foldr cons nil [1..3]
246+-- > S.'toList' $ 'Prelude.foldr' S.'cons' S.'nil' [1..3]
203-- [1,2,3]
247-- [1,2,3]
@@ -207,7 +251,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
207-- right fold using 'consM' and 'nil':
251-- right fold using 'consM' and 'nil':
210−-- > runStream $ Prelude.foldr (|:) nil [putStr "Hello ", putStrLn "world!"]
254+-- > 'runStream' $ 'Prelude.foldr' ('|:') S.'nil' ['putStr' "Hello ", 'putStrLn' "world!"]
211-- Hello world!
255-- Hello world!
@@ -236,7 +280,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
236-- forever:
280-- forever:
239−-- > runStream $ S.repeatM getLine & S.mapM putStrLn
283+-- > 'runStream' $ S.'repeatM' getLine & S.'mapM' putStrLn
242-- The following code snippet reads lines from standard input, filters blank
286-- The following code snippet reads lines from standard input, filters blank
@@ -244,19 +288,19 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
244-- numbers them and prints them:
288-- numbers them and prints them:
247−-- import Streamly
291+-- import "Streamly"
248−-- import qualified Streamly.Prelude as S
292+-- import qualified "Streamly.Prelude" as S
249-- import Data.Char (toUpper)
293-- import Data.Char (toUpper)
250-- import Data.Function ((&))
294-- import Data.Function ((&))
252−-- main = runStream $
296+-- main = 'runStream' $
253−-- S.repeatM getLine
297+-- S.'repeatM' getLine
254−-- & S.filter (not . null)
298+-- & S.'filter' (not . null)
255−-- & S.drop 1
299+-- & S.'drop' 1
256−-- & S.take 2
300+-- & S.'take' 2
257-- & fmap (map toUpper)
301-- & fmap (map toUpper)
258−-- & S.zipWith (\\n s -> show n ++ " " ++ s) (S.fromFoldable [1..])
302+-- & S.'zipWith' (\\n s -> show n ++ " " ++ s) (S.'fromFoldable' [1..])
259−-- & S.mapM putStrLn
303+-- & S.'mapM' putStrLn
262-- $semigroup
306-- $semigroup
@@ -279,10 +323,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
281-- import "Streamly"
325-- import "Streamly"
282−-- import "Streamly.Prelude"(once)
326+-- import "Streamly.Prelude" (nil)
327+-- import qualified "Streamly.Prelude" as S
283-- import Control.Concurrent
328-- import Control.Concurrent
285−-- delay n = once $ do
330+-- delay n = S.'once' $ do
286-- threadDelay (n * 1000000)
331-- threadDelay (n * 1000000)
287-- tid \<- myThreadId
332-- tid \<- myThreadId
288-- putStrLn (show tid ++ ": Delay " ++ show n)
333-- putStrLn (show tid ++ ": Delay " ++ show n)
@@ -477,7 +522,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
479-- main = 'runStream' . 'coparallely' $ traced (sqrt 9) '<>' traced (sqrt 16) '<>' traced (sqrt 25)
524-- main = 'runStream' . 'coparallely' $ traced (sqrt 9) '<>' traced (sqrt 16) '<>' traced (sqrt 25)
480−-- where traced m = once (myThreadId >>= print) >> return m
525+-- where traced m = S.'once' (myThreadId >>= print) >> return m
483-- ThreadId 40
528-- ThreadId 40
@@ -538,7 +583,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
540-- import "Streamly"
585-- import "Streamly"
541−-- import Streamly.Prelude (once)
586+-- import qualified Streamly.Prelude as S
542-- import Network.HTTP.Simple
587-- import Network.HTTP.Simple
544-- main = 'runStream' . 'parallely' $ google \<> bing \<> duckduckgo
589-- main = 'runStream' . 'parallely' $ google \<> bing \<> duckduckgo
@@ -546,7 +591,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
546-- google = get "https://www.google.com/search?q=haskell"
591-- google = get "https://www.google.com/search?q=haskell"
547-- bing = get "https://www.bing.com/search?q=haskell"
592-- bing = get "https://www.bing.com/search?q=haskell"
548-- duckduckgo = get "https://www.duckduckgo.com/?q=haskell"
593-- duckduckgo = get "https://www.duckduckgo.com/?q=haskell"
549−-- get s = once (httpNoBody (parseRequest_ s) >> putStrLn (show s))
594+-- get s = S.'once' (httpNoBody (parseRequest_ s) >> putStrLn (show s))
552-- In the following example we can see that the fairly parallel composition
597-- In the following example we can see that the fairly parallel composition
@@ -611,15 +656,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
613-- import "Streamly"
658-- import "Streamly"
614−-- import "Streamly.Prelude"
659+-- import qualified "Streamly.Prelude" as S
615-- import Control.Concurrent
660-- import Control.Concurrent
617-- main = do
662-- main = do
618−-- runStream $ parallely $ foldMap delay [1..10]
663+-- 'runStream' $ 'parallely' $ foldMap delay [1..10]
619−-- runStream $ 'foldWith' parallel (map delay [1..10])
664+-- 'runStream' $ 'foldWith' 'parallel' (map delay [1..10])
620−-- runStream $ 'foldMapWith' parallel delay [1..10]
665+-- 'runStream' $ 'foldMapWith' 'parallel' delay [1..10]
621−-- runStream $ 'forEachWith' parallel [1..10] delay
666+-- 'runStream' $ 'forEachWith' 'parallel' [1..10] delay
622−-- where delay n = once $ threadDelay (n * 1000000) >> print n
667+-- where delay n = S.'once' $ threadDelay (n * 1000000) >> print n
625-- $nesting
670-- $nesting
@@ -665,10 +710,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
667-- import "Streamly"
712-- import "Streamly"
668−-- import "Streamly.Prelude"
713+-- import qualified "Streamly.Prelude" as S
670-- main = 'runStream' $ do
715-- main = 'runStream' $ do
671−-- x <- 'fromFoldable' [3,2,1]
716+-- x <- S.'fromFoldable' [3,2,1]
672-- delay x
717-- delay x
@@ -687,8 +732,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
689-- import "Streamly"
734-- import "Streamly"
735+-- import qualified "Streamly.Prelude" as S
691−-- main = 'runStream' $ forever $ once getLine >>= once . putStrLn
737+-- main = 'runStream' $ forever $ S.once getLine >>= S.once . putStrLn
694-- When multiple streams are composed using this style they nest in a DFS
740-- When multiple streams are composed using this style they nest in a DFS
@@ -698,12 +744,12 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
700-- import "Streamly"
746-- import "Streamly"
701−-- import "Streamly.Prelude"
747+-- import qualified "Streamly.Prelude" as S
703-- main = 'runStream' $ do
749-- main = 'runStream' $ do
704−-- x <- 'fromFoldable' [1,2]
750+-- x <- S.'fromFoldable' [1,2]
705−-- y <- 'fromFoldable' [3,4]
751+-- y <- S.'fromFoldable' [3,4]
706−-- once $ putStrLn $ show (x, y)
752+-- S.'once' $ putStrLn $ show (x, y)
@@ -757,12 +803,12 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
759-- import "Streamly"
805-- import "Streamly"
760−-- import "Streamly.Prelude"
806+-- import qualified "Streamly.Prelude" as S
762-- main = 'runStream' . 'coparallely' $ do
808-- main = 'runStream' . 'coparallely' $ do
763−-- x <- 'fromFoldable' [1,2]
809+-- x <- S.'fromFoldable' [1,2]
764−-- y <- 'fromFoldable' [3,4]
810+-- y <- S.'fromFoldable' [3,4]
765−-- once $ putStrLn $ show (x, y)
811+-- S.'once' $ putStrLn $ show (x, y)
@@ -782,12 +828,12 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
784-- import "Streamly"
830-- import "Streamly"
785−-- import "Streamly.Prelude"
831+-- import qualified "Streamly.Prelude" as S
787-- main = 'runStream' . 'costreamly' $ do
833-- main = 'runStream' . 'costreamly' $ do
788−-- x <- 'fromFoldable' [1,2]
834+-- x <- S.'fromFoldable' [1,2]
789−-- y <- 'fromFoldable' [3,4]
835+-- y <- S.'fromFoldable' [3,4]
790−-- once $ putStrLn $ show (x, y)
836+-- S.once $ putStrLn $ show (x, y)
@@ -808,10 +854,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
810-- import "Streamly"
856-- import "Streamly"
811−-- import "Streamly.Prelude"
857+-- import qualified "Streamly.Prelude" as S
813-- main = 'runStream' . 'parallely' $ do
859-- main = 'runStream' . 'parallely' $ do
814−-- x <- 'fromFoldable' [3,2,1]
860+-- x <- S.'fromFoldable' [3,2,1]
815-- delay x
861-- delay x
@@ -829,20 +875,20 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
831-- import "Streamly"
877-- import "Streamly"
832−-- import "Streamly.Prelude"
878+-- import qualified "Streamly.Prelude" as S
834-- composed :: 'IsStream' t => t m a
880-- composed :: 'IsStream' t => t m a
835-- composed = do
881-- composed = do
836-- sz <- sizes
882-- sz <- sizes
837-- cl <- colors
883-- cl <- colors
838-- sh <- shapes
884-- sh <- shapes
839−-- once $ putStrLn $ show (sz, cl, sh)
885+-- S.'once' $ putStrLn $ show (sz, cl, sh)
843−-- sizes = 'fromFoldable' [1, 2, 3]
889+-- sizes = S.'fromFoldable' [1, 2, 3]
844−-- colors = 'fromFoldable' ["red", "green", "blue"]
890+-- colors = S.'fromFoldable' ["red", "green", "blue"]
845−-- shapes = 'fromFoldable' ["triangle", "square", "circle"]
891+-- shapes = S.'fromFoldable' ["triangle", "square", "circle"]
848-- Now we can interpret this in whatever way we want:
894-- Now we can interpret this in whatever way we want:
@@ -865,8 +911,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
867-- import "Streamly"
913-- import "Streamly"
914+-- import qualified "Streamly.Prelude" as S
869−-- main = ('toList' $ fmap show $ 'fromFoldable' [1..10]) >>= print
916+-- main = (S.'toList' $ fmap show $ S.'fromFoldable' [1..10]) >>= print
872-- Also see the 'mapM' and 'sequence' functions for mapping actions, in the
919-- Also see the 'mapM' and 'sequence' functions for mapping actions, in the
@@ -883,14 +930,14 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
885-- import "Streamly"
932-- import "Streamly"
886−-- import "Streamly.Prelude"
933+-- import qualified "Streamly.Prelude" as S
887-- import Control.Concurrent
934-- import Control.Concurrent
889-- s1 = d 1 <> d 2
936-- s1 = d 1 <> d 2
890-- s2 = d 3 <> d 4
937-- s2 = d 3 <> d 4
891-- d n = delay n >> return n
938-- d n = delay n >> return n
893−-- main = ('toList' . 'streamly' $ (,) \<$> s1 \<*> s2) >>= print
940+-- main = (S.'toList' . 'streamly' $ (,) \<$> s1 \<*> s2) >>= print
896-- ThreadId 36: Delay 1
943-- ThreadId 36: Delay 1
@@ -906,7 +953,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
906-- order but since it is serial it takes a total of 17 seconds:
953-- order but since it is serial it takes a total of 17 seconds:
909−-- main = ('toList' . 'costreamly' $ (,) \<$> s1 \<*> s2) >>= print
956+-- main = (S.'toList' . 'costreamly' $ (,) \<$> s1 \<*> s2) >>= print
912-- ThreadId 36: Delay 1
959-- ThreadId 36: Delay 1
@@ -922,7 +969,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
922-- of 10 seconds (1 + 2 + 3 + 4):
969-- of 10 seconds (1 + 2 + 3 + 4):
925−-- main = ('toList' . 'coparallely' $ (,) \<$> s1 \<*> s2) >>= print
972+-- main = (S.'toList' . 'coparallely' $ (,) \<$> s1 \<*> s2) >>= print
928-- ThreadId 34: Delay 1
975-- ThreadId 34: Delay 1
@@ -938,7 +985,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
938-- therefore takes a total of 10 seconds (1 + 2 + 3 + 4):
985-- therefore takes a total of 10 seconds (1 + 2 + 3 + 4):
941−-- main = ('toList' . 'parallely' $ (,) \<$> s1 \<*> s2) >>= print
988+-- main = (S.'toList' . 'parallely' $ (,) \<$> s1 \<*> s2) >>= print
944-- ThreadId 34: Delay 1
991-- ThreadId 34: Delay 1
@@ -987,14 +1034,14 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
989-- import "Streamly"
1036-- import "Streamly"
990−-- import "Streamly.Prelude"
1037+-- import qualified "Streamly.Prelude" as S
991-- import Control.Concurrent
1038-- import Control.Concurrent
993-- d n = delay n >> return n
1040-- d n = delay n >> return n
994-- s1 = 'streamly' $ d 1 <> d 2
1041-- s1 = 'streamly' $ d 1 <> d 2
995-- s2 = 'streamly' $ d 3 <> d 4
1042-- s2 = 'streamly' $ d 3 <> d 4
997−-- main = ('toList' . 'zipStreamly' $ (,) \<$> s1 \<*> s2) >>= print
1044+-- main = (S.'toList' . 'zipStreamly' $ (,) \<$> s1 \<*> s2) >>= print
1000-- This takes total 10 seconds to zip, which is (1 + 2 + 3 + 4) since
1047-- This takes total 10 seconds to zip, which is (1 + 2 + 3 + 4) since
@@ -1017,7 +1064,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1019-- import "Streamly"
1066-- import "Streamly"
1020−-- import "Streamly.Prelude"
1067+-- import qualified "Streamly.Prelude" as S
1021-- import Control.Concurrent
1068-- import Control.Concurrent
1022-- import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
1069-- import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
@@ -1027,7 +1074,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1028-- main = do
1075-- main = do
1029-- hSetBuffering stdout LineBuffering
1076-- hSetBuffering stdout LineBuffering
1030−-- ('toList' . 'zipParallely' $ (,) \<$> s1 \<*> s2) >>= print
1077+-- (S.'toList' . 'zipParallely' $ (,) \<$> s1 \<*> s2) >>= print
1033-- This takes 7 seconds to zip, which is max (1,3) + max (2,4) because 1 and 3
1080-- This takes 7 seconds to zip, which is max (1,3) + max (2,4) because 1 and 3
@@ -1059,11 +1106,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1061-- import "Streamly"
1108-- import "Streamly"
1062−-- import "Streamly.Prelude" (toList)
1109+-- import qualified "Streamly.Prelude" as S
1063-- import Data.List (sum)
1110-- import Data.List (sum)
1065-- main = do
1112-- main = do
1066−-- z \<- 'toList'
1113+-- z \<- S.'toList'
1067-- $ 'streamly' -- Serial monadic processing (sqrt below)
1114-- $ 'streamly' -- Serial monadic processing (sqrt below)
1069-- x2 \<- 'forEachWith' 'coparallel' [1..100] $ -- Concurrent @"for"@ loop
1116-- x2 \<- 'forEachWith' 'coparallel' [1..100] $ -- Concurrent @"for"@ loop
@@ -1252,10 +1299,22 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1253-- $comparison
1300-- $comparison
1255−-- Streamly unifies non-determinism, streaming, concurrency and FRP
1302+-- List transformers and logic programming monads also provide a product style
1256−-- functionality that is otherwise covered by several disparate packages, and
1303+-- composition similar to streamly, however streamly generalizes it with the
1257−-- it does that with a surprisingly concise API. Here is a list of popular and
1304+-- time dimension; allowing streams to be composed in an asynchronous and
1258−-- well-known packages in all these areas:
1305+-- concurrent fashion in many different ways. It also provides multiple
1306+-- alternative ways of composing streams e.g. serial, interleaved or
1309+-- This seemingly simple addition of asynchronicity and concurrency to product
1310+-- style streaming composition unifies a number of disparate abstractions into
1311+-- one powerful, concise and elegant abstraction. A wide variety of
1312+-- programming problems can be solved elegantly with this abstraction. In
1313+-- particular, it unifies three major programming domains namely
1314+-- non-deterministic (logic) programming, concurrent programming and functional
1315+-- reactive programming. In other words, you can do everything with this one
1316+-- abstraction that you could do with the popular libraries listed under these
1317+-- categories in the list below.
1261-- +-----------------+----------------+
1320-- +-----------------+----------------+
@@ -1318,3 +1377,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1318-- similar to @dunai@. dunai was designed from a FRP perspective and streamly
1377-- similar to @dunai@. dunai was designed from a FRP perspective and streamly
1319-- was originally designed from a concurrency perspective. However, both have
1378-- was originally designed from a concurrency perspective. However, both have
1320-- similarity at the core.
1379-- similarity at the core.
1383+-- * Read the documentation of "Streamly" module
1384+-- * Read the documentation of "Streamly.Prelude" module
1385+-- * See the examples in the "examples" directory of the package
1386+-- * See the tests in the "test" directory of the package