@@ -19,6 +19,7 @@
19module Asyncly.Prelude
19module Asyncly.Prelude
21 -- * Product Style Composition
21 -- * Product Style Composition
23 , InterleavedT
24 , InterleavedT
@@ -66,13 +67,58 @@ import Data.Semigroup (Semigroup(..), cycle1)
66import Prelude hiding (drop, take, zipWith)
67import Prelude hiding (drop, take, zipWith)
67import Asyncly.AsyncT
68import Asyncly.AsyncT
72+-- Different styles of streams dictate the manner in which a stream is
73+-- processed in monadic or applicative composition. Each style is represented
74+-- by a different type. The types have the same underlying representation and
75+-- therefore can be converted back and forth easily to switch the composition
76+-- style at any point. In other words, processing order of the elements or
77+-- processing parallelism is decided by the type of the stream.
79+-- 'StreamT' and 'InterleavedT' process the streams serially but differ in the
80+-- order in which the elements of the streams are processed. 'StreamT' goes
81+-- depth first that is it combine one element of the first stream to every
82+-- element of the second stream before it comes back to process th second
83+-- element of the first stream. 'InterleavedT' takes turns when processing
84+-- elements from each stream and therefore all iterations finish at the same
85+-- time rather than one iteration at a time as in the case of 'StreamT'.
86+-- Depending on the cache behavior or IO behavior 'InterleavedT' might be
87+-- slightly more efficient than 'StreamT' or vice-versa.
89+-- 'AsyncT' and 'ParallelT' can process the individual elements of the streams
90+-- in parallel. 'AsyncT' is the parallel version of 'StreamT', it follows the
91+-- order of 'StreamT' processing multiple elements of a stream concurrently.
92+-- 'ParallelT' follows the order of 'InterleavedT' processing and interleaving
93+-- both streams concurrently. Also, note that 'fmap' on these streams would
94+-- also work in a concurrent fashion i.e. multiple elements in a stream or
95+-- multiple streams can be mapped concurrently.
97+-- Zipping types have a different style of composition altogether, they zip the
98+-- streams rather than multiplying them. These types provide only Applicative
99+-- composition and no Monad instances. 'ZipStream' zips serially i.e. it
100+-- produces one element from each stream in a serial manner and then zips the
101+-- two items. 'ZipAsync' on the other hand zips in parallel, it produces one
102+-- element from each stream concurrently and then zips the two.
104+-- It may help develop a better understanding if we think in terms of stream
105+-- generation (production) side parallelism versus stream processing
106+-- (consumption) side parallelism. The sum style operators (e.g. '<|' or '<|>')
107+-- generate streams in parallel and then combine the results into a unified
108+-- stream. On the other hand the product style composition (e.g. 'AsyncT' or
109+-- 'ParallelT' or 'ZipAsync') dictate how the elements of a stream are
110+-- processed when it is consumed.
69------------------------------------------------------------------------------
112------------------------------------------------------------------------------
70-- StreamT iterates serially in the Monad and Applicative compositions
113-- StreamT iterates serially in the Monad and Applicative compositions
71------------------------------------------------------------------------------
114------------------------------------------------------------------------------
116+-- | Note that 'StreamT' style composition works well operationally for
117+-- infinite streams. However if the second stream is infinite we will never
118+-- come back to the second iteration of the first stream.
73newtype StreamT m a = StreamT {getStreamT :: Stream m a}
120newtype StreamT m a = StreamT {getStreamT :: Stream m a}
74− deriving (Functor, Applicative, Monad, Semigroup, Monoid,
75− MonadTrans, MonadIO, MonadThrow)
121+ deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
77deriving instance MonadAsync m => Alternative (StreamT m)
123deriving instance MonadAsync m => Alternative (StreamT m)
78deriving instance MonadAsync m => MonadPlus (StreamT m)
124deriving instance MonadAsync m => MonadPlus (StreamT m)
@@ -90,6 +136,36 @@ instance Streaming StreamT where
90-- from a single base type because they depend on the Monad instance which is
136-- from a single base type because they depend on the Monad instance which is
91-- different for each type.
137-- different for each type.
139+------------------------------------------------------------------------------
141+------------------------------------------------------------------------------
143+-- | Execute a monadic action sequentially for each element in the 'AsyncT'
144+-- stream, i.e. an iteration finishes completely before the next one starts.
145+instance Monad m => Monad (StreamT m) where
147+ (StreamT (Stream m)) >>= f = StreamT $ Stream $ \_ stp yld ->
148+ let run x = (runStream x) Nothing stp yld
149+ yield a Nothing = run $ getStreamT (f a)
150+ yield a (Just r) = run $ getStreamT (f a)
151+ <> getStreamT (StreamT r >>= f)
152+ in m Nothing stp yield
154+------------------------------------------------------------------------------
156+------------------------------------------------------------------------------
158+instance Monad m => Applicative (StreamT m) where
159+ pure = StreamT . yielding
162+------------------------------------------------------------------------------
164+------------------------------------------------------------------------------
166+instance Monad m => Functor (StreamT m) where
93------------------------------------------------------------------------------
169------------------------------------------------------------------------------
95------------------------------------------------------------------------------
171------------------------------------------------------------------------------
@@ -136,9 +212,13 @@ instance (Monad m, Floating a) => Floating (StreamT m a) where
137------------------------------------------------------------------------------
213------------------------------------------------------------------------------
138-- InterleavedT interleaves the iterations in the Monad and Applicative
214-- InterleavedT interleaves the iterations in the Monad and Applicative
139−-- compositions. It does not make much sense for infinite streams.
215+-- compositions.
140------------------------------------------------------------------------------
216------------------------------------------------------------------------------
218+-- | Note that 'InterleavedT' style composition cannot work well operationally
219+-- on infinite streams. It will keep accumulating the elements of the stream
220+-- resulting in continuous increase in space consumption.
142newtype InterleavedT m a = InterleavedT {getInterleavedT :: Stream m a}
222newtype InterleavedT m a = InterleavedT {getInterleavedT :: Stream m a}
143 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
223 deriving (Semigroup, Monoid, MonadTrans, MonadIO, MonadThrow)
@@ -162,16 +242,9 @@ instance Monad m => Monad (InterleavedT m) where
162 yield a Nothing = run $ getInterleavedT (f a)
242 yield a Nothing = run $ getInterleavedT (f a)
163 yield a (Just r) = run $ getInterleavedT (f a)
243 yield a (Just r) = run $ getInterleavedT (f a)
164 `interleave`
244 `interleave`
165− getInterleavedT ((InterleavedT r) >>= f)
245+ getInterleavedT (InterleavedT r >>= f)
166 in m Nothing stp yield
246 in m Nothing stp yield
168−------------------------------------------------------------------------------
170−------------------------------------------------------------------------------
172−instance Monad m => Functor (InterleavedT m) where
175------------------------------------------------------------------------------
248------------------------------------------------------------------------------
176-- Applicative
249-- Applicative
177------------------------------------------------------------------------------
250------------------------------------------------------------------------------
@@ -180,6 +253,13 @@ instance Monad m => Applicative (InterleavedT m) where
180 pure = InterleavedT . yielding
253 pure = InterleavedT . yielding
181 (<*>) = ap
254 (<*>) = ap
256+------------------------------------------------------------------------------
258+------------------------------------------------------------------------------
260+instance Monad m => Functor (InterleavedT m) where
183------------------------------------------------------------------------------
263------------------------------------------------------------------------------
185------------------------------------------------------------------------------
265------------------------------------------------------------------------------
@@ -230,14 +310,20 @@ instance (Monad m, Floating a) => Floating (InterleavedT m a) where
230-- with infinite streams.
310-- with infinite streams.
231------------------------------------------------------------------------------
311------------------------------------------------------------------------------
313+-- | Just like 'StreamT', 'AsyncT' style composition works well operationally
314+-- for infinite streams. However, if the second stream is infinite we will
315+-- never be able to finish the first stream.
317+-- Note that 'fmap' on 'AsyncT' would map on multiple elements concurrently.
233newtype AsyncT m a = AsyncT {getAsyncT :: Stream m a}
319newtype AsyncT m a = AsyncT {getAsyncT :: Stream m a}
234 deriving (Semigroup, Monoid, MonadTrans)
320 deriving (Semigroup, Monoid, MonadTrans)
322+deriving instance MonadAsync m => Alternative (AsyncT m)
323+deriving instance MonadAsync m => MonadPlus (AsyncT m)
236deriving instance MonadAsync m => MonadIO (AsyncT m)
324deriving instance MonadAsync m => MonadIO (AsyncT m)
237deriving instance MonadAsync m => MonadThrow (AsyncT m)
325deriving instance MonadAsync m => MonadThrow (AsyncT m)
238deriving instance (MonadBase b m, MonadAsync m) => MonadBase b (AsyncT m)
326deriving instance (MonadBase b m, MonadAsync m) => MonadBase b (AsyncT m)
239−deriving instance MonadAsync m => Alternative (AsyncT m)
240−deriving instance MonadAsync m => MonadPlus (AsyncT m)
241deriving instance (MonadError e m, MonadAsync m) => MonadError e (AsyncT m)
327deriving instance (MonadError e m, MonadAsync m) => MonadError e (AsyncT m)
242deriving instance (MonadReader r m, MonadAsync m) => MonadReader r (AsyncT m)
328deriving instance (MonadReader r m, MonadAsync m) => MonadReader r (AsyncT m)
243deriving instance (MonadState s m, MonadAsync m) => MonadState s (AsyncT m)
329deriving instance (MonadState s m, MonadAsync m) => MonadState s (AsyncT m)
@@ -270,13 +356,6 @@ instance MonadAsync m => Monad (AsyncT m) where
270 where g x = getAsyncT (f x)
356 where g x = getAsyncT (f x)
271 par = parallel (CtxType Conjunction LIFO)
357 par = parallel (CtxType Conjunction LIFO)
273−------------------------------------------------------------------------------
275−------------------------------------------------------------------------------
277−instance MonadAsync m => Functor (AsyncT m) where
280------------------------------------------------------------------------------
359------------------------------------------------------------------------------
281-- Applicative
360-- Applicative
282------------------------------------------------------------------------------
361------------------------------------------------------------------------------
@@ -285,6 +364,13 @@ instance MonadAsync m => Applicative (AsyncT m) where
285 pure = AsyncT . yielding
364 pure = AsyncT . yielding
286 (<*>) = ap
365 (<*>) = ap
367+------------------------------------------------------------------------------
369+------------------------------------------------------------------------------
371+instance MonadAsync m => Functor (AsyncT m) where
288------------------------------------------------------------------------------
374------------------------------------------------------------------------------
290------------------------------------------------------------------------------
376------------------------------------------------------------------------------
@@ -331,18 +417,24 @@ instance (MonadAsync m, Floating a) => Floating (AsyncT m a) where
332------------------------------------------------------------------------------
418------------------------------------------------------------------------------
333-- ParallelT runs all iterations in parallel in the Monad and Applicative
419-- ParallelT runs all iterations in parallel in the Monad and Applicative
334−-- compositions. Iterations are scheduled in a round-robin fashion. It cannot
420+-- compositions. Iterations are scheduled in a round-robin fashion.
335−-- work well when the streams being composed are infinite.
336------------------------------------------------------------------------------
421------------------------------------------------------------------------------
423+-- | Note that, just like 'InterleavedT', 'ParallelT' style composition cannot
424+-- work well operationally on infinite streams. It will keep accumulating the
425+-- elements of the stream resulting in continuous increase in space
428+-- Note that 'fmap' on 'ParallelT' would map on both streams concurrently.
338newtype ParallelT m a = ParallelT {getParallelT :: Stream m a}
430newtype ParallelT m a = ParallelT {getParallelT :: Stream m a}
339 deriving (Semigroup, Monoid, MonadTrans)
431 deriving (Semigroup, Monoid, MonadTrans)
433+deriving instance MonadAsync m => Alternative (ParallelT m)
434+deriving instance MonadAsync m => MonadPlus (ParallelT m)
341deriving instance MonadAsync m => MonadIO (ParallelT m)
435deriving instance MonadAsync m => MonadIO (ParallelT m)
342deriving instance MonadAsync m => MonadThrow (ParallelT m)
436deriving instance MonadAsync m => MonadThrow (ParallelT m)
343deriving instance (MonadBase b m, MonadAsync m) => MonadBase b (ParallelT m)
437deriving instance (MonadBase b m, MonadAsync m) => MonadBase b (ParallelT m)
344−deriving instance MonadAsync m => Alternative (ParallelT m)
345−deriving instance MonadAsync m => MonadPlus (ParallelT m)
346deriving instance (MonadError e m, MonadAsync m) => MonadError e (ParallelT m)
438deriving instance (MonadError e m, MonadAsync m) => MonadError e (ParallelT m)
347deriving instance (MonadReader r m, MonadAsync m) => MonadReader r (ParallelT m)
439deriving instance (MonadReader r m, MonadAsync m) => MonadReader r (ParallelT m)
348deriving instance (MonadState s m, MonadAsync m) => MonadState s (ParallelT m)
440deriving instance (MonadState s m, MonadAsync m) => MonadState s (ParallelT m)
@@ -360,13 +452,6 @@ instance MonadAsync m => Monad (ParallelT m) where
360 where g x = getParallelT (f x)
452 where g x = getParallelT (f x)
361 par = parallel (CtxType Conjunction FIFO)
453 par = parallel (CtxType Conjunction FIFO)
363−------------------------------------------------------------------------------
365−------------------------------------------------------------------------------
367−instance MonadAsync m => Functor (ParallelT m) where
370------------------------------------------------------------------------------
455------------------------------------------------------------------------------
371-- Applicative
456-- Applicative
372------------------------------------------------------------------------------
457------------------------------------------------------------------------------
@@ -375,6 +460,13 @@ instance MonadAsync m => Applicative (ParallelT m) where
375 pure = ParallelT . yielding
460 pure = ParallelT . yielding
376 (<*>) = ap
461 (<*>) = ap
463+------------------------------------------------------------------------------
465+------------------------------------------------------------------------------
467+instance MonadAsync m => Functor (ParallelT m) where
378------------------------------------------------------------------------------
470------------------------------------------------------------------------------
380------------------------------------------------------------------------------
472------------------------------------------------------------------------------
@@ -451,13 +543,19 @@ zipWith f m1 m2 = fromStream $ go (toStream m1) (toStream m2)
451 yield1 a (Just ra) = merge a ra
543 yield1 a (Just ra) = merge a ra
452 (runStream mx) Nothing stp yield1
544 (runStream mx) Nothing stp yield1
454−-- | Wrapper around AsyncT type with a serial zipping Applicative instance.
546+-- | 'ZipStream' zips serially i.e. it produces one element from each stream in
455−-- Note that the binary function interleave (\<=>) is a special case of
547+-- a serial manner and then zips the two elements.
456−-- ZipStream Applicative.
458−-- > f <$> ZipStream xs1 <*> ... <*> ZipStream xsN
459newtype ZipStream m a = ZipStream {getZipStream :: Stream m a}
548newtype ZipStream m a = ZipStream {getZipStream :: Stream m a}
460− deriving (Functor)
549+ deriving (Semigroup, Monoid)
551+deriving instance MonadAsync m => Alternative (ZipStream m)
553+instance Monad m => Functor (ZipStream m) where
554+ fmap f (ZipStream (Stream m)) = ZipStream $ Stream $ \_ stp yld ->
555+ let yield a Nothing = yld (f a) Nothing
556+ yield a (Just r) = yld (f a)
557+ (Just (getZipStream (fmap f (ZipStream r))))
558+ in m Nothing stp yield
462instance Monad m => Applicative (ZipStream m) where
560instance Monad m => Applicative (ZipStream m) where
463 pure a = ZipStream $ cycle1 (pure a)
561 pure a = ZipStream $ cycle1 (pure a)
@@ -508,7 +606,7 @@ instance (Monad m, Floating a) => Floating (ZipStream m a) where
508 logBase = liftA2 logBase
606 logBase = liftA2 logBase
510------------------------------------------------------------------------------
608------------------------------------------------------------------------------
511−-- Asyncly Zipping Streams
609+-- Parallely Zipping Streams
512------------------------------------------------------------------------------
610------------------------------------------------------------------------------
514-- | Zip two AsyncT streams asyncly (i.e. both the streams are generated
612-- | Zip two AsyncT streams asyncly (i.e. both the streams are generated
@@ -529,13 +627,19 @@ zipAsyncWith f m1 m2 = fromStream $ Stream $ \_ stp yld -> do
529 mb <- async m2
627 mb <- async m2
530 (runStream (toStream (zipWith f ma mb))) Nothing stp yld
628 (runStream (toStream (zipWith f ma mb))) Nothing stp yld
532−-- | Wrapper around AsyncT type with a parallel zipping Applicative instance.
630+-- | 'ZipAsync' zips in parallel, it produces one element from each stream
533−-- Note that the binary operator (\<|>) from the Alternative instance of AsyncT
631+-- concurrently and then zips the two.
534−-- is a special case of ZipAsync Applicative.
536−-- > f <$> ZipAsync xs1 <*> ... <*> ZipAsync xsN
537newtype ZipAsync m a = ZipAsync {getZipAsync :: Stream m a}
632newtype ZipAsync m a = ZipAsync {getZipAsync :: Stream m a}
538− deriving (Functor)
633+ deriving (Semigroup, Monoid)
635+deriving instance MonadAsync m => Alternative (ZipAsync m)
637+instance Monad m => Functor (ZipAsync m) where
638+ fmap f (ZipAsync (Stream m)) = ZipAsync $ Stream $ \_ stp yld ->
639+ let yield a Nothing = yld (f a) Nothing
640+ yield a (Just r) = yld (f a)
641+ (Just (getZipAsync (fmap f (ZipAsync r))))
642+ in m Nothing stp yield
540instance MonadAsync m => Applicative (ZipAsync m) where
644instance MonadAsync m => Applicative (ZipAsync m) where
541 pure a = ZipAsync $ cycle1 (pure a)
645 pure a = ZipAsync $ cycle1 (pure a)