on master help sign in
update tutorial
f3dd0a9Harendra Kumar
src/Streamly/Tutorial.hs+277−214
@@ -75,16 +75,16 @@ module Streamly.Tutorial
75 -- ** Monad
75 -- ** Monad
76 -- $monad
76 -- $monad
77
77
78− -- *** Serial Composition ('StreamT')
78+ -- *** Serial Nesting ('streamly')
79 -- $regularSerial
79 -- $regularSerial
80
80
81− -- *** Async Composition ('AsyncT')
81+ -- *** Serial Interleaved Nesting ('costreamly')
82− -- $concurrentNesting
83−
84− -- *** Interleaved Composition ('InterleavedT')
85 -- $interleavedNesting
82 -- $interleavedNesting
86
83
87− -- *** Fair Concurrent Composition ('ParallelT')
84+ -- *** Left Biased Concurrent Nesting ('coparallely')
85+ -- $concurrentNesting
86+
87+ -- *** Round Robin Concurrent Nesting ('parallely')
88 -- $fairlyConcurrentNesting
88 -- $fairlyConcurrentNesting
89
89
90 -- *** Exercise
90 -- *** Exercise
@@ -108,6 +108,9 @@ module Streamly.Tutorial
108 -- * Summary of Compositions
108 -- * Summary of Compositions
109 -- $compositionSummary
109 -- $compositionSummary
110
110
111+ -- * Monad transformers
112+ -- $monadtransformers
113+
111 -- * Concurrent Programming
114 -- * Concurrent Programming
112 -- $concurrent
115 -- $concurrent
113
116
@@ -137,11 +140,33 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
137--
140--
138-- Streamly provides many different stream types depending on the desired
141-- Streamly provides many different stream types depending on the desired
139-- composition style. The simplest type is 'Stream', @Stream a@ represents a
142-- composition style. The simplest type is 'Stream', @Stream a@ represents a
140−-- stream of elements of type @a@ in IO monad. To represent streams in an
143+-- stream of elements of type @a@ in IO monad. Streams are very much like lists
144+-- the only difference is that they are lists of monadic actions instead of
145+-- pure values. We can call them monadic lists. You can apply all the
146+-- constructs related to lists to monadic streams as well.
147+
148+-- $monadtransformers
149+--
141−-- arbitrary monad use the more general monad transformer type 'StreamT'.
150+-- To represent streams in an arbitrary monad use the more general monad
151+-- transformer types for example the monad transformer type corresponding to
142−-- @StreamT m a@ represents a stream of values of type 'a' in some underlying
152+-- the 'Stream' type is 'StreamT'. @StreamT m a@ represents a stream of values
143−-- monad 'm'. For example, @StreamT IO Int@ is a stream of 'Int' in 'IO' monad.
153+-- of type 'a' in some underlying monad 'm'. For example, @StreamT IO Int@ is a
144−-- In fact, the type 'Stream' is a synonym for @StreamT IO@.
154+-- stream of 'Int' in 'IO' monad. In fact, the type 'Stream' is a synonym for
155+-- @StreamT IO@.
156+--
157+-- Similarly we have monad transformer types for other stream types as well viz.
158+-- 'CostreamT', 'CoparallelT' and 'ParallelT'.
159+--
160+-- To lift a value from an underlying monad in a monad transformer stack into a
161+-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
162+--
163+-- @
164+-- > runStream $ liftIO $ putStrLn "Hello world!"
165+-- Hello world!
166+-- > runStream $ lift $ putStrLn "Hello world!"
167+-- Hello world!
168+-- @
169+--
145
170
146-- $generating
171-- $generating
147--
172--
@@ -168,26 +193,18 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
168-- ["hello"]
193-- ["hello"]
169-- @
194-- @
170--
195--
171−-- To lift a value from an underlying monad in a monad transformer stack into a
172−-- singleton stream use 'lift' and to lift from an IO action use 'liftIO'.
173−--
174−-- @
175−-- > runStream $ liftIO $ putStrLn "Hello world!"
176−-- Hello world!
177−-- > runStream $ lift $ putStrLn "Hello world!"
178−-- Hello world!
179−-- @
180−--
181−-- To create a stream of pure values from a 'Foldable' container use
196+-- To create a stream from pure values in a 'Foldable' container use
182−-- 'fromFoldable'.
197+-- 'fromFoldable' which is equivalent to a fold using 'cons' and 'nil':
183--
198--
184-- @
199-- @
185-- > toList $ fromFoldable [1..3]
200-- > toList $ fromFoldable [1..3]
186-- [1,2,3]
201-- [1,2,3]
202+-- > toList $ Prelude.foldr cons nil [1..3]
203+-- [1,2,3]
187-- @
204-- @
188--
205--
189-- To create a stream from monadic actions in a 'Foldable' container just use a
206-- To create a stream from monadic actions in a 'Foldable' container just use a
190−-- right fold:
207+-- right fold using 'consM' and 'nil':
191--
208--
192-- @
209-- @
193-- > runStream $ Prelude.foldr (|:) nil [putStr "Hello ", putStrLn "world!"]
210-- > runStream $ Prelude.foldr (|:) nil [putStr "Hello ", putStrLn "world!"]
@@ -213,9 +230,18 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
213-- mapping functions over the elements, filtering elements from the stream or
230-- mapping functions over the elements, filtering elements from the stream or
214-- folding all the elements in the stream into a single value. Streamly streams
231-- folding all the elements in the stream into a single value. Streamly streams
215-- are exactly like lists and you can perform all the transformations in the
232-- are exactly like lists and you can perform all the transformations in the
216−-- same way as you would on lists. For example the following snippet reads
233+-- same way as you would on lists.
234+--
235+-- Here is a simple console echo program that just echoes every input line,
236+-- forever:
237+--
238+-- @
239+-- > runStream $ cycle1 (S.once getLine) & S.mapM putStrLn
240+-- @
241+--
217−-- lines from standard input, filters blank lines, drops the first non-blank
242+-- The following code snippet reads lines from standard input, filters blank
243+-- lines, drops the first non-blank line, takes the next two, up cases them,
218−-- line, takes the next two, up cases them, numbers them and prints them:
244+-- numbers them and prints them:
219--
245--
220-- @
246-- @
221-- import Streamly
247-- import Streamly
@@ -253,9 +279,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
253--
279--
254-- @
280-- @
255-- import "Streamly"
281-- import "Streamly"
282+-- import "Streamly.Prelude"(once)
256-- import Control.Concurrent
283-- import Control.Concurrent
257--
284--
258−-- delay n = liftIO $ do
285+-- delay n = once $ do
259-- threadDelay (n * 1000000)
286-- threadDelay (n * 1000000)
260-- tid \<- myThreadId
287-- tid \<- myThreadId
261-- putStrLn (show tid ++ ": Delay " ++ show n)
288-- putStrLn (show tid ++ ": Delay " ++ show n)
@@ -371,8 +398,8 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
371-- 'Coparallel' type annotation for the stream type to acheive the same effect.
398-- 'Coparallel' type annotation for the stream type to acheive the same effect.
372--
399--
373-- When two streams with multiple elements are combined in this manner, the
400-- When two streams with multiple elements are combined in this manner, the
374−-- monadic actions in the two streams can be performed concurrently /when
401+-- monadic actions in the two streams can be performed concurrently
375−-- needed/ with a bias towards the left stream i.e. actions from the left
402+-- /when needed/ with a bias towards the left stream i.e. actions from the left
376-- stream are performed sequentially, if the left stream blocks or cannot
403-- stream are performed sequentially, if the left stream blocks or cannot
377-- generate enough output to keep the consumer of the stream busy then it can
404-- generate enough output to keep the consumer of the stream busy then it can
378-- execute actions from the right stream concurrently. The outputs of the
405-- execute actions from the right stream concurrently. The outputs of the
@@ -597,39 +624,50 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
597
624
598-- $nesting
625-- $nesting
599--
626--
600−-- The previous section discussed ways to merge the elements of two streams
627+-- Till now we discussed ways to apply transformations on a stream or to merge
601−-- without doing any transformation on them. In this section we will explore
628+-- streams together to create another stream. We mentioned earlier that
602−-- how to transform streams using 'Functor', 'Applicative' or 'Monad' style
629+-- transforming a stream is similar to a @for@ loop in the imperative paradigm.
603−-- compositions. The applicative and monad compositions of all 'IsStream' types
630+-- We will now discuss the concept of a nested composition of streams which is
604−-- behave exactly the same way as a list transformer. For simplicity of
631+-- analogous to nested @for@ loops in the imperative paradigm. Functional
605−-- illustration we are using streams of pure values in the following examples.
632+-- programmers call this style of composition a list transformer or @ListT@.
606−-- However, the real application of streams arises when these streams are
633+-- Logic programmers call it a logic monad or non-deterministic composition,
607−-- generated using monadic actions.
634+-- but for ordinary imperative minded people like me it is easier to think in
608−
635+-- terms of good old nested @for@ loops.
636+--
609-- $monad
637-- $monad
610--
638--
611−-- In functional programmer's parlance the 'Monad' instance of 'IsStream'
639+-- In functional programmer's parlance the 'Monad' instances of different
612−-- types implement non-determinism, exploring all possible combination of
640+-- 'IsStream' types implement non-determinism, exploring all possible
613−-- choices from both the streams. From an imperative programmer's point of view
641+-- combination of choices from both the streams. From an imperative
614−-- it behaves like nested loops i.e. for each element in the first stream and
642+-- programmer's point of view it behaves like nested loops i.e. for each
615−-- for each element in the second stream apply the body of the loop. If you are
643+-- element in the first stream and for each element in the second stream
616−-- familiar with list transformer this behavior is exactly the same as that of
644+-- execute the body of the loop.
617−-- a list transformer.
645+--
618−--
646+-- The 'Monad' instances of 'Stream', 'Costream', 'Coparallel' and 'Parallel'
619−-- Just like we saw in sum style compositions earlier, monadic composition also
647+-- stream types support different flavors of nested looping. In other words,
620−-- has multiple variants each of which exactly corresponds to one of the sum
648+-- they are all variants of list transformer. The nesting behavior of these
621−-- style composition variant.
649+-- types exactly correspond to the way they merge streams as we discussed in
650+-- the previous section.
651+--
622
652
623-- $regularSerial
653-- $regularSerial
624--
654--
625−-- When we interpret the monadic composition as 'StreamT' we get a standard
655+-- The 'Monad' composition of 'Stream' type behaves like a standard list
626−-- list transformer like serial composition.
656+-- transformer. This is the default when we do not use an explicit type
657+-- combinator. However, the 'streamly' type combinator can be used to switch to
658+-- this style of composition. We will see how this style of composition works
659+-- in the following examples.
660+--
661+-- Let's start with an example with a simple @for@ loop without any nesting.
662+-- For simplicity of illustration we are using streams of pure values in all
663+-- the examples. However, the streams could also be made of monadic actions
664+-- instead.
627--
665--
628-- @
666-- @
629-- import "Streamly"
667-- import "Streamly"
630-- import "Streamly.Prelude"
668-- import "Streamly.Prelude"
631--
669--
632−-- main = 'runStreamT' $ do
670+-- main = 'runStream' $ do
633-- x <- 'fromFoldable' [3,2,1]
671-- x <- 'fromFoldable' [3,2,1]
634-- delay x
672-- delay x
635-- @
673-- @
@@ -639,34 +677,33 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
639-- ThreadId 30: Delay 1
677-- ThreadId 30: Delay 1
640-- @
678-- @
641--
679--
642−-- As you can see the code after the @fromFoldable@ statement is run three
680+-- As you can see, the code after the @fromFoldable@ statement is run three
643−-- times, once for each value of @x@. All the three iterations are serial and
681+-- times, once for each value of @x@ drawn from the stream. All the three
644−-- run in the same thread one after another. When compared to imperative
682+-- iterations are serial and run in the same thread one after another. In
645−-- programming, this can also be viewed as a @for@ loop with three iterations.
683+-- imperative terms this is equivalent to a @for@ loop with three iterations.
646--
684--
647-- A console echo loop copying standard input to standard output can simply be
685-- A console echo loop copying standard input to standard output can simply be
648-- written like this:
686-- written like this:
649--
687--
650-- @
688-- @
651-- import "Streamly"
689-- import "Streamly"
652−-- import Data.Semigroup (cycle1)
653--
690--
654−-- main = 'runStreamT' $ cycle1 (liftIO getLine) >>= liftIO . putStrLn
691+-- main = 'runStream' $ forever $ once getLine >>= once . putStrLn
655-- @
692-- @
656--
693--
657-- When multiple streams are composed using this style they nest in a DFS
694-- When multiple streams are composed using this style they nest in a DFS
658−-- manner i.e. nested iterations of an iteration are executed before we proceed
695+-- manner i.e. nested iterations of a loop are executed before we proceed to
659−-- to the next iteration at higher level. This behaves just like nested @for@
696+-- the next iteration of the parent loop. This behaves just like nested @for@
660-- loops in imperative programming.
697-- loops in imperative programming.
661--
698--
662-- @
699-- @
663-- import "Streamly"
700-- import "Streamly"
664-- import "Streamly.Prelude"
701-- import "Streamly.Prelude"
665--
702--
666−-- main = 'runStreamT' $ do
703+-- main = 'runStream' $ do
667-- x <- 'fromFoldable' [1,2]
704-- x <- 'fromFoldable' [1,2]
668-- y <- 'fromFoldable' [3,4]
705-- y <- 'fromFoldable' [3,4]
669−-- liftIO $ putStrLn $ show (x, y)
706+-- once $ putStrLn $ show (x, y)
670-- @
707-- @
671-- @
708-- @
672-- (1,3)
709-- (1,3)
@@ -675,23 +712,27 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
675-- (2,4)
712-- (2,4)
676-- @
713-- @
677--
714--
678−-- You will also notice that this is the monadic equivalent of the sum style
715+-- Notice that this is analogous to merging streams of type 'Stream' or merging
679−-- composition using '<>'.
716+-- streams using 'splice'.
680
717
681-- $concurrentNesting
718-- $concurrentNesting
682--
719--
683−-- When we interpret the monadic composition as 'AsyncT' we get a /concurrent/
720+-- The 'Monad' composition of 'Coparallel' type can perform the iterations of a
684−-- list-transformer like composition. Multiple monadic continuations (or loop
685−-- iterations) may be started concurrently. Concurrency is demand driven
721+-- loop concurrently. Concurrency is demand driven i.e. more concurrent
686−-- i.e. more concurrent iterations are started only if the previous iterations
722+-- iterations are started only if the previous iterations are not able to
687−-- are not able to produce enough output for the consumer of the output stream.
723+-- produce enough output for the consumer of the output stream. This works
724+-- exactly the same way as the merging of two streams 'coparallely' works.
688−-- This is the concurrent version of 'StreamT'.
725+-- This is the concurrent analogue of 'Stream' style monadic composition.
726+--
727+-- The 'coparallely' type combinator can be used to switch to this
728+-- style of composition. Alternatively, a type annotation can be used to
729+-- specify the type of the stream as 'Coparallel'.
689--
730--
690-- @
731-- @
691-- import "Streamly"
732-- import "Streamly"
692-- import "Streamly.Prelude"
733-- import "Streamly.Prelude"
693--
734--
694−-- main = 'runAsyncT' $ do
735+-- main = 'runStream' . 'coparallely' $ do
695-- x <- 'fromFoldable' [3,2,1]
736-- x <- 'fromFoldable' [3,2,1]
696-- delay x
737-- delay x
697-- @
738-- @
@@ -701,27 +742,27 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
701-- ThreadId 38: Delay 3
742-- ThreadId 38: Delay 3
702-- @
743-- @
703--
744--
704−-- As you can see the code after the @fromFoldable'@ statement is run three
745+-- As you can see the code after the @fromFoldable@ statement is run three
705-- times, once for each value of @x@. All the three iterations are concurrent
746-- times, once for each value of @x@. All the three iterations are concurrent
706-- and run in different threads. The iteration with least delay finishes first.
747-- and run in different threads. The iteration with least delay finishes first.
707-- When compared to imperative programming, this can be viewed as a @for@ loop
748-- When compared to imperative programming, this can be viewed as a @for@ loop
708-- with three concurrent iterations.
749-- with three concurrent iterations.
709--
750--
710−-- Concurrency is demand driven just as in the case of '<|'. When multiple
751+-- Concurrency is demand driven just as in the case of 'coparallel' merging.
711−-- streams are composed using this style the iterations are triggered in a DFS
752+-- When multiple streams are composed using this style, the iterations are
712−-- manner just like 'StreamT' i.e. nested iterations are executed before we
753+-- triggered in a DFS manner just like 'Stream' i.e. nested iterations are
713−-- proceed to the next iteration at higher level. However, unlike 'StreamT'
754+-- executed before we proceed to the next iteration at higher level. However,
714−-- more than one iterations may be started concurrently, and based on the
755+-- unlike 'Stream' more than one iterations may be started concurrently based
715−-- demand from the consumer.
756+-- on the demand from the consumer.
716--
757--
717-- @
758-- @
718-- import "Streamly"
759-- import "Streamly"
719-- import "Streamly.Prelude"
760-- import "Streamly.Prelude"
720--
761--
721−-- main = 'runAsyncT' $ do
762+-- main = 'runStream' . 'coparallely' $ do
722-- x <- 'fromFoldable' [1,2]
763-- x <- 'fromFoldable' [1,2]
723-- y <- 'fromFoldable' [3,4]
764-- y <- 'fromFoldable' [3,4]
724−-- liftIO $ putStrLn $ show (x, y)
765+-- once $ putStrLn $ show (x, y)
725-- @
766-- @
726-- @
767-- @
727-- (1,3)
768-- (1,3)
@@ -729,25 +770,24 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
729-- (2,3)
770-- (2,3)
730-- (2,4)
771-- (2,4)
731-- @
772-- @
732−--
733−-- You will notice that this is the monadic equivalent of the '<|' style
734−-- sum composition. The same caveats apply to this as the '<|' operation.
735
773
736-- $interleavedNesting
774-- $interleavedNesting
737--
775--
738−-- When we interpret the monadic composition as 'InterleavedT' we get a serial
776+-- The 'Monad' composition of 'Costream' type interleaves the iterations of
739−-- but fairly interleaved list-transformer like composition. The monadic
777+-- outer and inner loops in a nested loop composition. This works exactly the
740−-- continuations or iterations of the outer loop are fairly interleaved with
778+-- same way as the merging of two streams in 'costreamly' fashion works. The
741−-- the continuations or iterations of the inner loop.
779+-- 'costreamly' type combinator can be used to switch to this style of
780+-- composition. Alternatively, a type annotation can be used to specify the
781+-- type of the stream as 'Costream'.
742--
782--
743-- @
783-- @
744-- import "Streamly"
784-- import "Streamly"
745-- import "Streamly.Prelude"
785-- import "Streamly.Prelude"
746--
786--
747−-- main = 'runInterleavedT' $ do
787+-- main = 'runStream' . 'costreamly' $ do
748-- x <- 'fromFoldable' [1,2]
788-- x <- 'fromFoldable' [1,2]
749-- y <- 'fromFoldable' [3,4]
789-- y <- 'fromFoldable' [3,4]
750−-- liftIO $ putStrLn $ show (x, y)
790+-- once $ putStrLn $ show (x, y)
751-- @
791-- @
752-- @
792-- @
753-- (1,3)
793-- (1,3)
@@ -756,21 +796,21 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
756-- (2,4)
796-- (2,4)
757-- @
797-- @
758--
798--
759−-- You will notice that this is the monadic equivalent of the '<=>' style
760−-- sum composition. The same caveats apply to this as the '<=>' operation.
761
799
762-- $fairlyConcurrentNesting
800-- $fairlyConcurrentNesting
763--
801--
764−-- When we interpret the monadic composition as 'ParallelT' we get a
802+-- The 'Monad' composition of 'Parallel' type starts all the iterations of a
765−-- /concurrent/ list-transformer like composition just like 'AsyncT'. The
766−-- difference is that this is fully parallel with all iterations starting
767−-- concurrently instead of the demand driven concurrency of 'AsyncT'.
803+-- loop concurrently instead of demand driven as in the case of 'Coparallel'.
804+-- This works exactly the same way as the merging streams 'parallely' works.
805+-- The 'parallely' type combinator can be used to switch to this style of
806+-- composition. Alternatively, a type annotation can be used to specify the
807+-- type of the stream as 'Parallel'.
768--
808--
769-- @
809-- @
770-- import "Streamly"
810-- import "Streamly"
771-- import "Streamly.Prelude"
811-- import "Streamly.Prelude"
772--
812--
773−-- main = 'runParallelT' $ do
813+-- main = 'runStream' . 'parallely' $ do
774-- x <- 'fromFoldable' [3,2,1]
814-- x <- 'fromFoldable' [3,2,1]
775-- delay x
815-- delay x
776-- @
816-- @
@@ -779,28 +819,24 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
779-- ThreadId 39: Delay 2
819-- ThreadId 39: Delay 2
780-- ThreadId 38: Delay 3
820-- ThreadId 38: Delay 3
781-- @
821-- @
782−--
783−-- You will notice that this is the monadic equivalent of the '<|>' style
784−-- sum composition. The same caveats apply to this as the '<|>' operation.
785
822
786-- $monadExercise
823-- $monadExercise
787--
824--
788-- The streamly code is usually written in a way that is agnostic of the
825-- The streamly code is usually written in a way that is agnostic of the
789-- specific monadic composition type. We use a polymorphic type with a
826-- specific monadic composition type. We use a polymorphic type with a
790-- 'IsStream' type class constraint. When running the stream we can choose the
827-- 'IsStream' type class constraint. When running the stream we can choose the
791−-- specific mode of composition. For example look at the following code.
828+-- specific mode of composition. For example take a look at the following code.
792--
829--
793-- @
830-- @
794-- import "Streamly"
831-- import "Streamly"
795-- import "Streamly.Prelude"
832-- import "Streamly.Prelude"
796--
833--
797−--
798-- composed :: 'IsStream' t => t m a
834-- composed :: 'IsStream' t => t m a
799-- composed = do
835-- composed = do
800-- sz <- sizes
836-- sz <- sizes
801-- cl <- colors
837-- cl <- colors
802-- sh <- shapes
838-- sh <- shapes
803−-- liftIO $ putStrLn $ show (sz, cl, sh)
839+-- once $ putStrLn $ show (sz, cl, sh)
804--
840--
805-- where
841-- where
806--
842--
@@ -812,20 +848,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
812-- Now we can interpret this in whatever way we want:
848-- Now we can interpret this in whatever way we want:
813--
849--
814-- @
850-- @
815−-- main = 'runStreamT' composed
816−-- main = 'runAsyncT' composed
817−-- main = 'runInterleavedT' composed
818−-- main = 'runParallelT' composed
819−-- @
820−--
821−-- Equivalently, we can also write it using the type adapter combinators, like
822−-- this:
823−--
824−-- @
825−-- main = 'runStream' $ 'serially' $ composed
851+-- main = 'runStream' . 'streamly' $ composed
826−-- main = 'runStream' $ 'asyncly' $ composed
852+-- main = 'runStream' . 'costreamly' $ composed
827−-- main = 'runStream' $ 'interleaving' $ composed
853+-- main = 'runStream' . 'coparallely' $ composed
828−-- main = 'runStream' $ 'parallely' $ composed
854+-- main = 'runStream' . 'parallely' $ composed
829-- @
855-- @
830--
856--
831-- As an exercise try to figure out the output of this code for each mode of
857-- As an exercise try to figure out the output of this code for each mode of
@@ -834,15 +860,13 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
834-- $functor
860-- $functor
835--
861--
836-- 'fmap' transforms a stream by mapping a function on all elements of the
862-- 'fmap' transforms a stream by mapping a function on all elements of the
837−-- stream. The functor instance of each stream type defines 'fmap' to be
863+-- stream. 'fmap' behaves in the same way for all stream types, it is always
838−-- precisely the same as 'liftM', and therefore 'fmap' is always serial
864+-- serial.
839−-- irrespective of the type. For concurrent mapping, alternative versions of
840−-- 'fmap', namely, 'asyncMap' and 'parMap' are provided.
841--
865--
842-- @
866-- @
843-- import "Streamly"
867-- import "Streamly"
844--
868--
845−-- main = ('toList' $ 'serially' $ fmap show $ 'fromFoldable' [1..10]) >>= print
869+-- main = ('toList' $ fmap show $ 'fromFoldable' [1..10]) >>= print
846-- @
870-- @
847--
871--
848-- Also see the 'mapM' and 'sequence' functions for mapping actions, in the
872-- Also see the 'mapM' and 'sequence' functions for mapping actions, in the
@@ -851,11 +875,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
851-- $applicative
875-- $applicative
852--
876--
853-- Applicative is precisely the same as the 'ap' operation of 'Monad'. For
877-- Applicative is precisely the same as the 'ap' operation of 'Monad'. For
854−-- zipping and parallel applicatives separate types 'ZipSerial' and 'ZipAsync'
878+-- zipping applicatives separate types 'ZipStream' and 'ZipParallel' are
855−-- are provided.
879+-- provided.
856--
880--
857−-- The following example runs all iterations serially and takes a total 17
881+-- The following example uses the 'Stream' applicative, it runs all iterations
858−-- seconds (1 + 3 + 4 + 2 + 3 + 4):
882+-- serially and takes a total 17 seconds (1 + 3 + 4 + 2 + 3 + 4):
859--
883--
860-- @
884-- @
861-- import "Streamly"
885-- import "Streamly"
@@ -866,7 +890,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
866-- s2 = d 3 <> d 4
890-- s2 = d 3 <> d 4
867-- d n = delay n >> return n
891-- d n = delay n >> return n
868--
892--
869−-- main = ('toList' . 'serially' $ (,) \<$> s1 \<*> s2) >>= print
893+-- main = ('toList' . 'streamly' $ (,) \<$> s1 \<*> s2) >>= print
870-- @
894-- @
871-- @
895-- @
872-- ThreadId 36: Delay 1
896-- ThreadId 36: Delay 1
@@ -878,11 +902,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
878-- [(1,3),(1,4),(2,3),(2,4)]
902-- [(1,3),(1,4),(2,3),(2,4)]
879-- @
903-- @
880--
904--
881−-- Similalrly interleaving runs the iterations in an interleaved order but
905+-- Similalrly 'Costream' applicative runs the iterations in an interleaved
882−-- since it is serial it takes a total of 17 seconds:
906+-- order but since it is serial it takes a total of 17 seconds:
883--
907--
884-- @
908-- @
885−-- main = ('toList' . 'interleaving' $ (,) \<$> s1 \<*> s2) >>= print
909+-- main = ('toList' . 'costreamly' $ (,) \<$> s1 \<*> s2) >>= print
886-- @
910-- @
887-- @
911-- @
888-- ThreadId 36: Delay 1
912-- ThreadId 36: Delay 1
@@ -894,11 +918,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
894-- [(1,3),(2,3),(1,4),(2,4)]
918-- [(1,3),(2,3),(1,4),(2,4)]
895-- @
919-- @
896--
920--
897−-- 'AsyncT' can run the iterations concurrently and therefore takes a total
921+-- 'Coparallel' can run the iterations concurrently and therefore takes a total
898-- of 10 seconds (1 + 2 + 3 + 4):
922-- of 10 seconds (1 + 2 + 3 + 4):
899--
923--
900-- @
924-- @
901−-- main = ('toList' . 'asyncly' $ (,) \<$> s1 \<*> s2) >>= print
925+-- main = ('toList' . 'coparallely' $ (,) \<$> s1 \<*> s2) >>= print
902-- @
926-- @
903-- @
927-- @
904-- ThreadId 34: Delay 1
928-- ThreadId 34: Delay 1
@@ -910,7 +934,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
910-- [(1,3),(2,3),(1,4),(2,4)]
934-- [(1,3),(2,3),(1,4),(2,4)]
911-- @
935-- @
912--
936--
913−-- Similalrly 'ParallelT' as well can run the iterations concurrently and
937+-- Similalrly 'Parallel' as well can run the iterations concurrently and
914-- therefore takes a total of 10 seconds (1 + 2 + 3 + 4):
938-- therefore takes a total of 10 seconds (1 + 2 + 3 + 4):
915--
939--
916-- @
940-- @
@@ -933,17 +957,17 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
933-- streamly.
957-- streamly.
934--
958--
935-- @
959-- @
936−-- +-----+--------------+------------+
960+-- +-----+--------------+--------------+
937−-- | | Serial | Concurrent |
961+-- | | Serial | Concurrent |
938−-- +=====+==============+============+
962+-- +=====+==============+==============+
939−-- | DFS | 'StreamT' | 'AsyncT' |
963+-- | DFS | 'Stream' | 'Coparallel' |
940−-- | +--------------+------------+
964+-- | +--------------+--------------+
941−-- | | '<>' | '<|' |
965+-- | | 'splice' | 'coparallel' |
942−-- +-----+--------------+------------+
966+-- +-----+--------------+--------------+
943−-- | BFS | 'InterleavedT' | 'ParallelT' |
967+-- | BFS | 'Costream' | 'Parallel' |
944−-- | +--------------+------------+
968+-- | +--------------+--------------+
945−-- | | '<=>' | '<|>' |
969+-- | | 'cosplice' | 'parallel' |
946−-- +-----+--------------+------------+
970+-- +-----+--------------+--------------+
947-- @
971-- @
948
972
949-- $zipping
973-- $zipping
@@ -951,12 +975,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
951-- Zipping is a special transformation where the corresponding elements of two
975-- Zipping is a special transformation where the corresponding elements of two
952-- streams are combined together using a zip function producing a new stream of
976-- streams are combined together using a zip function producing a new stream of
953-- outputs. Two different types are provided for serial and concurrent zipping.
977-- outputs. Two different types are provided for serial and concurrent zipping.
954−-- These types provide an applicative instance that zips the argument streams.
978+-- These types provide an applicative instance that can be used to lift
979+-- functions to zip the argument streams.
955−-- Also see the zipping function in the "Streamly.Prelude" module.
980+-- Also see the zipping functions in the "Streamly.Prelude" module.
956
981
957-- $serialzip
982-- $serialzip
958--
983--
959−-- 'ZipSerial' zips streams serially:
984+-- The applicative instance of 'ZipStream' type zips streams serially.
985+-- 'zipStreamly' type combinator can be used to switch to serial applicative
986+-- zip composition:
960--
987--
961-- @
988-- @
962-- import "Streamly"
989-- import "Streamly"
@@ -964,10 +991,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
964-- import Control.Concurrent
991-- import Control.Concurrent
965--
992--
966-- d n = delay n >> return n
993-- d n = delay n >> return n
967−-- s1 = 'adapt' . 'serially' $ d 1 <> d 2
994+-- s1 = 'streamly' $ d 1 <> d 2
968−-- s2 = 'adapt' . 'serially' $ d 3 <> d 4
995+-- s2 = 'streamly' $ d 3 <> d 4
969--
996--
970−-- main = ('toList' . 'zipping' $ (,) \<$> s1 \<*> s2) >>= print
997+-- main = ('toList' . 'zipStreamly' $ (,) \<$> s1 \<*> s2) >>= print
971-- @
998-- @
972--
999--
973-- This takes total 10 seconds to zip, which is (1 + 2 + 3 + 4) since
1000-- This takes total 10 seconds to zip, which is (1 + 2 + 3 + 4) since
@@ -983,7 +1010,10 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
983
1010
984-- $parallelzip
1011-- $parallelzip
985--
1012--
986−-- 'ZipAsync' zips streams concurrently:
1013+-- The applicative instance of 'ZipParallel' type zips streams concurrently.
1014+-- 'zipParallely' type combinator can be used to switch to parallel applicative
1015+-- zip composition:
1016+--
987--
1017--
988-- @
1018-- @
989-- import "Streamly"
1019-- import "Streamly"
@@ -992,12 +1022,12 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
992-- import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
1022-- import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
993--
1023--
994-- d n = delay n >> return n
1024-- d n = delay n >> return n
995−-- s1 = 'adapt' . 'serially' $ d 1 <> d 2
1025+-- s1 = 'streamly' $ d 1 <> d 2
996−-- s2 = 'adapt' . 'serially' $ d 3 <> d 4
1026+-- s2 = 'streamly' $ d 3 <> d 4
997--
1027--
998-- main = do
1028-- main = do
999−-- liftIO $ hSetBuffering stdout LineBuffering
1029+-- hSetBuffering stdout LineBuffering
1000−-- ('toList' . 'zippingAsync' $ (,) \<$> s1 \<*> s2) >>= print
1030+-- ('toList' . 'zipParallely' $ (,) \<$> s1 \<*> s2) >>= print
1001-- @
1031-- @
1002--
1032--
1003-- This takes 7 seconds to zip, which is max (1,3) + max (2,4) because 1 and 3
1033-- This takes 7 seconds to zip, which is max (1,3) + max (2,4) because 1 and 3
@@ -1014,16 +1044,18 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1014-- $concurrent
1044-- $concurrent
1015--
1045--
1016-- When writing concurrent programs there are two distinct places where the
1046-- When writing concurrent programs there are two distinct places where the
1017−-- programmer chooses the type of concurrency. First, when /generating/ a
1047+-- programmer can control the concurrency. First, when /composing/ a stream by
1018−-- stream by combining other streams we can use one of the sum style operators
1048+-- merging multiple streams we can choose an appropriate sum style operators to
1019−-- to combine them concurrently or serially. Second, when /processing/ a stream
1049+-- combine them concurrently or serially. Second, when /processing/ a stream in
1020−-- in a monadic composition we can choose one of the monad composition types to
1050+-- a monadic composition we can choose one of the monad composition types to
1021-- choose the desired type of concurrency.
1051-- choose the desired type of concurrency.
1022--
1052--
1023-- In the following example the squares of @x@ and @y@ are computed
1053-- In the following example the squares of @x@ and @y@ are computed
1024−-- concurrently using the '<|' operator and the square roots of their sum are
1054+-- concurrently using the 'coparallel' operation and the square roots of their
1025−-- also computed concurrently by using the 'asyncly' combinator. We can choose
1055+-- sum are computed serially because of the 'streamly' combinator. We can
1056+-- choose different combinators for the monadic processing and the stream
1026−-- different combinators e.g. '<>' and 'serially', to control the concurrency.
1057+-- generation, to control the concurrency. We can also use the 'coparallely'
1058+-- combinator instead of explicitly folding with 'coparallel'.
1027--
1059--
1028-- @
1060-- @
1029-- import "Streamly"
1061-- import "Streamly"
@@ -1032,11 +1064,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1032--
1064--
1033-- main = do
1065-- main = do
1034-- z \<- 'toList'
1066-- z \<- 'toList'
1035−-- $ 'asyncly' -- Concurrent monadic processing (sqrt below)
1067+-- $ 'streamly' -- Serial monadic processing (sqrt below)
1036-- $ do
1068-- $ do
1037−-- x2 \<- 'forEachWith' ('<|') [1..100] $ -- Concurrent @"for"@ loop
1069+-- x2 \<- 'forEachWith' 'coparallel' [1..100] $ -- Concurrent @"for"@ loop
1038-- \\x -> return $ x * x -- body of the loop
1070-- \\x -> return $ x * x -- body of the loop
1039−-- y2 \<- 'forEachWith' ('<|') [1..100] $
1071+-- y2 \<- 'forEachWith' 'coparallel' [1..100] $
1040-- \\y -> return $ y * y
1072-- \\y -> return $ y * y
1041-- return $ sqrt (x2 + y2)
1073-- return $ sqrt (x2 + y2)
1042-- print $ sum z
1074-- print $ sum z
@@ -1061,12 +1093,11 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1061-- (active hyperlinks are the streamly APIs) and yet it is a reactive
1093-- (active hyperlinks are the streamly APIs) and yet it is a reactive
1062-- application.
1094-- application.
1063--
1095--
1064−--
1065-- This application has two independent and concurrent sources of event
1096-- This application has two independent and concurrent sources of event
1066-- streams, @acidRain@ and @userAction@. @acidRain@ continuously generates
1097-- streams, @acidRain@ and @userAction@. @acidRain@ continuously generates
1067−-- events that deteriorate the health of the game character. @userAction@ can
1098+-- events that deteriorate the health of the character in the game.
1068−-- be "potion" or "quit". When the user types "potion" the health improves and
1099+-- @userAction@ can be "potion" or "quit". When the user types "potion" the
1069−-- the game continues.
1100+-- health improves and the game continues.
1070--
1101--
1071-- @
1102-- @
1072-- {-\# LANGUAGE FlexibleContexts #-}
1103-- {-\# LANGUAGE FlexibleContexts #-}
@@ -1074,7 +1105,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1074-- import "Streamly"
1105-- import "Streamly"
1075-- import Control.Concurrent (threadDelay)
1106-- import Control.Concurrent (threadDelay)
1076-- import Control.Monad (when)
1107-- import Control.Monad (when)
1077−-- import Control.Monad.State
1108+-- import Control.Monad.State (MonadState, get, modify, runStateT)
1078-- import Data.Semigroup (cycle1)
1109-- import Data.Semigroup (cycle1)
1079--
1110--
1080-- data Event = Harm Int | Heal Int | Quit deriving (Show)
1111-- data Event = Harm Int | Heal Int | Quit deriving (Show)
@@ -1092,9 +1123,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1092-- acidRain :: MonadIO m => 'StreamT' m Event
1123-- acidRain :: MonadIO m => 'StreamT' m Event
1093-- acidRain = cycle1 $ liftIO (threadDelay 1000000) >> return (Harm 1)
1124-- acidRain = cycle1 $ liftIO (threadDelay 1000000) >> return (Harm 1)
1094--
1125--
1095−-- game :: ('MonadAsync' m, MonadState Int m) => 'StreamT' m ()
1126+-- game :: ('MonadParallel' m, MonadState Int m) => 'StreamT' m ()
1096-- game = do
1127-- game = do
1097−-- event \<- userAction \<|> acidRain
1128+-- event \<- userAction \`parallel` acidRain
1098-- case event of
1129-- case event of
1099-- Harm n -> modify $ \\h -> h - n
1130-- Harm n -> modify $ \\h -> h - n
1100-- Heal n -> modify $ \\h -> h + n
1131-- Heal n -> modify $ \\h -> h + n
@@ -1107,7 +1138,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1107-- main = do
1138-- main = do
1108-- putStrLn "Your health is deteriorating due to acid rain,\\
1139-- putStrLn "Your health is deteriorating due to acid rain,\\
1109-- \\ type \\"potion\\" or \\"quit\\""
1140-- \\ type \\"potion\\" or \\"quit\\""
1110−-- _ <- runStateT ('runStreamT' game) 60
1141+-- _ <- runStateT ('runStream' game) 60
1111-- return ()
1142-- return ()
1112-- @
1143-- @
1113--
1144--
@@ -1127,65 +1158,96 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1127-- package which is specifically and carefully designed to measure the
1158-- package which is specifically and carefully designed to measure the
1128-- performance of Haskell streaming libraries fairly and squarely in the right
1159-- performance of Haskell streaming libraries fairly and squarely in the right
1129-- way. Streamly performs at par or even better than most streaming libraries
1160-- way. Streamly performs at par or even better than most streaming libraries
1130−-- for common operations even though it needs to deal with the concurrency
1161+-- for serial operations even though it needs to deal with the concurrency
1131-- capability.
1162-- capability.
1132
1163
1133-- $interop
1164-- $interop
1134--
1165--
1135-- We can use @unfoldr@ and @uncons@ to convert one streaming type to another.
1166-- We can use @unfoldr@ and @uncons@ to convert one streaming type to another.
1136−-- We will assume the following common code to be available in the examples
1167+--
1137−-- demonstrated below.
1168+-- Interop with @vector@:
1138--
1169--
1139-- @
1170-- @
1140−-- import "Streamly"
1171+-- import Streamly
1141−-- import "Streamly.Prelude"
1172+-- import qualified Streamly.Prelude as S
1142−-- import System.IO (stdin)
1173+-- import qualified Data.Vector.Fusion.Stream.Monadic as V
1174+--
1175+-- main = do
1176+-- -- streamly to vector
1177+-- V.toList (V.unfoldrM S.uncons (S.fromFoldable [1..3])) >>= print
1178+--
1179+-- -- vector to streamly
1180+-- S.toList (S.unfoldrM unconsV (V.fromList [1..3])) >>= print
1143--
1181--
1144−-- -- Adapt uncons to return an Either instead of Maybe
1182+-- where
1145−-- unconsE s = 'uncons' s >>= maybe (return $ Left ()) (return . Right)
1183+-- unconsV v = do
1146−-- stdinLn = 'serially' $ 'fromHandle' stdin
1184+-- r <- V.null v
1185+-- if r
1186+-- then return Nothing
1187+-- else do
1188+-- h <- V.head v
1189+-- return $ Just (h, V.tail v)
1147-- @
1190-- @
1148--
1191--
1149-- Interop with @pipes@:
1192-- Interop with @pipes@:
1150--
1193--
1151-- @
1194-- @
1195+-- import "Streamly"
1196+-- import qualified "Streamly.Prelude" as S
1152-- import qualified Pipes as P
1197-- import qualified Pipes as P
1153-- import qualified Pipes.Prelude as P
1198-- import qualified Pipes.Prelude as P
1154--
1199--
1155-- main = do
1200-- main = do
1156-- -- streamly to pipe
1201-- -- streamly to pipe
1157−-- P.runEffect $ P.for (P.unfoldr unconsE stdinLn) (lift . putStrLn)
1202+-- P.toListM (P.unfoldr unconsS (S.'fromFoldable' [1..3])) >>= print
1158--
1203--
1159-- -- pipe to streamly
1204-- -- pipe to streamly
1205+-- S.'toList' (S.'unfoldrM' unconsP (P.each [1..3])) >>= print
1206+--
1207+-- where
1160-- -- Adapt P.next to return a Maybe instead of Either
1208-- -- Adapt P.next to return a Maybe instead of Either
1161−-- let nextM p = P.next p >>= either (\\_ -> return Nothing) (return . Just)
1209+-- unconsP p = P.next p >>= either (\\_ -> return Nothing) (return . Just)
1162−-- 'runStreamT' $ 'unfoldrM' nextM P.stdinLn >>= lift . putStrLn
1210+--
1211+-- -- Adapt S.uncons to return an Either instead of Maybe
1212+-- unconsS s = S.'uncons' s >>= maybe (return $ Left ()) (return . Right)
1163-- @
1213-- @
1164--
1214--
1165-- Interop with @streaming@:
1215-- Interop with @streaming@:
1166--
1216--
1167-- @
1217-- @
1218+-- import "Streamly"
1219+-- import qualified "Streamly.Prelude" as S
1168−-- import qualified Streaming as S
1220+-- import qualified Streaming as SG
1169−-- import qualified Streaming.Prelude as S
1221+-- import qualified Streaming.Prelude as SG
1170--
1222--
1171-- main = do
1223-- main = do
1172-- -- streamly to streaming
1224-- -- streamly to streaming
1173−-- S.stdoutLn $ S.unfoldr unconsE stdinLn
1225+-- SG.toList (SG.unfoldr unconsS (S.'fromFoldable' [1..3])) >>= print
1174--
1226--
1175-- -- streaming to streamly
1227-- -- streaming to streamly
1176−-- 'runStreamT' $ unfoldrM S.uncons S.stdinLn >>= lift . putStrLn
1228+-- S.'toList' (S.unfoldrM SG.uncons (SG.each [1..3])) >>= print
1229+--
1230+-- where
1177--
1231--
1232+-- -- Adapt S.uncons to return an Either instead of Maybe
1233+-- unconsS s = S.'uncons' s >>= maybe (return $ Left ()) (return . Right)
1178-- @
1234-- @
1179--
1235--
1180-- Interop with @conduit@:
1236-- Interop with @conduit@:
1181--
1237--
1182-- @
1238-- @
1239+-- import "Streamly"
1240+-- import qualified "Streamly.Prelude" as S
1183-- import qualified Data.Conduit as C
1241-- import qualified Data.Conduit as C
1184-- import qualified Data.Conduit.List as C
1242-- import qualified Data.Conduit.List as C
1185-- import qualified Data.Conduit.Combinators as C
1243-- import qualified Data.Conduit.Combinators as C
1186--
1244--
1245+-- main = do
1187−-- -- streamly to conduit
1246+-- -- streamly to conduit
1188−-- main = (C.unfoldM 'uncons' stdinLn) C.$$ C.print
1247+-- C.runConduit (C.unfoldM S.'uncons' (S.'fromFoldable' [1..3]) C..| C.sinkList) >>= print
1248+--
1249+-- -- It seems there is no way out of a conduit as it does not provide an
1250+-- -- uncons or a tail function.
1189-- @
1251-- @
1190
1252
1191-- $comparison
1253-- $comparison
@@ -1197,17 +1259,19 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1197--
1259--
1198-- @
1260-- @
1199-- +-----------------+----------------+
1261-- +-----------------+----------------+
1200−-- | Non-determinism | <https://hackage.haskell.org/package/list-t list-t> |
1262+-- | Non-determinism | <https://hackage.haskell.org/package/pipes pipes> |
1263+-- | +----------------+
1264+-- | | <https://hackage.haskell.org/package/list-t list-t> |
1201-- | +----------------+
1265-- | +----------------+
1202-- | | <https://hackage.haskell.org/package/logict logict> |
1266-- | | <https://hackage.haskell.org/package/logict logict> |
1203-- +-----------------+----------------+
1267-- +-----------------+----------------+
1204−-- | Streaming | <https://hackage.haskell.org/package/streaming streaming> |
1268+-- | Streaming | <https://hackage.haskell.org/package/vector vector> |
1205-- | +----------------+
1269-- | +----------------+
1206−-- | | <https://hackage.haskell.org/package/conduit conduit> |
1270+-- | | <https://hackage.haskell.org/package/streaming streaming> |
1207-- | +----------------+
1271-- | +----------------+
1208-- | | <https://hackage.haskell.org/package/pipes pipes> |
1272-- | | <https://hackage.haskell.org/package/pipes pipes> |
1209-- | +----------------+
1273-- | +----------------+
1210−-- | | <https://hackage.haskell.org/package/simple-conduit simple-conduit> |
1274+-- | | <https://hackage.haskell.org/package/conduit conduit> |
1211-- +-----------------+----------------+
1275-- +-----------------+----------------+
1212-- | Concurrency | <https://hackage.haskell.org/package/async async> |
1276-- | Concurrency | <https://hackage.haskell.org/package/async async> |
1213-- | +----------------+
1277-- | +----------------+
@@ -1221,31 +1285,30 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1221-- +-----------------+----------------+
1285-- +-----------------+----------------+
1222-- @
1286-- @
1223--
1287--
1224−-- Streamly covers all the functionality provided by both the non-determinism
1288+-- Streamly is a list-transformer. It provides all the functionality provided
1225−-- packages listed above and provides better performance in comparison to
1289+-- by any of the list transformer and logic programming packages listed above.
1226−-- those. In fact, at the core streamly is a list transformer but it naturally
1227−-- integrates the concurrency dimension to the basic list transformer
1290+-- In addition, Streamly naturally integrates the concurrency dimension to the
1228−-- functionality.
1291+-- basic list transformer functionality.
1229--
1292--
1230−-- When it comes to streaming, in terms of core concepts, @simple-conduit@ is
1293+-- When it comes to streaming, in terms of the streaming API streamly is almost
1231−-- the package that is closest to streamly if we set aside the concurrency
1294+-- identical to the vector package. Streamly, vector and streaming packages all
1232−-- dimension, both are streaming packages with list transformer like monad
1295+-- represent a stream as data and are therefore similar in the fundamental
1233−-- composition. However, in terms of API @streamly@ is more like the @streaming@
1296+-- approach to streaming. The fundamental difference is that streamly adds
1234−-- package. Streamly can be used to achieve more or less the functionality
1297+-- concurrency support and the monad instance provides concurrent looping.
1235−-- provided by any of the streaming packages listed above. The types and API of
1298+-- Other streaming libraries like pipes, conduit and machines represent and
1236−-- streamly are much simpler in comparison to conduit and pipes. It is more or
1299+-- compose stream processors rather than the stream data and therefore fall in
1237−-- less like the standard Haskell list APIs.
1300+-- another class of streaming libraries and have comparatively more complicated
1301+-- types.
1238--
1302--
1239-- When it comes to concurrency, streamly can do everything that the @async@
1303-- When it comes to concurrency, streamly can do everything that the @async@
1240-- package can do and more. async provides applicative concurrency whereas
1304-- package can do and more. async provides applicative concurrency whereas
1241−-- streamly provides both applicative and monadic concurrency. The 'ZipAsync'
1305+-- streamly provides both applicative and monadic concurrency. The
1242−-- type behaves like the applicative instance of async. This work was
1306+-- 'ZipParallel' type behaves like the applicative instance of async. In
1243−-- originally inspired by the concurrency implementation in @transient@ though
1244−-- it has no resemblence with that. Streamly provides concurrency as transient
1245−-- does but in a sort of dual manner, it can lazily stream the output. In
1246-- comparison to transient streamly has a first class streaming interface and
1307-- comparison to transient streamly has a first class streaming interface and
1247-- is a monad transformer that can be used universally in any Haskell monad
1308-- is a monad transformer that can be used universally in any Haskell monad
1248−-- transformer stack.
1309+-- transformer stack. Streamly was in fact originally inspired by the
1310+-- concurrency implementation in @transient@ though it has no resemblence with
1311+-- that and takes a lazy pull approach versus transient's strict push approach.
1249--
1312--
1250-- The non-determinism, concurrency and streaming combination make streamly a
1313-- The non-determinism, concurrency and streaming combination make streamly a
1251-- strong FRP capable library as well. FRP is fundamentally stream of events
1314-- strong FRP capable library as well. FRP is fundamentally stream of events