2-- Module : Streamly.Internal.Data.Stream.Async
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9-- Non-parallelizable stream combinators like unfoldrM, iterateM etc. can be
10-- evaluated concurrently with the stream consumer by using `eval`.
11-- Parallelizable combinators like repeatM, replicateM can generate the stream
12-- concurrently using 'concatMap'.
14-- Single effects related functionality can be moved to
15-- Data.Async/Control.Async.
16-- Common Channel functionality to Data.Channel.
17-- Stream channel to Data.Stream.Channel.
19module Streamly.Internal.Data.Stream.Async
40 -- | Stream combinators using Async channel
42 -- XXX Move to parallel module
51 -- XXX experimental binary ops, move to parallel?
64 , concatMapInterleaveWith
68import Streamly.Internal.Control.Concurrent (MonadAsync)
69import Streamly.Internal.Data.Stream.Type (Stream)
71import qualified Streamly.Internal.Data.Stream as Stream
73import Streamly.Internal.Data.Stream.Async.Channel
74import Prelude hiding (mapM, sequence, concat, concatMap)
78-- Imports for example snippets in this module.
81-- >>> import Control.Concurrent (threadDelay)
82-- >>> import qualified Streamly.Data.Array.Unboxed as Array
83-- >>> import qualified Streamly.Data.Fold as Fold
84-- >>> import qualified Streamly.Data.Parser as Parser
85-- >>> import qualified Streamly.Data.Stream as Stream
86-- >>> import qualified Streamly.Internal.Data.Stream.Async as Async
87-- >>> import Prelude hiding (concatMap, concat)
90-- threadDelay (n * 1000000) -- sleep for n seconds
91-- putStrLn (show n ++ " sec") -- print "n sec"
95-------------------------------------------------------------------------------
97-------------------------------------------------------------------------------
99-- | Like 'eval' but can specify a config modifier to change the concurrent
100-- channel parameters.
101{-# INLINE evalWith #-}
102evalWith :: MonadAsync m => (Config -> Config) -> Stream m a -> Stream m a
104 -- Stream.fromStreamD $ evalWithD cfg $ Stream.toStreamD stream
105 Stream.fromStreamK $ evalWithK cfg $ Stream.toStreamK stream
107-- | Evaluate a stream asynchronously using a channel and serve the consumer
108-- from the evaluation buffer.
110-- >>> eval = Async.evalWith id
113eval :: MonadAsync m => Stream m a -> Stream m a
116-------------------------------------------------------------------------------
117-- Concurrent combinators for "Stream" type
118-------------------------------------------------------------------------------
120-- | Like 'append' but with a Config modifier.
121{-# INLINE appendWith #-}
122appendWith :: MonadAsync m =>
123 (Config -> Config) -> Stream m a -> Stream m a -> Stream m a
124appendWith modifier stream1 stream2 =
127 modifier (Stream.toStreamK stream1) (Stream.toStreamK stream2)
129-- | Like 'interleave' but with a Config modifier.
130{-# INLINE interleaveWith #-}
131interleaveWith :: MonadAsync m =>
132 (Config -> Config) -> Stream m a -> Stream m a -> Stream m a
133interleaveWith modifier stream1 stream2 =
136 modifier (Stream.toStreamK stream1) (Stream.toStreamK stream2)
138-- | Binary operation to evaluate two streams concurrently prioritizing the
141-- If you want to combine more than two streams you almost always want the
142-- 'concat' operation instead. The performance of this operation degrades
143-- rapidly when more streams are combined as each operation adds one more
144-- concurrent channel. On the other hand, 'concat' uses a single channel for
145-- all streams. However, with this operation you can precisely control the
146-- scheduling by creating arbitrary shape expression trees.
148-- >>> append = Async.appendWith id
150-- The following code finishes in 4 seconds:
152-- >>> stream1 = Stream.fromEffect (delay 4)
153-- >>> stream2 = Stream.fromEffect (delay 2)
154-- >>> Stream.fold Fold.toList $ stream1 `Async.append` stream2
160append :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
161append = appendWith id
163-- | Like 'append' but interleaves the streams fairly instead of prioritizing
164-- the left stream. This schedules all streams in a round robin fashion over
165-- limited number of threads.
167-- >>> interleave = Async.interleaveWith id
169{-# INLINE interleave #-}
170interleave :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
171interleave = interleaveWith id
173-- concatMapWith modifier f stream = concatWith modifier $ fmap f stream
175-- | Like 'concatMap' but we can also specify the concurrent channel's
176-- configuration parameters using Config modifiers.
178{-# INLINE concatMapWith #-}
179concatMapWith :: MonadAsync m =>
180 (Config -> Config) -> (a -> Stream m b) -> Stream m a -> Stream m b
181concatMapWith modifier f stream =
184 modifier (Stream.toStreamK . f) (Stream.toStreamK stream)
186-- | Like 'concatMapInterleave' but we can also specify the concurrent
187-- channel's configuration parameters using Config modifiers.
189{-# INLINE concatMapInterleaveWith #-}
190concatMapInterleaveWith :: MonadAsync m =>
191 (Config -> Config) -> (a -> Stream m b) -> Stream m a -> Stream m b
192concatMapInterleaveWith modifier f stream =
194 $ concatMapInterleaveWithK
195 modifier (Stream.toStreamK . f) (Stream.toStreamK stream)
197-- | Map each element of the input to a stream and then concurrently evaluate
198-- and concatenate the resulting streams. Multiple streams may be evaluated
199-- concurrently but earlier streams are perferred. Output from the streams are
200-- used as they arrive.
202-- >>> concatMap = Async.concatMapWith id
204-- >>> f cfg xs = Stream.fold Fold.toList $ Async.concatMapWith cfg id $ Stream.fromList xs
206-- The following streams finish in 4 seconds:
208-- >>> stream1 = Stream.fromEffect (delay 4)
209-- >>> stream2 = Stream.fromEffect (delay 2)
210-- >>> stream3 = Stream.fromEffect (delay 1)
211-- >>> f id [stream1, stream2, stream3]
217-- Limiting threads to 2 schedules the third stream only after one of the first
218-- two has finished, releasing a thread:
220-- >>> f (Async.maxThreads 2) [stream1, stream2, stream3]
224-- When used with a Single thread it behaves like serial concatMap:
226-- >>> f (Async.maxThreads 1) [stream1, stream2, stream3]
230-- >>> stream1 = Stream.fromList [1,2,3]
231-- >>> stream2 = Stream.fromList [4,5,6]
232-- >>> f (Async.maxThreads 1) [stream1, stream2]
235{-# INLINE concatMap #-}
236concatMap :: MonadAsync m => (a -> Stream m b) -> Stream m a -> Stream m b
237concatMap = concatMapWith id
239-- | Map each element of the input to a stream and then concurrently evaluate
240-- and interleave the resulting streams. Unlike 'concatMap' which prefers to
241-- evaluate the earlier stream first, this schedules all streams in a round
242-- robin fashion over the available threads.
244-- >>> concatMapInterleave = Async.concatMapInterleaveWith id
246-- When used with a single thread it behaves like serial interleaving:
248-- >>> f cfg xs = Stream.fold Fold.toList $ Async.concatMapInterleaveWith cfg id $ Stream.fromList xs
250-- >>> stream1 = Stream.fromList [1,2,3]
251-- >>> stream2 = Stream.fromList [4,5,6]
252-- >>> f (Async.maxThreads 1) [stream1, stream2]
255-- /Works only on finite number of streams/
257{-# INLINE concatMapInterleave #-}
258concatMapInterleave :: MonadAsync m => (a -> Stream m b) -> Stream m a -> Stream m b
259concatMapInterleave = concatMapInterleaveWith id
261-- | Like 'concat' but we can also specify the concurrent channel's
262-- configuration parameters using Config modifiers.
264-- >>> concatWith modifier = Async.concatMapWith modifier id
266{-# INLINE concatWith #-}
267concatWith :: MonadAsync m =>
268 (Config -> Config) -> Stream m (Stream m a) -> Stream m a
269concatWith modifier = concatMapWith modifier id
271-- | Evaluate the streams in the input stream concurrently and combine them.
273-- >>> concat = Async.concatWith id
276concat :: MonadAsync m => Stream m (Stream m a) -> Stream m a
277concat = concatWith id
279-- | Like 'concat' but works on a list of streams.
281-- >>> concatList = Async.concat . Stream.fromList
283{-# INLINE concatList #-}
284concatList :: MonadAsync m => [Stream m a] -> Stream m a
285concatList = concat . Stream.fromList
288{-# SPECIALIZE apply :: Stream IO (a -> b) -> Stream IO a -> Stream IO b #-}
289apply :: MonadAsync m => Stream m (a -> b) -> Stream m a -> Stream m b
290apply stream1 stream2 = concatMap (\g -> concatMap (pure . g) stream2) stream1
293-- >>> mapMWith modifier f = Async.concatMapWith modifier (Stream.fromEffect . f)
295{-# INLINE mapMWith #-}
296mapMWith :: MonadAsync m =>
297 (Config -> Config) -> (a -> m b) -> Stream m a -> Stream m b
298mapMWith modifier f = concatMapWith modifier (Stream.fromEffect . f)
301-- >>> mapM = Async.mapMWith id
303mapM :: MonadAsync m => (a -> m b) -> Stream m a -> Stream m b
307-- >>> sequenceWith modifier = Async.mapMWith modifier id
309{-# INLINE sequenceWith #-}
310sequenceWith :: MonadAsync m =>
311 (Config -> Config) -> Stream m (m a) -> Stream m a
312sequenceWith modifier = mapMWith modifier id
315-- >>> sequence = Async.sequenceWith id
317{-# INLINE sequence #-}
318sequence :: MonadAsync m =>
319 Stream m (m a) -> Stream m a
320sequence = sequenceWith id
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list