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
9-- Expand a stream by combining two or more streams or by combining streams
12module Streamly.Internal.Data.Stream.IsStream.Expand
14 -- * Binary Combinators (Linear)
15 -- | Functions ending in the shape:
17 -- @t m a -> t m a -> t m a@.
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
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
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.
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
83 -- * Combine Streams and Unfolds
85 -- Expand a stream by repeatedly using an unfold and merging the resulting
86 -- streams. Functions generally ending in the shape:
88 -- @Unfold m a b -> t m a -> t m b@
90 -- ** Append Many (Unfold)
91 -- | Unfold and flatten streams.
93 , unfoldManyInterleave
94 , unfoldManyRoundRobin
97 -- | Insert effects between streams. Like unfoldMany but intersperses an
98 -- effect between the streams. A special case of gintercalate.
104 -- | Insert Streams between Streams.
105 -- Like unfoldMany but intersperses streams from another source between
106 -- the streams from the first source.
112 -- * Append Many (concatMap)
113 -- | Map and serially append streams. 'concatMapM' is a generalization of
114 -- the binary append operation to append many streams.
120 -- * Flatten Containers
121 -- | Flatten 'Foldable' containers using the binary stream merging
124 , concatMapFoldableWith
125 , concatForFoldableWith
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.
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'.
144 -- | See the notes about suitable merge functions in the 'concatMapWith'
149 -- | Map and flatten Trees of Streams
152 , iterateMapLeftsWith
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
182import qualified Streamly.Internal.Data.Stream.StreamD as S
184import qualified Streamly.Internal.Data.Stream.Zip as Z
186import Prelude hiding (concat, concatMap)
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-------------------------------------------------------------------------------
201-------------------------------------------------------------------------------
203-- XXX Reconcile the names "serial" and "append".
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.
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.
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-------------------------------------------------------------------------------
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.
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.
236-- >>> :set -XOverloadedStrings
237-- >>> import Data.Functor.Identity (Identity)
238-- >>> Stream.interleave "ab" ",,,," :: Stream.SerialT Identity Char
240-- >>> Stream.interleave "abcd" ",," :: Stream.SerialT Identity Char
243-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
245-- Do not use at scale in concatMapWith.
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.
259-- >>> :set -XOverloadedStrings
260-- >>> import Data.Functor.Identity (Identity)
261-- >>> Stream.interleaveSuffix "abc" ",,,," :: Stream.SerialT Identity Char
263-- >>> Stream.interleaveSuffix "abc" "," :: Stream.SerialT Identity Char
266-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
268-- Do not use at scale in concatMapWith.
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
283-- >>> :set -XOverloadedStrings
284-- >>> import Data.Functor.Identity (Identity)
285-- >>> Stream.interleaveInfix "abc" ",,,," :: Stream.SerialT Identity Char
287-- >>> Stream.interleaveInfix "abc" "," :: Stream.SerialT Identity Char
290-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
292-- Do not use at scale in concatMapWith.
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
306-- >>> :set -XOverloadedStrings
307-- >>> import Data.Functor.Identity (Identity)
308-- >>> Stream.interleaveMin "ab" ",,,," :: Stream.SerialT Identity Char
310-- >>> Stream.interleaveMin "abcd" ",," :: Stream.SerialT Identity Char
313-- 'interleaveMin' is dual to 'interleave'.
315-- Do not use at scale in concatMapWith.
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-------------------------------------------------------------------------------
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`.
335-- Do not use at scale in concatMapWith.
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
351-- If the streams are sorted in ascending order, the resulting stream would
352-- also remain sorted in ascending order.
355-- >>> Stream.toList $ Stream.mergeBy compare (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
361{-# INLINABLE mergeBy #-}
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.
368-- Merge two streams randomly:
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])
376-- Merge two streams in a proportion of 2:1:
379-- proportionately m n = do
380-- ref <- newIORef $ cycle $ concat [replicate m LT, replicate n GT]
381-- return $ \\_ _ -> do
383-- writeIORef ref $ tail r
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])
392-- [1,1,2,1,1,2,1,1,2]
396{-# INLINABLE mergeByM #-}
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)
403-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
404{-# INLINABLE mergeEndByAny #-}
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 #-}
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'@.
423-- > S.toList $ S.merge (S.fromList [1,3,5]) (S.fromList [2,4,6,8])
428{-# INLINABLE merge #-}
430 (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
431merge = mergeBy compare
434-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
435-- merged are generated concurrently).
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).
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 =
452 let par = Par.mkParallelD . toStreamD
453 in D.mergeByM f (par m1) (par m2)
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.
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.
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
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)
501-- | Unfold the elements of a stream, intersperse the given element between the
502-- unfolded streams and then concat them into a single stream.
504-- > unwords = S.interpose ' '
507{-# INLINE interpose #-}
508interpose :: (IsStream t, Monad m)
509 => c -> Unfold m b c -> t m b -> t m c
511 D.fromStreamD $ D.interpose (return x) unf (D.toStreamD str)
513-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
515-- | Unfold the elements of a stream, append the given element after each
516-- unfolded stream and then concat them into a single stream.
518-- > unlines = S.interposeSuffix '\n'
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.
536-- > unfoldMany unf str =
537-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
539-- | 'interleaveInfix' followed by unfold and concat.
542{-# INLINE gintercalate #-}
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
560-- | 'intersperse' followed by unfold and concat.
562-- > unwords = intercalate " " Unfold.fromList
564-- >>> Stream.toList $ Stream.intercalate " " Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
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.
577{-# INLINE gintercalateSuffix #-}
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
589-- | 'intersperseSuffix' followed by unfold and concat.
591-- > unlines = intercalateSuffix "\n" Unfold.fromList
593-- >>> Stream.toList $ Stream.intercalateSuffix "\n" Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
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)
604{-# INLINE iterateUnfold #-}
605iterateUnfold :: (IsStream t, MonadAsync m)
606 => Unfold m a a -> t m a -> t m a
607iterateUnfold unf xs = undefined
610------------------------------------------------------------------------------
611-- Combine N Streams - concatMap
612------------------------------------------------------------------------------
614-- | Flatten a stream of streams to a single stream.
617-- concat = concatMap id
622concat :: (IsStream t, Monad m) => t m (t m a) -> t m a
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
634-- Note we can merge streams concurrently by using a concurrent merge function.
638-- /Since: 0.8.0 (signature change)/
639{-# INLINE concatMapWith #-}
642 => (t m b -> t m b -> 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.
652-- concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
657{-# INLINE concatSmapMWith #-}
659 :: (IsStream t, Monad m)
660 => (t m b -> t m b -> t m b)
661 -> (s -> a -> m (s, 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.
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))
681concatMapEitherWith = undefined
684-- XXX Implement a StreamD version for fusion.
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
690-- For example, you can sort a stream using merge sort like this:
692-- >>> Stream.toList $ Stream.concatPairsWith (Stream.mergeBy compare) Stream.yield $ Stream.fromList [5,1,7,9,2]
695-- /Caution: the stream of streams must be finite/
699{-# INLINE concatPairsWith #-}
700concatPairsWith :: IsStream t =>
701 (t m b -> t m b -> 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
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.
718-- Note that 'iterateM' is a special case of 'iterateMapWith':
721-- iterateM f = iterateMapWith serial (yieldM . f) . yieldM
724-- It can be used to traverse a tree structure. For example, to list a
728-- Stream.iterateMapWith Stream.serial
729-- (either Dir.toEither (const nil))
730-- (yield (Left "tmp"))
735{-# INLINE iterateMapWith #-}
738 => (t m a -> t m a -> t m a)
742iterateMapWith combine f = concatMapWith combine go
744 go x = yield x `combine` concatMapWith combine go (f x)
747{-# INLINE iterateUnfold #-}
748iterateUnfold :: (IsStream t, MonadAsync m)
749 => Unfold m a a -> t m a -> t m a
750iterateUnfold unf xs = undefined
753------------------------------------------------------------------------------
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.
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.
772{-# INLINE iterateSmapMWith #-}
774 :: (IsStream t, Monad m)
775 => (t m a -> t m a -> t m a)
776 -> (b -> a -> m (b, t m a))
780iterateSmapMWith combine f initial stream =
781 concatMap (\b -> concatMapWith combine (go b) stream) (yieldM initial)
785 go b a = yield a `combine` feedback b a
789 (\(b1, s) -> concatMapWith combine (go b1) s)
792------------------------------------------------------------------------------
793-- iterateMap - Either streams
794------------------------------------------------------------------------------
796-- | In an 'Either' stream iterate on 'Left's. This is a special case of
800-- iterateMapLeftsWith combine f = iterateMapWith combine (either f (const nil))
803-- To traverse a directory tree:
806-- iterateMapLeftsWith serial Dir.toEither (yield (Left "tmp"))
811{-# INLINE iterateMapLeftsWith #-}
814 => (t m (Either a b) -> t m (Either a b) -> t m (Either a b))
815 -> (a -> t m (Either a b))
818iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))
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