on 3feeec2 help sign in
816 lines 28 kB history raw
1-- |
2-- Module : Streamly.Internal.Data.Stream.IsStream.Expand
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
7-- Portability : GHC
8--
9-- Expand a stream by combining two or more streams or by combining streams
10-- with unfolds.
12module Streamly.Internal.Data.Stream.IsStream.Expand
13 (
14 -- * Binary Combinators (Linear)
15 -- | Functions ending in the shape:
16 --
17 -- @t m a -> t m a -> t m a@.
18 --
19 -- The functions in this section have a linear or flat n-ary combining
20 -- characterstics. It means that when combined @n@ times (e.g. @a `serial`
21 -- b `serial` c ...@) the resulting expression will have an @O(n)@
22 -- complexity (instead O(n^2) for pair wise combinators described in the
23 -- next section. These functions can be used efficiently with
24 -- 'concatMapWith' et. al. combinators that combine streams in a linear
25 -- fashion (contrast with 'concatPairsWith' which combines streams as a
26 -- binary tree).
28 serial
29 , ahead
30 , async
31 , wAsync
32 , parallel
33 , Par.parallelFst
34 , Par.parallelMin
36 -- * Binary Combinators (Pair Wise)
37 -- | Like the functions in the section above these functions also combine
38 -- two streams into a single stream but when used @n@ times linearly they
39 -- exhibit O(n^2) complexity. They are best combined in a binary tree
40 -- fashion using 'concatPairsWith' giving a @n * log n@ complexity. Avoid
41 -- using these with 'concatMapWith' when combining a large or infinite
42 -- number of streams.
44 -- ** Append
45 , append
47 -- ** wSerial
48 -- | 'wSerial' is a CPS based stream interleaving functions. It can be
49 -- used with 'concatMapWith' as well, however, the interleaving behavior of
50 -- @n@ streams would be asymmetric giving exponentially more weightage to
51 -- streams that come earlier in the composition.
52 --
53 , wSerial
54 , Serial.wSerialFst
55 , Serial.wSerialMin
57 -- ** Interleave
58 -- | 'interleave' is like 'wSerial' but using a direct style
59 -- implementation instead of CPS. It is faster than 'wSerial' due to stream
60 -- fusion but has worse efficiency when used with 'concatMapWith' for large
61 -- number of streams.
62 , interleave
63 , interleaveMin
64 , interleaveSuffix
65 , interleaveInfix
67 -- ** Round Robin
68 , roundrobin
70 -- ** Zip
71 , Z.zipWith
72 , Z.zipWithM
73 , Z.zipAsyncWith
74 , Z.zipAsyncWithM
76 -- ** Merge
77 -- , merge
78 , mergeBy
79 , mergeByM
80 , mergeAsyncBy
81 , mergeAsyncByM
83 -- * Combine Streams and Unfolds
84 -- |
85 -- Expand a stream by repeatedly using an unfold and merging the resulting
86 -- streams. Functions generally ending in the shape:
87 --
88 -- @Unfold m a b -> t m a -> t m b@
90 -- ** Append Many (Unfold)
91 -- | Unfold and flatten streams.
92 , unfoldMany
93 , unfoldManyInterleave
94 , unfoldManyRoundRobin
96 -- ** Interpose
97 -- | Insert effects between streams. Like unfoldMany but intersperses an
98 -- effect between the streams. A special case of gintercalate.
99 , interpose
100 , interposeSuffix
101 -- , interposeBy
103 -- ** Intercalate
104 -- | Insert Streams between Streams.
105 -- Like unfoldMany but intersperses streams from another source between
106 -- the streams from the first source.
107 , intercalate
108 , intercalateSuffix
109 , gintercalate
110 , gintercalateSuffix
112 -- * Append Many (concatMap)
113 -- | Map and serially append streams. 'concatMapM' is a generalization of
114 -- the binary append operation to append many streams.
115 , concatMapM
116 , concatMap
117 , concatM
118 , concat
120 -- * Flatten Containers
121 -- | Flatten 'Foldable' containers using the binary stream merging
122 -- operations.
123 , concatFoldableWith
124 , concatMapFoldableWith
125 , concatForFoldableWith
127 -- * ConcatMapWith
128 -- | Map and flatten a stream like 'concatMap' but using a custom binary
129 -- stream merging combinator instead of just appending the streams. The
130 -- merging occurs sequentially, it works efficiently for 'serial', 'async',
131 -- 'ahead' like merge operations where we consume one stream before the
132 -- next or in case of 'wAsync' or 'parallel' where we consume all streams
133 -- simultaneously anyway.
134 --
135 -- However, in cases where the merging consumes streams in a round robin
136 -- fashion, a pair wise merging using 'concatPairsWith' would be more
137 -- efficient. These cases include operations like 'mergeBy' or 'zipWith'.
139 , concatMapWith
140 , K.bindWith
141 , concatSmapMWith
143 -- * ConcatPairsWith
144 -- | See the notes about suitable merge functions in the 'concatMapWith'
145 -- section.
146 , concatPairsWith
148 -- * IterateMap
149 -- | Map and flatten Trees of Streams
150 , iterateMapWith
151 , iterateSmapMWith
152 , iterateMapLeftsWith
154 -- * Deprecated
155 , concatUnfold
156 )
157where
159#include "inline.hs"
161import Streamly.Internal.Data.Stream.Ahead (ahead)
162import Streamly.Internal.Data.Stream.Async (async, wAsync)
163import Streamly.Internal.Data.Stream.IsStream.Common
164 (concatM, concatMapM, concatMap, smapM, fromPure, fromEffect)
165import Streamly.Internal.Data.Stream.Parallel (parallel)
166import Streamly.Internal.Data.Stream.Prelude
167 ( concatFoldableWith, concatMapFoldableWith
168 , concatForFoldableWith, fromStreamS, toStreamS)
169import Streamly.Internal.Data.Stream.Serial (serial, wSerial)
170import Streamly.Internal.Data.Stream.StreamD (fromStreamD, toStreamD)
171import Streamly.Internal.Data.Stream.StreamK (IsStream)
172import Streamly.Internal.Data.SVar (MonadAsync)
173import Streamly.Internal.Data.Unfold.Type (Unfold)
175import qualified Streamly.Internal.Data.Stream.Parallel as Par
176import qualified Streamly.Internal.Data.Stream.Serial as Serial
177import qualified Streamly.Internal.Data.Stream.StreamD as D
178import qualified Streamly.Internal.Data.Stream.StreamK as K
179#ifdef USE_STREAMK_ONLY
180import qualified Streamly.Internal.Data.Stream.StreamK as S
181#else
182import qualified Streamly.Internal.Data.Stream.StreamD as S
183#endif
184import qualified Streamly.Internal.Data.Stream.Zip as Z
186import Prelude hiding (concat, concatMap)
188-- $setup
189-- >>> :m
190-- >>> import Data.IORef
191-- >>> import Prelude hiding (zipWith, concatMap, concat)
192-- >>> import qualified Streamly.Prelude as Stream
193-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
194-- >>> import qualified Streamly.Data.Fold as Fold
195-- >>> import qualified Streamly.Internal.Data.Fold as Fold
196-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
197-- >>> import qualified Streamly.Internal.Data.Parser as Parser
198-- >>> import qualified Streamly.Data.Array.Foreign as Array
200-------------------------------------------------------------------------------
201-- Appending
202-------------------------------------------------------------------------------
204-- XXX Reconcile the names "serial" and "append".
205--
206-- | Append the outputs of two streams, yielding all the elements from the
207-- first stream and then yielding all the elements from the second stream.
208--
209-- IMPORTANT NOTE: This could be 100x faster than @serial/<>@ for appending a
210-- few (say 100) streams because it can fuse via stream fusion. However, it
211-- does not scale for a large number of streams (say 1000s) and becomes
212-- qudartically slow. Therefore use this for custom appending of a few streams
213-- but use 'concatMap' or 'concatMapWith serial' for appending @n@ streams or
214-- infinite containers of streams.
215--
216-- /Pre-release/
217{-# INLINE append #-}
218append ::(IsStream t, Monad m) => t m b -> t m b -> t m b
219append m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
221-------------------------------------------------------------------------------
222-- Interleaving
223-------------------------------------------------------------------------------
225-- XXX Same as 'wSerial'. We should perhaps rename wSerial to interleave.
226-- XXX Document the interleaving behavior of side effects in all the
227-- interleaving combinators.
228-- XXX Write time-domain equivalents of these. In the time domain we can
229-- interleave two streams such that the value of second stream is always taken
230-- from its last value even if no new value is being yielded, like
231-- zipWithLatest. It would be something like interleaveWithLatest.
232--
233-- | Interleaves the outputs of two streams, yielding elements from each stream
234-- alternately, starting from the first stream. If any of the streams finishes
235-- early the other stream continues alone until it too finishes.
236--
237-- >>> :set -XOverloadedStrings
238-- >>> import Data.Functor.Identity (Identity)
239-- >>> Stream.interleave "ab" ",,,," :: Stream.SerialT Identity Char
240-- fromList "a,b,,,"
241--
242-- >>> Stream.interleave "abcd" ",," :: Stream.SerialT Identity Char
243-- fromList "a,b,cd"
244--
245-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
246--
247-- Do not use at scale in concatMapWith.
248--
249-- /Pre-release/
250{-# INLINE interleave #-}
251interleave ::(IsStream t, Monad m) => t m b -> t m b -> t m b
252interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
254-- | Interleaves the outputs of two streams, yielding elements from each stream
255-- alternately, starting from the first stream. As soon as the first stream
256-- finishes, the output stops, discarding the remaining part of the second
257-- stream. In this case, the last element in the resulting stream would be from
258-- the second stream. If the second stream finishes early then the first stream
259-- still continues to yield elements until it finishes.
260--
261-- >>> :set -XOverloadedStrings
262-- >>> import Data.Functor.Identity (Identity)
263-- >>> Stream.interleaveSuffix "abc" ",,,," :: Stream.SerialT Identity Char
264-- fromList "a,b,c,"
265-- >>> Stream.interleaveSuffix "abc" "," :: Stream.SerialT Identity Char
266-- fromList "a,bc"
267--
268-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
269--
270-- Do not use at scale in concatMapWith.
271--
272-- /Pre-release/
273{-# INLINE interleaveSuffix #-}
274interleaveSuffix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
275interleaveSuffix m1 m2 =
276 fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
278-- | Interleaves the outputs of two streams, yielding elements from each stream
279-- alternately, starting from the first stream and ending at the first stream.
280-- If the second stream is longer than the first, elements from the second
281-- stream are infixed with elements from the first stream. If the first stream
282-- is longer then it continues yielding elements even after the second stream
283-- has finished.
284--
285-- >>> :set -XOverloadedStrings
286-- >>> import Data.Functor.Identity (Identity)
287-- >>> Stream.interleaveInfix "abc" ",,,," :: Stream.SerialT Identity Char
288-- fromList "a,b,c"
289-- >>> Stream.interleaveInfix "abc" "," :: Stream.SerialT Identity Char
290-- fromList "a,bc"
291--
292-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
293--
294-- Do not use at scale in concatMapWith.
295--
296-- /Pre-release/
297{-# INLINE interleaveInfix #-}
298interleaveInfix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
299interleaveInfix m1 m2 =
300 fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
302-- | Interleaves the outputs of two streams, yielding elements from each stream
303-- alternately, starting from the first stream. The output stops as soon as any
304-- of the two streams finishes, discarding the remaining part of the other
305-- stream. The last element of the resulting stream would be from the longer
306-- stream.
307--
308-- >>> :set -XOverloadedStrings
309-- >>> import Data.Functor.Identity (Identity)
310-- >>> Stream.interleaveMin "ab" ",,,," :: Stream.SerialT Identity Char
311-- fromList "a,b,"
312-- >>> Stream.interleaveMin "abcd" ",," :: Stream.SerialT Identity Char
313-- fromList "a,b,c"
314--
315-- 'interleaveMin' is dual to 'interleave'.
316--
317-- Do not use at scale in concatMapWith.
318--
319-- /Pre-release/
320{-# INLINE interleaveMin #-}
321interleaveMin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
322interleaveMin m1 m2 = fromStreamD $ D.interleaveMin (toStreamD m1) (toStreamD m2)
324-------------------------------------------------------------------------------
325-- Scheduling
326-------------------------------------------------------------------------------
328-- | Schedule the execution of two streams in a fair round-robin manner,
329-- executing each stream once, alternately. Execution of a stream may not
330-- necessarily result in an output, a stream may chose to @Skip@ producing an
331-- element until later giving the other stream a chance to run. Therefore, this
332-- combinator fairly interleaves the execution of two streams rather than
333-- fairly interleaving the output of the two streams. This can be useful in
334-- co-operative multitasking without using explicit threads. This can be used
335-- as an alternative to `async`.
336--
337-- Do not use at scale in concatMapWith.
338--
339-- /Pre-release/
340{-# INLINE roundrobin #-}
341roundrobin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
342roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)
344------------------------------------------------------------------------------
345-- Merging (sorted streams)
346------------------------------------------------------------------------------
348-- | Merge two streams using a comparison function. The head elements of both
349-- the streams are compared and the smaller of the two elements is emitted, if
350-- both elements are equal then the element from the first stream is used
351-- first.
352--
353-- If the streams are sorted in ascending order, the resulting stream would
354-- also remain sorted in ascending order.
355--
356-- @
357-- >>> Stream.toList $ Stream.mergeBy compare (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
358-- [1,2,3,4,5,6,8]
359--
360-- @
361--
362-- @since 0.6.0
363{-# INLINABLE mergeBy #-}
364mergeBy ::
365 (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
366mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
368-- | Like 'mergeBy' but with a monadic comparison function.
369--
370-- Merge two streams randomly:
371--
372-- @
373-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
374-- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])
375-- [2,1,2,2,2,1,1,1]
376-- @
377--
378-- Merge two streams in a proportion of 2:1:
379--
380-- @
381-- >>> :{
382-- do
383-- let proportionately m n = do
384-- ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]
385-- return $ \_ _ -> do
386-- r <- readIORef ref
387-- writeIORef ref $ Prelude.tail r
388-- return $ Prelude.head r
389-- f <- proportionately 2 1
390-- xs <- Stream.toList $ Stream.mergeByM f (Stream.fromList [1,1,1,1,1,1]) (Stream.fromList [2,2,2])
391-- print xs
392-- :}
393-- [1,1,2,1,1,2,1,1,2]
394--
395-- @
396--
397-- @since 0.6.0
398{-# INLINABLE mergeByM #-}
399mergeByM
400 :: (IsStream t, Monad m)
401 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
402mergeByM f m1 m2 = fromStreamS $ S.mergeByM f (toStreamS m1) (toStreamS m2)
404{-
405-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
406{-# INLINABLE mergeEndByAny #-}
407mergeEndByAny
408 :: (IsStream t, Monad m)
409 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
410mergeEndByAny f m1 m2 = fromStreamD $
411 D.mergeEndByAny f (toStreamD m1) (toStreamD m2)
413-- Like 'mergeByM' but stops merging as soon as the first stream stops.
414{-# INLINABLE mergeEndByFirst #-}
415mergeEndByFirst
416 :: (IsStream t, Monad m)
417 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
418mergeEndByFirst f m1 m2 = fromStreamS $
419 D.mergeEndByFirst f (toStreamD m1) (toStreamD m2)
421-- Holding this back for now, we may want to use the name "merge" differently
422-- | Same as @'mergeBy' 'compare'@.
423--
424-- @
425-- >>> Stream.toList $ Stream.merge (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
426-- [1,2,3,4,5,6,8]
427-- @
428--
429-- @since 0.6.0
430{-# INLINABLE merge #-}
431merge ::
432 (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
433merge = mergeBy compare
434-}
436-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
437-- merged are generated concurrently).
438--
439-- @since 0.6.0
440{-# INLINE mergeAsyncBy #-}
441mergeAsyncBy :: (IsStream t, MonadAsync m)
442 => (a -> a -> Ordering) -> t m a -> t m a -> t m a
443mergeAsyncBy f = mergeAsyncByM (\a b -> return $ f a b)
445-- | Like 'mergeByM' but merges concurrently (i.e. both the elements being
446-- merged are generated concurrently).
447--
448-- @since 0.6.0
449{-# INLINE mergeAsyncByM #-}
450mergeAsyncByM :: (IsStream t, MonadAsync m)
451 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
452mergeAsyncByM f m1 m2 =
453 fromStreamD $
454 let par = Par.mkParallelD . toStreamD
455 in D.mergeByM f (par m1) (par m2)
458-- @since 0.7.0
459{-# DEPRECATED concatUnfold "Please use unfoldMany instead." #-}
460{-# INLINE concatUnfold #-}
461concatUnfold ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
462concatUnfold u m = fromStreamD $ D.unfoldMany u (toStreamD m)
464------------------------------------------------------------------------------
465-- Combine N Streams - unfoldMany
466------------------------------------------------------------------------------
468-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
469-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
470-- therefore provide many times better performance.
471--
472-- @since 0.8.0
473{-# INLINE unfoldMany #-}
474unfoldMany ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
475unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
477-- | Like 'unfoldMany' but interleaves the streams in the same way as
478-- 'interleave' behaves instead of appending them.
479--
480-- /Pre-release/
481{-# INLINE unfoldManyInterleave #-}
482unfoldManyInterleave ::(IsStream t, Monad m)
483 => Unfold m a b -> t m a -> t m b
484unfoldManyInterleave u m =
485 fromStreamD $ D.unfoldManyInterleave u (toStreamD m)
487-- | Like 'unfoldMany' but executes the streams in the same way as
488-- 'roundrobin'.
489--
490-- /Pre-release/
491{-# INLINE unfoldManyRoundRobin #-}
492unfoldManyRoundRobin ::(IsStream t, Monad m)
493 => Unfold m a b -> t m a -> t m b
494unfoldManyRoundRobin u m =
495 fromStreamD $ D.unfoldManyRoundRobin u (toStreamD m)
497------------------------------------------------------------------------------
498-- Combine N Streams - interpose
499------------------------------------------------------------------------------
501-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
502--
503-- | Unfold the elements of a stream, intersperse the given element between the
504-- unfolded streams and then concat them into a single stream.
505--
506-- > unwords = S.interpose ' '
507--
508-- /Pre-release/
509{-# INLINE interpose #-}
510interpose :: (IsStream t, Monad m)
511 => c -> Unfold m b c -> t m b -> t m c
512interpose x unf str =
513 D.fromStreamD $ D.interpose (return x) unf (D.toStreamD str)
515-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
516--
517-- | Unfold the elements of a stream, append the given element after each
518-- unfolded stream and then concat them into a single stream.
519--
520-- > unlines = S.interposeSuffix '\n'
521--
522-- /Pre-release/
523{-# INLINE interposeSuffix #-}
524interposeSuffix :: (IsStream t, Monad m)
525 => c -> Unfold m b c -> t m b -> t m c
526interposeSuffix x unf str =
527 D.fromStreamD $ D.interposeSuffix (return x) unf (D.toStreamD str)
529------------------------------------------------------------------------------
530-- Combine N Streams - intercalate
531------------------------------------------------------------------------------
533-- XXX we can swap the order of arguments to gintercalate so that the
534-- definition of unfoldMany becomes simpler? The first stream should be
535-- infixed inside the second one. However, if we change the order in
536-- "interleave" as well similarly, then that will make it a bit unintuitive.
537--
538-- > unfoldMany unf str =
539-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
540--
541-- | 'interleaveInfix' followed by unfold and concat.
542--
543-- /Pre-release/
544{-# INLINE gintercalate #-}
545gintercalate
546 :: (IsStream t, Monad m)
547 => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
548gintercalate unf1 str1 unf2 str2 =
549 D.fromStreamD $ D.gintercalate
550 unf1 (D.toStreamD str1)
551 unf2 (D.toStreamD str2)
553-- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)
554--
555-- | 'intersperse' followed by unfold and concat.
556--
557-- > intercalate unf a str = unfoldMany unf $ intersperse a str
558-- > intersperse = intercalate (Unfold.function id)
559-- > unwords = intercalate Unfold.fromList " "
560--
561-- >>> Stream.toList $ Stream.intercalate Unfold.fromList " " $ Stream.fromList ["abc", "def", "ghi"]
562-- "abc def ghi"
563--
564-- @since 0.8.0
565{-# INLINE intercalate #-}
566intercalate :: (IsStream t, Monad m)
567 => Unfold m b c -> b -> t m b -> t m c
568intercalate unf seed str = D.fromStreamD $
569 D.unfoldMany unf $ D.intersperse seed (toStreamD str)
571-- | 'interleaveSuffix' followed by unfold and concat.
572--
573-- /Pre-release/
574{-# INLINE gintercalateSuffix #-}
575gintercalateSuffix
576 :: (IsStream t, Monad m)
577 => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
578gintercalateSuffix unf1 str1 unf2 str2 =
579 D.fromStreamD $ D.gintercalateSuffix
580 unf1 (D.toStreamD str1)
581 unf2 (D.toStreamD str2)
583-- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)
584--
585-- | 'intersperseSuffix' followed by unfold and concat.
586--
587-- > intercalateSuffix unf a str = unfoldMany unf $ intersperseSuffix a str
588-- > intersperseSuffix = intercalateSuffix (Unfold.function id)
589-- > unlines = intercalateSuffix Unfold.fromList "\n"
590--
591-- >>> Stream.toList $ Stream.intercalateSuffix Unfold.fromList "\n" $ Stream.fromList ["abc", "def", "ghi"]
592-- "abc\ndef\nghi\n"
593--
594-- @since 0.8.0
595{-# INLINE intercalateSuffix #-}
596intercalateSuffix :: (IsStream t, Monad m)
597 => Unfold m b c -> b -> t m b -> t m c
598intercalateSuffix unf seed str = fromStreamD $ D.unfoldMany unf
599 $ D.intersperseSuffix (return seed) (D.toStreamD str)
601{-
602{-# INLINE iterateUnfold #-}
603iterateUnfold :: (IsStream t, MonadAsync m)
604 => Unfold m a a -> t m a -> t m a
605iterateUnfold unf xs = undefined
606-}
608------------------------------------------------------------------------------
609-- Combine N Streams - concatMap
610------------------------------------------------------------------------------
612-- | Flatten a stream of streams to a single stream.
613--
614-- @
615-- concat = concatMap id
616-- @
617--
618-- /Pre-release/
619{-# INLINE concat #-}
620concat :: (IsStream t, Monad m) => t m (t m a) -> t m a
621concat = concatMap id
623------------------------------------------------------------------------------
624-- Combine N Streams - concatMap
625------------------------------------------------------------------------------
627-- | @concatMapWith mixer generator stream@ is a two dimensional looping
628-- combinator. The @generator@ function is used to generate streams from the
629-- elements in the input @stream@ and the @mixer@ function is used to merge
630-- those streams.
631--
632-- Note we can merge streams concurrently by using a concurrent merge function.
633--
634-- /Since: 0.7.0/
635--
636-- /Since: 0.8.0 (signature change)/
637{-# INLINE concatMapWith #-}
638concatMapWith
639 :: IsStream t
640 => (t m b -> t m b -> t m b)
641 -> (a -> t m b)
642 -> t m a
643 -> t m b
644concatMapWith = K.concatMapBy
646-- | Like 'concatMapWith' but carries a state which can be used to share
647-- information across multiple steps of concat.
648--
649-- @
650-- concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
651-- @
652--
653-- /Pre-release/
654--
655{-# INLINE concatSmapMWith #-}
656concatSmapMWith
657 :: (IsStream t, Monad m)
658 => (t m b -> t m b -> t m b)
659 -> (s -> a -> m (s, t m b))
660 -> m s
661 -> t m a
662 -> t m b
663concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
665-- Keep concating either streams as long as rights are generated, stop as soon
666-- as a left is generated and concat the left stream.
667--
668-- See also: 'handle'
669--
670-- /Unimplemented/
671--
672{-
673concatMapEitherWith
674 :: -- (IsStream t, MonadAsync m) =>
675 (forall x. t m x -> t m x -> t m x)
676 -> (a -> t m (Either (t m b) b))
677 -> t m a
678 -> t m b
679concatMapEitherWith = undefined
680-}
682-- XXX Implement a StreamD version for fusion.
683--
684-- | Combine streams in pairs using a binary stream combinator, then combine
685-- the resulting streams in pairs recursively until we get to a single combined
686-- stream.
687--
688-- For example, you can sort a stream using merge sort like this:
689--
690-- >>> Stream.toList $ Stream.concatPairsWith (Stream.mergeBy compare) Stream.fromPure $ Stream.fromList [5,1,7,9,2]
691-- [1,2,5,7,9]
692--
693-- /Caution: the stream of streams must be finite/
694--
695-- /Pre-release/
696--
697{-# INLINE concatPairsWith #-}
698concatPairsWith :: IsStream t =>
699 (t m b -> t m b -> t m b)
700 -> (a -> t m b)
701 -> t m a
702 -> t m b
703concatPairsWith = K.concatPairsWith
705------------------------------------------------------------------------------
706-- IterateMap - Map and flatten Trees of Streams
707------------------------------------------------------------------------------
709-- | Like 'iterateM' but iterates after mapping a stream generator on the
710-- output.
711--
712-- Yield an input element in the output stream, map a stream generator on it
713-- and then do the same on the resulting stream. This can be used for a depth
714-- first traversal of a tree like structure.
715--
716-- Note that 'iterateM' is a special case of 'iterateMapWith':
717--
718-- @
719-- iterateM f = iterateMapWith serial (fromEffect . f) . fromEffect
720-- @
721--
722-- It can be used to traverse a tree structure. For example, to list a
723-- directory tree:
724--
725-- @
726-- Stream.iterateMapWith Stream.serial
727-- (either Dir.toEither (const nil))
728-- (fromPure (Left "tmp"))
729-- @
730--
731-- /Pre-release/
732--
733{-# INLINE iterateMapWith #-}
734iterateMapWith
735 :: IsStream t
736 => (t m a -> t m a -> t m a)
737 -> (a -> t m a)
738 -> t m a
739 -> t m a
740iterateMapWith combine f = concatMapWith combine go
741 where
742 go x = fromPure x `combine` concatMapWith combine go (f x)
744{-
745{-# INLINE iterateUnfold #-}
746iterateUnfold :: (IsStream t, MonadAsync m)
747 => Unfold m a a -> t m a -> t m a
748iterateUnfold unf xs = undefined
749-}
751------------------------------------------------------------------------------
752-- Flattening Graphs
753------------------------------------------------------------------------------
755-- To traverse graphs we need a state to be carried around in the traversal.
756-- For example, we can use a hashmap to store the visited status of nodes.
758-- | Like 'iterateMap' but carries a state in the stream generation function.
759-- This can be used to traverse graph like structures, we can remember the
760-- visited nodes in the state to avoid cycles.
761--
762-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
763-- traverse graphs. However, this function provides a more localized state
764-- instead of using a global state.
765--
766-- See also: 'mfix'
767--
768-- /Pre-release/
769--
770{-# INLINE iterateSmapMWith #-}
771iterateSmapMWith
772 :: (IsStream t, Monad m)
773 => (t m a -> t m a -> t m a)
774 -> (b -> a -> m (b, t m a))
775 -> m b
776 -> t m a
777 -> t m a
778iterateSmapMWith combine f initial stream =
779 concatMap (\b -> concatMapWith combine (go b) stream) (fromEffect initial)
781 where
783 go b a = fromPure a `combine` feedback b a
785 feedback b a =
786 concatMap
787 (\(b1, s) -> concatMapWith combine (go b1) s)
788 (fromEffect $ f b a)
790------------------------------------------------------------------------------
791-- iterateMap - Either streams
792------------------------------------------------------------------------------
794-- | In an 'Either' stream iterate on 'Left's. This is a special case of
795-- 'iterateMapWith':
796--
797-- @
798-- iterateMapLeftsWith combine f = iterateMapWith combine (either f (const nil))
799-- @
800--
801-- To traverse a directory tree:
802--
803-- @
804-- iterateMapLeftsWith serial Dir.toEither (fromPure (Left "tmp"))
805-- @
806--
807-- /Pre-release/
808--
809{-# INLINE iterateMapLeftsWith #-}
810iterateMapLeftsWith
811 :: IsStream t
812 => (t m (Either a b) -> t m (Either a b) -> t m (Either a b))
813 -> (a -> t m (Either a b))
814 -> t m (Either a b)
815 -> t m (Either a b)
816iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))