on master help sign in
Add examples, update doc, mostly for Streamly module
b0faa34Harendra Kumar
src/Streamly.hs+65−16
@@ -167,31 +167,80 @@ import Control.Monad.Trans.Class (MonadTrans (..))
167-- $zipping
167-- $zipping
168--
168--
169-- 'ZipStream' and 'ZipAsync', provide 'Applicative' instances for zipping the
169-- 'ZipStream' and 'ZipAsync', provide 'Applicative' instances for zipping the
170−-- corresponding elements of two streams together. Note that these types do not
170+-- corresponding elements of two streams together. Note that these types are
171−-- provide 'Monad' instances.
171+-- not monads.
172
172
173-- $sum
173-- $sum
174--
174--
175-- Just like product style composition there are four distinct ways to combine
175-- Just like product style composition there are four distinct ways to combine
176-- streams in sum style each directly corresponding to one of the product style
176-- streams in sum style each directly corresponding to one of the product style
177+-- composition.
178+--
177−-- composition. The standard semigroup append '<>' operator appends two
179+-- The standard semigroup append '<>' operator appends two streams serially,
178−-- streams serially, this style corresponds to the 'StreamT' style of monadic
180+-- this style corresponds to the 'StreamT' style of monadic composition.
181+--
182+-- @
183+-- main = ('toList' . 'serially' $ (return 1 <> return 2) <> (return 3 <> return 4)) >>= print
184+-- @
185+-- @
186+-- [1,2,3,4]
187+-- @
188+--
179−-- composition. The standard 'Alternative' operator '<|>' fairly interleaves
189+-- The standard 'Alternative' operator '<|>' fairly interleaves two streams in
180−-- two streams in parallel, this corresponds to the 'ParallelT' style. Two
190+-- parallel, this operator corresponds to the 'ParallelT' style.
191+--
192+-- @
193+-- main = ('toList' . 'serially' $ (return 1 <> return 2) \<|\> (return 3 <> return 4)) >>= print
194+-- @
195+-- @
196+-- [1,3,2,4]
197+-- @
198+--
199+-- Unlike '<|', this operator cannot be used to fold infinite containers since
200+-- that might accumulate too many partially drained streams. To be clear, it
201+-- can combine infinite streams but not infinite number of streams.
202+--
181−-- additional sum style composition operators that streamly introduces are
203+-- Two additional sum style composition operators that streamly introduces are
182-- described below.
204-- described below.
183
205
184-- $adapters
206-- $adapters
185--
207--
186−-- Code using streamly is written such that it is agnostic of any specific
208+-- Code using streamly is usually written such that it is agnostic of any
187−-- streaming type. We use a polymorphic type with the 'Streaming' class
209+-- specific streaming type. We use a type variable (polymorphic type) with the
188−-- constraint. Finally when running the monad we can specify the actual type
210+-- 'Streaming' class constraint. Finally, when running the monad we can specify
189−-- that we want to use to interpret the code. However, in certain cases we may
211+-- the actual type that we want to use to interpret the code. However, in
190−-- want to use a specific type to force a certain type of composition. Since
212+-- certain cases we may want to use a specific type to force a certain type of
191−-- they have the same underlying type, streams can be converted from one type
192−-- to another freely. These combinators can be used to convert the streaming
213+-- composition. These combinators can be used to convert the stream types from
214+-- one to another at no cost as all the types have the same underlying
215+-- representation.
216+--
193−-- types at will. If you get an ambiguous type error you perhaps forgot to
217+-- If you see an @ambiguous type variable@ error then most likely it is because
194−-- use a sepcific type before running the monad.
218+-- you have not specified the stream type. You either need a type annotation or
219+-- one of the following combinators to specify what type of stream you mean.
220+--
221+-- This code:
222+-- @
223+-- main = ('toList' $ (return 1 <> return 2)) >>= print
224+-- @
225+--
226+-- will result in a type error like this:
227+--
228+-- @
229+-- Ambiguous type variable ‘t0’ arising from a use of ...
230+-- @
231+--
232+-- To fix the error:
233+--
234+-- @
235+-- main = ('toList' . 'serially' $ (return 1 <> return 2)) >>= print
236+-- @
237+-- @
238+-- main = ('toList' $ (return 1 <> return 2 :: StreamT IO Int)) >>= print
239+-- @
240+--
241+-- Note that using the combinators is easier as you do not have to think about
242+-- the specific types, they are just inferred.
243+--
195
244
196-- $foldutils
245-- $foldutils
197--
246--
src/Streamly/Streams.hs+154−55
@@ -192,17 +192,40 @@ async m = do
192-- StreamT
192-- StreamT
193------------------------------------------------------------------------------
193------------------------------------------------------------------------------
194
194
195−-- | The 'Monad' instance of 'StreamT' combines two streams serially in the
195+-- | The 'Monad' instance of 'StreamT' runs the /monadic continuation/ for each
196−-- most straightforward manner, just like the nested 'for' loops in imperative
196+-- element of the stream, serially.
197−-- paradigm. It moves to the next element in the first stream only after the
197+--
198−-- first element has been combined with every element in the second stream. In
198+-- @
199−-- other words, 'StreamT' nests loops in a depth first manner.
199+-- main = 'runStreamT' $ do
200+-- x <- return 1 \<\> return 2
201+-- liftIO $ print x
202+-- @
203+-- @
204+-- "1"
205+-- "2"
206+-- @
207+--
208+-- 'StreamT' nests streams serially in a depth first manner.
209+--
210+-- @
211+-- main = 'runStreamT' $ do
212+-- x <- return 1 \<\> return 2
213+-- y <- return 3 \<\> return 4
214+-- liftIO $ print (x, y)
215+-- @
216+-- @
217+-- (1,3)
218+-- (1,4)
219+-- (2,3)
220+-- (2,4)
221+-- @
222+--
223+-- This behavior is exactly like a list transformer. We call the monadic code
224+-- being run for each element of the stream a monadic continuation. In
225+-- imperative paradigm we can think of this composition as nested @for@ loops
226+-- and the monadic continuation is the body of the loop. The loop iterates for
227+-- all elements of the stream.
200--
228--
201−-- Note that 'StreamT' style composition works well operationally even when the
202−-- streams being composed are infinite. However, if the second stream is
203−-- infinite we will never come back to the second iteration of the first
204−-- stream.
205−
206newtype StreamT m a = StreamT {getStreamT :: Stream m a}
229newtype StreamT m a = StreamT {getStreamT :: Stream m a}
207 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
230 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
208
231
@@ -302,13 +325,23 @@ instance (Monad m, Floating a) => Floating (StreamT m a) where
302-- InterleavedT
325-- InterleavedT
303------------------------------------------------------------------------------
326------------------------------------------------------------------------------
304
327
305−-- | The 'Monad' instance of 'InterleavedT' fairly interleaves the iterations
328+-- | Like 'StreamT' but different in nesting behavior. It fairly interleaves
306−-- of the inner and the outer loop, nesting loops in a breadth first manner.
329+-- the iterations of the inner and the outer loop, nesting loops in a breadth
330+-- first manner.
307--
331--
308−-- Note that 'InterleavedT' style composition cannot work well operationally on
332+--
309−-- infinite streams. Since the next iteration of the outer loop is started even
333+-- @
310−-- before the first one has finsihed, it will keep accumulating space resulting
334+-- main = 'runInterleavedT' $ do
311−-- in a continuous increase in space consumption.
335+-- x <- return 1 \<\> return 2
336+-- y <- return 3 \<\> return 4
337+-- liftIO $ print (x, y)
338+-- @
339+-- @
340+-- (1,3)
341+-- (2,3)
342+-- (1,4)
343+-- (2,4)
344+-- @
312--
345--
313newtype InterleavedT m a = InterleavedT {getInterleavedT :: Stream m a}
346newtype InterleavedT m a = InterleavedT {getInterleavedT :: Stream m a}
314 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
347 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
@@ -401,16 +434,27 @@ instance (Monad m, Floating a) => Floating (InterleavedT m a) where
401-- AsyncT
434-- AsyncT
402------------------------------------------------------------------------------
435------------------------------------------------------------------------------
403
436
404−-- | 'AsyncT' is the concurrent equivalent of 'StreamT'. The type 'AsyncT'
437+-- | Like 'StreamT' but /may/ run each iteration concurrently using demand
405−-- follows the same composition scheme as 'StreamT' except that it can run
406−-- multiple iterations concurrently. More concurrent iterations are started
438+-- driven concurrency. More concurrent iterations are started only if the
407−-- only if the previous iterations are not able to produce enough output for
439+-- previous iterations are not able to produce enough output for the consumer.
408−-- the consumer.
409--
440--
410−-- Just like 'StreamT', 'AsyncT' style composition works well operationally
441+-- @
411−-- for infinite streams. However, if the second stream is infinite we will
442+-- import "Streamly"
412−-- never be able to start the second iteration of the first stream.
443+-- import Control.Concurrent
413−
444+--
445+-- main = 'runAsyncT' $ do
446+-- n <- return 3 \<\> return 2 \<\> return 1
447+-- liftIO $ do
448+-- threadDelay (n * 1000000)
449+-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
450+-- @
451+-- @
452+-- ThreadId 40: Delay 1
453+-- ThreadId 39: Delay 2
454+-- ThreadId 38: Delay 3
455+-- @
456+--
457+-- All iterations may run in the same thread if they do not block.
414newtype AsyncT m a = AsyncT {getAsyncT :: Stream m a}
458newtype AsyncT m a = AsyncT {getAsyncT :: Stream m a}
415 deriving (Semigroup, Monoid, MonadTrans)
459 deriving (Semigroup, Monoid, MonadTrans)
416
460
@@ -514,17 +558,27 @@ instance (MonadAsync m, Floating a) => Floating (AsyncT m a) where
514-- ParallelT
558-- ParallelT
515------------------------------------------------------------------------------
559------------------------------------------------------------------------------
516
560
517−-- | 'ParallelT' is the concurrent equivalent of 'InterleavedT'. It follows the
561+-- | Like 'StreamT' but runs /all/ iterations fairly concurrently using a round
518−-- same composition scheme as 'InterleavedT' i.e. fairly interleaving the inner
562+-- robin scheduling.
519−-- and outer loops in a round robin fashion except that it can run multiple
563+--
520−-- iterations concurrently.
564+-- @
565+-- import "Streamly"
566+-- import Control.Concurrent
521--
567--
522−-- Note that, just like 'InterleavedT', 'ParallelT' style composition cannot
568+-- main = 'runParallelT' $ do
523−-- work well operationally on infinite streams. Because earlier iterations do
569+-- n <- return 3 \<\> return 2 \<\> return 1
524−-- not finish before the new ones are started, it will keep on accumulating the
570+-- liftIO $ do
525−-- elements of the stream resulting in continuous increase in space
571+-- threadDelay (n * 1000000)
526−-- consumption.
572+-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
573+-- @
574+-- @
575+-- ThreadId 40: Delay 1
576+-- ThreadId 39: Delay 2
577+-- ThreadId 38: Delay 3
578+-- @
527--
579--
580+-- Unlike 'AsyncT' all iterations are guaranteed to run fairly concurrently,
581+-- unconditionally.
528newtype ParallelT m a = ParallelT {getParallelT :: Stream m a}
582newtype ParallelT m a = ParallelT {getParallelT :: Stream m a}
529 deriving (Semigroup, Monoid, MonadTrans)
583 deriving (Semigroup, Monoid, MonadTrans)
530
584
@@ -627,8 +681,22 @@ zipWith f m1 m2 = fromStream $ go (toStream m1) (toStream m2)
627 yield1 a (Just ra) = merge a ra
681 yield1 a (Just ra) = merge a ra
628 (runStream mx) Nothing stp yield1
682 (runStream mx) Nothing stp yield1
629
683
630−-- | 'ZipStream' zips serially i.e. it produces one element from each stream in
684+-- | 'ZipStream' zips serially i.e. it produces one element from each stream
631−-- a serial manner and then zips the two elements.
685+-- serially and then zips the two elements. Note, for convenience we have used
686+-- the 'zipping' combinator in the following example instead of using a type
687+-- annotation.
688+--
689+-- @
690+-- main = (toList . 'zipping' $ (,) \<$\> s1 \<*\> s2) >>= print
691+-- where s1 = pure 1 <> pure 2
692+-- s2 = pure 3 <> pure 4
693+-- @
694+-- @
695+-- [(1,3),(2,4)]
696+-- @
697+--
698+-- This applicative operation can be seen as the zipping equivalent of
699+-- interleaving with '<=>'.
632newtype ZipStream m a = ZipStream {getZipStream :: Stream m a}
700newtype ZipStream m a = ZipStream {getZipStream :: Stream m a}
633 deriving (Semigroup, Monoid)
701 deriving (Semigroup, Monoid)
634
702
@@ -702,8 +770,20 @@ zipAsyncWith f m1 m2 = fromStream $ Stream $ \_ stp yld -> do
702 mb <- async m2
770 mb <- async m2
703 (runStream (toStream (zipWith f ma mb))) Nothing stp yld
771 (runStream (toStream (zipWith f ma mb))) Nothing stp yld
704
772
705−-- | 'ZipAsync' zips in parallel, it produces one element from each stream
773+-- | Like 'ZipStream' but zips in parallel, it generates both the elements to
706−-- concurrently and then zips the two.
774+-- be zipped concurrently.
775+--
776+-- @
777+-- main = (toList . 'zippingAsync' $ (,) \<$\> s1 \<*\> s2) >>= print
778+-- where s1 = pure 1 <> pure 2
779+-- s2 = pure 3 <> pure 4
780+-- @
781+-- @
782+-- [(1,3),(2,4)]
783+-- @
784+--
785+-- This applicative operation can be seen as the zipping equivalent of
786+-- parallel composition with '<|>'.
707newtype ZipAsync m a = ZipAsync {getZipAsync :: Stream m a}
787newtype ZipAsync m a = ZipAsync {getZipAsync :: Stream m a}
708 deriving (Semigroup, Monoid)
788 deriving (Semigroup, Monoid)
709
789
@@ -800,27 +880,27 @@ zippingAsync x = x
800-- Running Streams, convenience functions specialized to types
880-- Running Streams, convenience functions specialized to types
801-------------------------------------------------------------------------------
881-------------------------------------------------------------------------------
802
882
803−-- | Run a 'StreamT' computation. Same as @runStreaming . serially@.
883+-- | Same as @runStreaming . serially@.
804runStreamT :: Monad m => StreamT m a -> m ()
884runStreamT :: Monad m => StreamT m a -> m ()
805runStreamT = runStreaming
885runStreamT = runStreaming
806
886
807−-- | Run an 'InterleavedT' computation. Same as @runStreaming . interleaving@.
887+-- | Same as @runStreaming . interleaving@.
808runInterleavedT :: Monad m => InterleavedT m a -> m ()
888runInterleavedT :: Monad m => InterleavedT m a -> m ()
809runInterleavedT = runStreaming
889runInterleavedT = runStreaming
810
890
811−-- | Run an 'AsyncT' computation. Same as @runStreaming . asyncly@.
891+-- | Same as @runStreaming . asyncly@.
812runAsyncT :: Monad m => AsyncT m a -> m ()
892runAsyncT :: Monad m => AsyncT m a -> m ()
813runAsyncT = runStreaming
893runAsyncT = runStreaming
814
894
815−-- | Run a 'ParallelT' computation. Same as @runStreaming . parallely@.
895+-- | Same as @runStreaming . parallely@.
816runParallelT :: Monad m => ParallelT m a -> m ()
896runParallelT :: Monad m => ParallelT m a -> m ()
817runParallelT = runStreaming
897runParallelT = runStreaming
818
898
819−-- | Run a 'ZipStream' computation. Same as @runStreaming . zipping@.
899+-- | Same as @runStreaming . zipping@.
820runZipStream :: Monad m => ZipStream m a -> m ()
900runZipStream :: Monad m => ZipStream m a -> m ()
821runZipStream = runStreaming
901runZipStream = runStreaming
822
902
823−-- | Run a 'ZipAsync' computation. Same as @runStreaming . zippingAsync@.
903+-- | Same as @runStreaming . zippingAsync@.
824runZipAsync :: Monad m => ZipAsync m a -> m ()
904runZipAsync :: Monad m => ZipAsync m a -> m ()
825runZipAsync = runStreaming
905runZipAsync = runStreaming
826
906
@@ -831,21 +911,40 @@ runZipAsync = runStreaming
831infixr 5 <=>
911infixr 5 <=>
832
912
833-- | Sequential interleaved composition, in contrast to '<>' this operator
913-- | Sequential interleaved composition, in contrast to '<>' this operator
834−-- fairly interleaves the two streams instead of appending them; yielding one
914+-- fairly interleaves two streams instead of appending them; yielding one
835−-- element from each stream alternately. Unlike '<>' it cannot be used to fold
915+-- element from each stream alternately.
836−-- an infinite container of streams. This operator corresponds to the
916+--
837−-- 'InterleavedT' type for product style composition.
917+-- @
918+-- main = ('toList' . 'serially' $ (return 1 <> return 2) \<=\> (return 3 <> return 4)) >>= print
919+-- @
920+-- @
921+-- [1,3,2,4]
922+-- @
923+--
924+-- This operator corresponds to the 'InterleavedT' style. Unlike '<>', this
925+-- operator cannot be used to fold infinite containers since that might
926+-- accumulate too many partially drained streams. To be clear, it can combine
927+-- infinite streams but not infinite number of streams.
838{-# INLINE (<=>) #-}
928{-# INLINE (<=>) #-}
839(<=>) :: Streaming t => t m a -> t m a -> t m a
929(<=>) :: Streaming t => t m a -> t m a -> t m a
840m1 <=> m2 = fromStream $ interleave (toStream m1) (toStream m2)
930m1 <=> m2 = fromStream $ interleave (toStream m1) (toStream m2)
841
931
842−-- | Parallel interleaved composition, in contrast to '<|>' this operator
932+-- | Demand driven concurrent composition. In contrast to '<|>' this operator
843−-- "merges" streams in a left biased manner rather than fairly interleaving
933+-- concurrently "merges" streams in a left biased manner rather than fairly
844−-- them. It keeps yielding from the stream on the left as long as it can. If
934+-- interleaving them. It keeps yielding from the stream on the left as long as
845−-- the left stream blocks or cannot keep up with the pace of the consumer it
935+-- it can. If the left stream blocks or cannot keep up with the pace of the
846−-- can yield from the stream on the right in parallel. Unlike '<|>' it can be
936+-- consumer it can concurrently yield from the stream on the right in parallel.
937+--
938+-- @
939+-- main = ('toList' . 'serially' $ (return 1 <> return 2) \<| (return 3 <> return 4)) >>= print
940+-- @
941+-- @
942+-- [1,2,3,4]
943+-- @
944+--
847−-- used to fold infinite containers of streams. This operator corresponds to
945+-- Unlike '<|>' it can be used to fold infinite containers of streams. This
848−-- the 'AsyncT' type for product style composition.
946+-- operator corresponds to the 'AsyncT' type for product style composition.
947+--
849{-# INLINE (<|) #-}
948{-# INLINE (<|) #-}
850(<|) :: (Streaming t, MonadAsync m) => t m a -> t m a -> t m a
949(<|) :: (Streaming t, MonadAsync m) => t m a -> t m a -> t m a
851m1 <| m2 = fromStream $ parLeft (toStream m1) (toStream m2)
950m1 <| m2 = fromStream $ parLeft (toStream m1) (toStream m2)
src/Streamly/Tutorial.hs+2−1
@@ -244,7 +244,8 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
244-- @
244-- @
245--
245--
246-- Note that this operator cannot be used to fold infinite containers since it
246-- Note that this operator cannot be used to fold infinite containers since it
247−-- requires preserving the state until a stream is finished.
247+-- requires preserving the state until a stream is finished. To be clear, it
248+-- can combine infinite streams but not infinite number of streams.
248
249
249-- $parallel
250-- $parallel
250--
251--