on 796bb22 help sign in
818 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, yield, yieldM)
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 Prelude hiding (zipWith, concatMap, concat)
191-- >>> import qualified Streamly.Prelude as Stream
192-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
193-- >>> import qualified Streamly.Data.Fold as Fold
194-- >>> import qualified Streamly.Internal.Data.Fold as Fold
195-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
196-- >>> import qualified Streamly.Internal.Data.Parser as Parser
197-- >>> import qualified Streamly.Data.Array.Foreign as Array
199-------------------------------------------------------------------------------
200-- Appending
201-------------------------------------------------------------------------------
203-- XXX Reconcile the names "serial" and "append".
204--
205-- | Append the outputs of two streams, yielding all the elements from the
206-- first stream and then yielding all the elements from the second stream.
207--
208-- IMPORTANT NOTE: This could be 100x faster than @serial/<>@ for appending a
209-- few (say 100) streams because it can fuse via stream fusion. However, it
210-- does not scale for a large number of streams (say 1000s) and becomes
211-- qudartically slow. Therefore use this for custom appending of a few streams
212-- but use 'concatMap' or 'concatMapWith serial' for appending @n@ streams or
213-- infinite containers of streams.
214--
215-- /Pre-release/
216{-# INLINE append #-}
217append ::(IsStream t, Monad m) => t m b -> t m b -> t m b
218append m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
220-------------------------------------------------------------------------------
221-- Interleaving
222-------------------------------------------------------------------------------
224-- XXX Same as 'wSerial'. We should perhaps rename wSerial to interleave.
225-- XXX Document the interleaving behavior of side effects in all the
226-- interleaving combinators.
227-- XXX Write time-domain equivalents of these. In the time domain we can
228-- interleave two streams such that the value of second stream is always taken
229-- from its last value even if no new value is being yielded, like
230-- zipWithLatest. It would be something like interleaveWithLatest.
231--
232-- | Interleaves the outputs of two streams, yielding elements from each stream
233-- alternately, starting from the first stream. If any of the streams finishes
234-- early the other stream continues alone until it too finishes.
235--
236-- >>> :set -XOverloadedStrings
237-- >>> import Data.Functor.Identity (Identity)
238-- >>> Stream.interleave "ab" ",,,," :: Stream.SerialT Identity Char
239-- fromList "a,b,,,"
240-- >>> Stream.interleave "abcd" ",," :: Stream.SerialT Identity Char
241-- fromList "a,b,cd"
242--
243-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
244--
245-- Do not use at scale in concatMapWith.
246--
247-- /Pre-release/
248{-# INLINE interleave #-}
249interleave ::(IsStream t, Monad m) => t m b -> t m b -> t m b
250interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
252-- | Interleaves the outputs of two streams, yielding elements from each stream
253-- alternately, starting from the first stream. As soon as the first stream
254-- finishes, the output stops, discarding the remaining part of the second
255-- stream. In this case, the last element in the resulting stream would be from
256-- the second stream. If the second stream finishes early then the first stream
257-- still continues to yield elements until it finishes.
258--
259-- >>> :set -XOverloadedStrings
260-- >>> import Data.Functor.Identity (Identity)
261-- >>> Stream.interleaveSuffix "abc" ",,,," :: Stream.SerialT Identity Char
262-- fromList "a,b,c,"
263-- >>> Stream.interleaveSuffix "abc" "," :: Stream.SerialT Identity Char
264-- fromList "a,bc"
265--
266-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
267--
268-- Do not use at scale in concatMapWith.
269--
270-- /Pre-release/
271{-# INLINE interleaveSuffix #-}
272interleaveSuffix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
273interleaveSuffix m1 m2 =
274 fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
276-- | Interleaves the outputs of two streams, yielding elements from each stream
277-- alternately, starting from the first stream and ending at the first stream.
278-- If the second stream is longer than the first, elements from the second
279-- stream are infixed with elements from the first stream. If the first stream
280-- is longer then it continues yielding elements even after the second stream
281-- has finished.
282--
283-- >>> :set -XOverloadedStrings
284-- >>> import Data.Functor.Identity (Identity)
285-- >>> Stream.interleaveInfix "abc" ",,,," :: Stream.SerialT Identity Char
286-- fromList "a,b,c"
287-- >>> Stream.interleaveInfix "abc" "," :: Stream.SerialT Identity Char
288-- fromList "a,bc"
289--
290-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
291--
292-- Do not use at scale in concatMapWith.
293--
294-- /Pre-release/
295{-# INLINE interleaveInfix #-}
296interleaveInfix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
297interleaveInfix m1 m2 =
298 fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
300-- | Interleaves the outputs of two streams, yielding elements from each stream
301-- alternately, starting from the first stream. The output stops as soon as any
302-- of the two streams finishes, discarding the remaining part of the other
303-- stream. The last element of the resulting stream would be from the longer
304-- stream.
305--
306-- >>> :set -XOverloadedStrings
307-- >>> import Data.Functor.Identity (Identity)
308-- >>> Stream.interleaveMin "ab" ",,,," :: Stream.SerialT Identity Char
309-- fromList "a,b,"
310-- >>> Stream.interleaveMin "abcd" ",," :: Stream.SerialT Identity Char
311-- fromList "a,b,c"
312--
313-- 'interleaveMin' is dual to 'interleave'.
314--
315-- Do not use at scale in concatMapWith.
316--
317-- /Pre-release/
318{-# INLINE interleaveMin #-}
319interleaveMin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
320interleaveMin m1 m2 = fromStreamD $ D.interleaveMin (toStreamD m1) (toStreamD m2)
322-------------------------------------------------------------------------------
323-- Scheduling
324-------------------------------------------------------------------------------
326-- | Schedule the execution of two streams in a fair round-robin manner,
327-- executing each stream once, alternately. Execution of a stream may not
328-- necessarily result in an output, a stream may chose to @Skip@ producing an
329-- element until later giving the other stream a chance to run. Therefore, this
330-- combinator fairly interleaves the execution of two streams rather than
331-- fairly interleaving the output of the two streams. This can be useful in
332-- co-operative multitasking without using explicit threads. This can be used
333-- as an alternative to `async`.
334--
335-- Do not use at scale in concatMapWith.
336--
337-- /Pre-release/
338{-# INLINE roundrobin #-}
339roundrobin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
340roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)
342------------------------------------------------------------------------------
343-- Merging (sorted streams)
344------------------------------------------------------------------------------
346-- | Merge two streams using a comparison function. The head elements of both
347-- the streams are compared and the smaller of the two elements is emitted, if
348-- both elements are equal then the element from the first stream is used
349-- first.
350--
351-- If the streams are sorted in ascending order, the resulting stream would
352-- also remain sorted in ascending order.
353--
354-- @
355-- >>> Stream.toList $ Stream.mergeBy compare (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
356-- [1,2,3,4,5,6,8]
357--
358-- @
359--
360-- @since 0.6.0
361{-# INLINABLE mergeBy #-}
362mergeBy ::
363 (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
364mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
366-- | Like 'mergeBy' but with a monadic comparison function.
367--
368-- Merge two streams randomly:
369--
370-- @
371-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
372-- > S.toList $ S.mergeByM randomly (S.fromList [1,1,1,1]) (S.fromList [2,2,2,2])
373-- [2,1,2,2,2,1,1,1]
374-- @
375--
376-- Merge two streams in a proportion of 2:1:
377--
378-- @
379-- proportionately m n = do
380-- ref <- newIORef $ cycle $ concat [replicate m LT, replicate n GT]
381-- return $ \\_ _ -> do
382-- r <- readIORef ref
383-- writeIORef ref $ tail r
384-- return $ head r
385--
386-- main = do
387-- f <- proportionately 2 1
388-- xs <- S.toList $ S.mergeByM f (S.fromList [1,1,1,1,1,1]) (S.fromList [2,2,2])
389-- print xs
390-- @
391-- @
392-- [1,1,2,1,1,2,1,1,2]
393-- @
394--
395-- @since 0.6.0
396{-# INLINABLE mergeByM #-}
397mergeByM
398 :: (IsStream t, Monad m)
399 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
400mergeByM f m1 m2 = fromStreamS $ S.mergeByM f (toStreamS m1) (toStreamS m2)
402{-
403-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
404{-# INLINABLE mergeEndByAny #-}
405mergeEndByAny
406 :: (IsStream t, Monad m)
407 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
408mergeEndByAny f m1 m2 = fromStreamD $
409 D.mergeEndByAny f (toStreamD m1) (toStreamD m2)
411-- Like 'mergeByM' but stops merging as soon as the first stream stops.
412{-# INLINABLE mergeEndByFirst #-}
413mergeEndByFirst
414 :: (IsStream t, Monad m)
415 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
416mergeEndByFirst f m1 m2 = fromStreamS $
417 D.mergeEndByFirst f (toStreamD m1) (toStreamD m2)
419-- Holding this back for now, we may want to use the name "merge" differently
420-- | Same as @'mergeBy' 'compare'@.
421--
422-- @
423-- > S.toList $ S.merge (S.fromList [1,3,5]) (S.fromList [2,4,6,8])
424-- [1,2,3,4,5,6,8]
425-- @
426--
427-- @since 0.6.0
428{-# INLINABLE merge #-}
429merge ::
430 (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
431merge = mergeBy compare
432-}
434-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
435-- merged are generated concurrently).
436--
437-- @since 0.6.0
438{-# INLINE mergeAsyncBy #-}
439mergeAsyncBy :: (IsStream t, MonadAsync m)
440 => (a -> a -> Ordering) -> t m a -> t m a -> t m a
441mergeAsyncBy f = mergeAsyncByM (\a b -> return $ f a b)
443-- | Like 'mergeByM' but merges concurrently (i.e. both the elements being
444-- merged are generated concurrently).
445--
446-- @since 0.6.0
447{-# INLINE mergeAsyncByM #-}
448mergeAsyncByM :: (IsStream t, MonadAsync m)
449 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
450mergeAsyncByM f m1 m2 =
451 fromStreamD $
452 let par = Par.mkParallelD . toStreamD
453 in D.mergeByM f (par m1) (par m2)
456-- @since 0.7.0
457{-# DEPRECATED concatUnfold "Please use unfoldMany instead." #-}
458{-# INLINE concatUnfold #-}
459concatUnfold ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
460concatUnfold u m = fromStreamD $ D.unfoldMany u (toStreamD m)
462------------------------------------------------------------------------------
463-- Combine N Streams - unfoldMany
464------------------------------------------------------------------------------
466-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
467-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
468-- therefore provide many times better performance.
469--
470-- @since 0.8.0
471{-# INLINE unfoldMany #-}
472unfoldMany ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
473unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
475-- | Like 'unfoldMany' but interleaves the streams in the same way as
476-- 'interleave' behaves instead of appending them.
477--
478-- /Pre-release/
479{-# INLINE unfoldManyInterleave #-}
480unfoldManyInterleave ::(IsStream t, Monad m)
481 => Unfold m a b -> t m a -> t m b
482unfoldManyInterleave u m =
483 fromStreamD $ D.unfoldManyInterleave u (toStreamD m)
485-- | Like 'unfoldMany' but executes the streams in the same way as
486-- 'roundrobin'.
487--
488-- /Pre-release/
489{-# INLINE unfoldManyRoundRobin #-}
490unfoldManyRoundRobin ::(IsStream t, Monad m)
491 => Unfold m a b -> t m a -> t m b
492unfoldManyRoundRobin u m =
493 fromStreamD $ D.unfoldManyRoundRobin u (toStreamD m)
495------------------------------------------------------------------------------
496-- Combine N Streams - interpose
497------------------------------------------------------------------------------
499-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
500--
501-- | Unfold the elements of a stream, intersperse the given element between the
502-- unfolded streams and then concat them into a single stream.
503--
504-- > unwords = S.interpose ' '
505--
506-- /Pre-release/
507{-# INLINE interpose #-}
508interpose :: (IsStream t, Monad m)
509 => c -> Unfold m b c -> t m b -> t m c
510interpose x unf str =
511 D.fromStreamD $ D.interpose (return x) unf (D.toStreamD str)
513-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
514--
515-- | Unfold the elements of a stream, append the given element after each
516-- unfolded stream and then concat them into a single stream.
517--
518-- > unlines = S.interposeSuffix '\n'
519--
520-- /Pre-release/
521{-# INLINE interposeSuffix #-}
522interposeSuffix :: (IsStream t, Monad m)
523 => c -> Unfold m b c -> t m b -> t m c
524interposeSuffix x unf str =
525 D.fromStreamD $ D.interposeSuffix (return x) unf (D.toStreamD str)
527------------------------------------------------------------------------------
528-- Combine N Streams - intercalate
529------------------------------------------------------------------------------
531-- XXX we can swap the order of arguments to gintercalate so that the
532-- definition of unfoldMany becomes simpler? The first stream should be
533-- infixed inside the second one. However, if we change the order in
534-- "interleave" as well similarly, then that will make it a bit unintuitive.
535--
536-- > unfoldMany unf str =
537-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
538--
539-- | 'interleaveInfix' followed by unfold and concat.
540--
541-- /Pre-release/
542{-# INLINE gintercalate #-}
543gintercalate
544 :: (IsStream t, Monad m)
545 => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
546gintercalate unf1 str1 unf2 str2 =
547 D.fromStreamD $ D.gintercalate
548 unf1 (D.toStreamD str1)
549 unf2 (D.toStreamD str2)
551-- XXX The order of arguments in "intercalate" is consistent with the list
552-- intercalate but inconsistent with gintercalate and other stream interleaving
553-- combinators. We can change the order of the arguments in other combinators
554-- but then 'interleave' combinator may become a bit unintuitive because we
555-- will be starting with the second stream.
557-- > intercalate seed unf str = gintercalate unf str unf (repeatM seed)
558-- > intercalate a unf str = unfoldMany unf $ intersperse a str
559--
560-- | 'intersperse' followed by unfold and concat.
561--
562-- > unwords = intercalate " " Unfold.fromList
563--
564-- >>> Stream.toList $ Stream.intercalate " " Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
565-- "abc def ghi"
566--
567-- /Pre-release/
568{-# INLINE intercalate #-}
569intercalate :: (IsStream t, Monad m)
570 => b -> Unfold m b c -> t m b -> t m c
571intercalate seed unf str = D.fromStreamD $
572 D.unfoldMany unf $ D.intersperse seed (toStreamD str)
574-- | 'interleaveSuffix' followed by unfold and concat.
575--
576-- /Pre-release/
577{-# INLINE gintercalateSuffix #-}
578gintercalateSuffix
579 :: (IsStream t, Monad m)
580 => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
581gintercalateSuffix unf1 str1 unf2 str2 =
582 D.fromStreamD $ D.gintercalateSuffix
583 unf1 (D.toStreamD str1)
584 unf2 (D.toStreamD str2)
586-- > intercalateSuffix seed unf str = gintercalateSuffix unf str unf (repeatM seed)
587-- > intercalateSuffix a unf str = unfoldMany unf $ intersperseSuffix a str
588--
589-- | 'intersperseSuffix' followed by unfold and concat.
590--
591-- > unlines = intercalateSuffix "\n" Unfold.fromList
592--
593-- >>> Stream.toList $ Stream.intercalateSuffix "\n" Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
594-- "abc\ndef\nghi\n"
595--
596-- /Pre-release/
597{-# INLINE intercalateSuffix #-}
598intercalateSuffix :: (IsStream t, Monad m)
599 => b -> Unfold m b c -> t m b -> t m c
600intercalateSuffix seed unf str = fromStreamD $ D.unfoldMany unf
601 $ D.intersperseSuffix (return seed) (D.toStreamD str)
603{-
604{-# INLINE iterateUnfold #-}
605iterateUnfold :: (IsStream t, MonadAsync m)
606 => Unfold m a a -> t m a -> t m a
607iterateUnfold unf xs = undefined
608-}
610------------------------------------------------------------------------------
611-- Combine N Streams - concatMap
612------------------------------------------------------------------------------
614-- | Flatten a stream of streams to a single stream.
615--
616-- @
617-- concat = concatMap id
618-- @
619--
620-- /Pre-release/
621{-# INLINE concat #-}
622concat :: (IsStream t, Monad m) => t m (t m a) -> t m a
623concat = concatMap id
625------------------------------------------------------------------------------
626-- Combine N Streams - concatMap
627------------------------------------------------------------------------------
629-- | @concatMapWith mixer generator stream@ is a two dimensional looping
630-- combinator. The @generator@ function is used to generate streams from the
631-- elements in the input @stream@ and the @mixer@ function is used to merge
632-- those streams.
633--
634-- Note we can merge streams concurrently by using a concurrent merge function.
635--
636-- /Since: 0.7.0/
637--
638-- /Since: 0.8.0 (signature change)/
639{-# INLINE concatMapWith #-}
640concatMapWith
641 :: IsStream t
642 => (t m b -> t m b -> t m b)
643 -> (a -> t m b)
644 -> t m a
645 -> t m b
646concatMapWith = K.concatMapBy
648-- | Like 'concatMapWith' but carries a state which can be used to share
649-- information across multiple steps of concat.
650--
651-- @
652-- concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
653-- @
654--
655-- /Pre-release/
656--
657{-# INLINE concatSmapMWith #-}
658concatSmapMWith
659 :: (IsStream t, Monad m)
660 => (t m b -> t m b -> t m b)
661 -> (s -> a -> m (s, t m b))
662 -> m s
663 -> t m a
664 -> t m b
665concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
667-- Keep concating either streams as long as rights are generated, stop as soon
668-- as a left is generated and concat the left stream.
669--
670-- See also: 'handle'
671--
672-- /Unimplemented/
673--
674{-
675concatMapEitherWith
676 :: -- (IsStream t, MonadAsync m) =>
677 (forall x. t m x -> t m x -> t m x)
678 -> (a -> t m (Either (t m b) b))
679 -> t m a
680 -> t m b
681concatMapEitherWith = undefined
682-}
684-- XXX Implement a StreamD version for fusion.
685--
686-- | Combine streams in pairs using a binary stream combinator, then combine
687-- the resulting streams in pairs recursively until we get to a single combined
688-- stream.
689--
690-- For example, you can sort a stream using merge sort like this:
691--
692-- >>> Stream.toList $ Stream.concatPairsWith (Stream.mergeBy compare) Stream.yield $ Stream.fromList [5,1,7,9,2]
693-- [1,2,5,7,9]
694--
695-- /Caution: the stream of streams must be finite/
696--
697-- /Pre-release/
698--
699{-# INLINE concatPairsWith #-}
700concatPairsWith :: IsStream t =>
701 (t m b -> t m b -> t m b)
702 -> (a -> t m b)
703 -> t m a
704 -> t m b
705concatPairsWith = K.concatPairsWith
707------------------------------------------------------------------------------
708-- IterateMap - Map and flatten Trees of Streams
709------------------------------------------------------------------------------
711-- | Like 'iterateM' but iterates after mapping a stream generator on the
712-- output.
713--
714-- Yield an input element in the output stream, map a stream generator on it
715-- and then do the same on the resulting stream. This can be used for a depth
716-- first traversal of a tree like structure.
717--
718-- Note that 'iterateM' is a special case of 'iterateMapWith':
719--
720-- @
721-- iterateM f = iterateMapWith serial (yieldM . f) . yieldM
722-- @
723--
724-- It can be used to traverse a tree structure. For example, to list a
725-- directory tree:
726--
727-- @
728-- Stream.iterateMapWith Stream.serial
729-- (either Dir.toEither (const nil))
730-- (yield (Left "tmp"))
731-- @
732--
733-- /Pre-release/
734--
735{-# INLINE iterateMapWith #-}
736iterateMapWith
737 :: IsStream t
738 => (t m a -> t m a -> t m a)
739 -> (a -> t m a)
740 -> t m a
741 -> t m a
742iterateMapWith combine f = concatMapWith combine go
743 where
744 go x = yield x `combine` concatMapWith combine go (f x)
746{-
747{-# INLINE iterateUnfold #-}
748iterateUnfold :: (IsStream t, MonadAsync m)
749 => Unfold m a a -> t m a -> t m a
750iterateUnfold unf xs = undefined
751-}
753------------------------------------------------------------------------------
754-- Flattening Graphs
755------------------------------------------------------------------------------
757-- To traverse graphs we need a state to be carried around in the traversal.
758-- For example, we can use a hashmap to store the visited status of nodes.
760-- | Like 'iterateMap' but carries a state in the stream generation function.
761-- This can be used to traverse graph like structures, we can remember the
762-- visited nodes in the state to avoid cycles.
763--
764-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
765-- traverse graphs. However, this function provides a more localized state
766-- instead of using a global state.
767--
768-- See also: 'mfix'
769--
770-- /Pre-release/
771--
772{-# INLINE iterateSmapMWith #-}
773iterateSmapMWith
774 :: (IsStream t, Monad m)
775 => (t m a -> t m a -> t m a)
776 -> (b -> a -> m (b, t m a))
777 -> m b
778 -> t m a
779 -> t m a
780iterateSmapMWith combine f initial stream =
781 concatMap (\b -> concatMapWith combine (go b) stream) (yieldM initial)
783 where
785 go b a = yield a `combine` feedback b a
787 feedback b a =
788 concatMap
789 (\(b1, s) -> concatMapWith combine (go b1) s)
790 (yieldM $ f b a)
792------------------------------------------------------------------------------
793-- iterateMap - Either streams
794------------------------------------------------------------------------------
796-- | In an 'Either' stream iterate on 'Left's. This is a special case of
797-- 'iterateMapWith':
798--
799-- @
800-- iterateMapLeftsWith combine f = iterateMapWith combine (either f (const nil))
801-- @
802--
803-- To traverse a directory tree:
804--
805-- @
806-- iterateMapLeftsWith serial Dir.toEither (yield (Left "tmp"))
807-- @
808--
809-- /Pre-release/
810--
811{-# INLINE iterateMapLeftsWith #-}
812iterateMapLeftsWith
813 :: IsStream t
814 => (t m (Either a b) -> t m (Either a b) -> t m (Either a b))
815 -> (a -> t m (Either a b))
816 -> t m (Either a b)
817 -> t m (Either a b)
818iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))