on master help sign in
Split IsStream.Nesting into Expand and Reduce
commit8be2c1c4847f9a5ad5935c055afc92034e5771ac authorHarendra Kumar <harendra@composewell.com> dateSat 13 Mar 2021, 01:58 (6 years ago) parentee691b6
7 files changed+913−813
src/Streamly/Internal/Data/Stream/IsStream.hs+4−2 src/Streamly/Internal/Data/Stream/IsStream/Common.hs+17−1 src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+810 src/Streamly/Internal/Data/Stream/IsStream/Nesting.hs → src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+75−808 src/Streamly/Internal/Data/Stream/IsStream/Top.hs+2−1 src/Streamly/Internal/Data/Stream/Prelude.hs+3 streamly.cabal+2−1
src/Streamly/Internal/Data/Stream/IsStream.hs+4−2
@@ -15,7 +15,8 @@ module Streamly.Internal.Data.Stream.IsStream
1515 , module Streamly.Internal.Data.Stream.IsStream.Generate
1616 , module Streamly.Internal.Data.Stream.IsStream.Eliminate
1717 , module Streamly.Internal.Data.Stream.IsStream.Transform
18− , module Streamly.Internal.Data.Stream.IsStream.Nesting
18+ , module Streamly.Internal.Data.Stream.IsStream.Expand
19+ , module Streamly.Internal.Data.Stream.IsStream.Reduce
1920 , module Streamly.Internal.Data.Stream.IsStream.Exception
2021 , module Streamly.Internal.Data.Stream.IsStream.Lift
2122 , module Streamly.Internal.Data.Stream.IsStream.Top
@@ -27,6 +28,7 @@ import Streamly.Internal.Data.Stream.IsStream.Eliminate
2728import Streamly.Internal.Data.Stream.IsStream.Exception
2829import Streamly.Internal.Data.Stream.IsStream.Generate
2930import Streamly.Internal.Data.Stream.IsStream.Lift
30−import Streamly.Internal.Data.Stream.IsStream.Nesting
31+import Streamly.Internal.Data.Stream.IsStream.Expand
32+import Streamly.Internal.Data.Stream.IsStream.Reduce
3133import Streamly.Internal.Data.Stream.IsStream.Transform
3234import Streamly.Internal.Data.Stream.IsStream.Types
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+17−1
@@ -42,6 +42,7 @@ module Streamly.Internal.Data.Stream.IsStream.Common
4242 -- * Nesting
4343 , concatM
4444 , concatMapM
45+ , concatMap
4546 , splitOnSeq
4647 )
4748where
@@ -72,7 +73,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK as S
7273import qualified Streamly.Internal.Data.Stream.StreamD as S
7374#endif
7475
75−import Prelude hiding (take, takeWhile, drop, reverse)
76+import Prelude hiding (take, takeWhile, drop, reverse, concatMap)
7677
7778--
7879-- $setup
@@ -452,6 +453,21 @@ reverse' s = fromStreamD $ D.reverse' $ toStreamD s
452453concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
453454concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
454455
456+-- | Map a stream producing function on each element of the stream and then
457+-- flatten the results into a single stream.
458+--
459+-- @
460+-- concatMap f = 'concatMapM' (return . f)
461+-- concatMap = 'concatMapWith' 'Serial.serial'
462+-- concatMap f = 'concat . map f'
463+-- concatMap f = 'concatUnfold' (UF.lmap f UF.fromStream)
464+-- @
465+--
466+-- @since 0.6.0
467+{-# INLINE concatMap #-}
468+concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
469+concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
470+
455471-- | Given a stream value in the underlying monad, lift and join the underlying
456472-- monad with the stream monad.
457473--
src/Streamly/Internal/Data/Stream/IsStream/Expand.hsnew file+810
@@ -0,0 +1,810 @@
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.
11+
12+module 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).
27+
28+ serial
29+ , ahead
30+ , async
31+ , wAsync
32+ , parallel
33+ , Par.parallelFst
34+ , Par.parallelMin
35+
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.
43+
44+ -- ** Append
45+ , append
46+
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
56+
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
66+
67+ -- ** Round Robin
68+ , roundrobin
69+
70+ -- ** Zip
71+ , Z.zipWith
72+ , Z.zipWithM
73+ , Z.zipAsyncWith
74+ , Z.zipAsyncWithM
75+
76+ -- ** Merge
77+ -- , merge
78+ , mergeBy
79+ , mergeByM
80+ , mergeAsyncBy
81+ , mergeAsyncByM
82+
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@
89+
90+ -- ** Append Many (Unfold)
91+ -- | Unfold and flatten streams.
92+ , concatUnfold
93+ , concatUnfoldInterleave
94+ , concatUnfoldRoundrobin
95+
96+ -- ** Interpose
97+ -- | Insert effects between streams. Like concatUnfold but intersperses an
98+ -- effect between the streams. A special case of gintercalate.
99+ , interpose
100+ , interposeSuffix
101+ -- , interposeBy
102+
103+ -- ** Intercalate
104+ -- | Insert Streams between Streams.
105+ -- Like concatUnfold but intersperses streams from another source between
106+ -- the streams from the first source.
107+ , intercalate
108+ , intercalateSuffix
109+ , gintercalate
110+ , gintercalateSuffix
111+
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
119+
120+ -- * Flatten Containers
121+ -- | Flatten 'Foldable' containers using the binary stream merging
122+ -- operations.
123+ , concatFoldableWith
124+ , concatMapFoldableWith
125+ , concatForFoldableWith
126+
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'.
138+
139+ , concatMapWith
140+ , K.bindWith
141+ , concatSmapMWith
142+
143+ -- * ConcatPairsWith
144+ -- | See the notes about suitable merge functions in the 'concatMapWith'
145+ -- section.
146+ , concatPairsWith
147+
148+ -- * IterateMap
149+ -- | Map and flatten Trees of Streams
150+ , iterateMapWith
151+ , iterateSmapMWith
152+ , iterateMapLeftsWith
153+ )
154+where
155+
156+#include "inline.hs"
157+
158+import Streamly.Internal.Data.Stream.Ahead (ahead)
159+import Streamly.Internal.Data.Stream.Async (async, wAsync)
160+import Streamly.Internal.Data.Stream.IsStream.Common
161+ (concatM, concatMapM, concatMap, smapM, yield, yieldM)
162+import Streamly.Internal.Data.Stream.Parallel (parallel)
163+import Streamly.Internal.Data.Stream.Prelude
164+ ( concatFoldableWith, concatMapFoldableWith
165+ , concatForFoldableWith, fromStreamS, toStreamS)
166+import Streamly.Internal.Data.Stream.Serial (serial, wSerial)
167+import Streamly.Internal.Data.Stream.StreamD (fromStreamD, toStreamD)
168+import Streamly.Internal.Data.Stream.StreamK (IsStream)
169+import Streamly.Internal.Data.SVar (MonadAsync)
170+import Streamly.Internal.Data.Unfold.Type (Unfold)
171+
172+import qualified Streamly.Internal.Data.Stream.Parallel as Par
173+import qualified Streamly.Internal.Data.Stream.Serial as Serial
174+import qualified Streamly.Internal.Data.Stream.StreamD as D
175+import qualified Streamly.Internal.Data.Stream.StreamK as K
176+#ifdef USE_STREAMK_ONLY
177+import qualified Streamly.Internal.Data.Stream.StreamK as S
178+#else
179+import qualified Streamly.Internal.Data.Stream.StreamD as S
180+#endif
181+import qualified Streamly.Internal.Data.Stream.Zip as Z
182+
183+import Prelude hiding (concat, concatMap)
184+
185+-- $setup
186+-- >>> :m
187+-- >>> import Prelude hiding (zipWith, concatMap, concat)
188+-- >>> import qualified Streamly.Prelude as Stream
189+-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
190+-- >>> import qualified Streamly.Data.Fold as Fold
191+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
192+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
193+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
194+-- >>> import qualified Streamly.Data.Array.Foreign as Array
195+
196+-------------------------------------------------------------------------------
197+-- Appending
198+-------------------------------------------------------------------------------
199+
200+-- XXX Reconcile the names "serial" and "append".
201+--
202+-- | Append the outputs of two streams, yielding all the elements from the
203+-- first stream and then yielding all the elements from the second stream.
204+--
205+-- IMPORTANT NOTE: This could be 100x faster than @serial/<>@ for appending a
206+-- few (say 100) streams because it can fuse via stream fusion. However, it
207+-- does not scale for a large number of streams (say 1000s) and becomes
208+-- qudartically slow. Therefore use this for custom appending of a few streams
209+-- but use 'concatMap' or 'concatMapWith serial' for appending @n@ streams or
210+-- infinite containers of streams.
211+--
212+-- /Pre-release/
213+{-# INLINE append #-}
214+append ::(IsStream t, Monad m) => t m b -> t m b -> t m b
215+append m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
216+
217+-------------------------------------------------------------------------------
218+-- Interleaving
219+-------------------------------------------------------------------------------
220+
221+-- XXX Same as 'wSerial'. We should perhaps rename wSerial to interleave.
222+-- XXX Document the interleaving behavior of side effects in all the
223+-- interleaving combinators.
224+-- XXX Write time-domain equivalents of these. In the time domain we can
225+-- interleave two streams such that the value of second stream is always taken
226+-- from its last value even if no new value is being yielded, like
227+-- zipWithLatest. It would be something like interleaveWithLatest.
228+--
229+-- | Interleaves the outputs of two streams, yielding elements from each stream
230+-- alternately, starting from the first stream. If any of the streams finishes
231+-- early the other stream continues alone until it too finishes.
232+--
233+-- >>> :set -XOverloadedStrings
234+-- >>> import Data.Functor.Identity (Identity)
235+-- >>> Stream.interleave "ab" ",,,," :: Stream.SerialT Identity Char
236+-- fromList "a,b,,,"
237+-- >>> Stream.interleave "abcd" ",," :: Stream.SerialT Identity Char
238+-- fromList "a,b,cd"
239+--
240+-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
241+--
242+-- Do not use at scale in concatMapWith.
243+--
244+-- /Pre-release/
245+{-# INLINE interleave #-}
246+interleave ::(IsStream t, Monad m) => t m b -> t m b -> t m b
247+interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
248+
249+-- | Interleaves the outputs of two streams, yielding elements from each stream
250+-- alternately, starting from the first stream. As soon as the first stream
251+-- finishes, the output stops, discarding the remaining part of the second
252+-- stream. In this case, the last element in the resulting stream would be from
253+-- the second stream. If the second stream finishes early then the first stream
254+-- still continues to yield elements until it finishes.
255+--
256+-- >>> :set -XOverloadedStrings
257+-- >>> import Data.Functor.Identity (Identity)
258+-- >>> Stream.interleaveSuffix "abc" ",,,," :: Stream.SerialT Identity Char
259+-- fromList "a,b,c,"
260+-- >>> Stream.interleaveSuffix "abc" "," :: Stream.SerialT Identity Char
261+-- fromList "a,bc"
262+--
263+-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
264+--
265+-- Do not use at scale in concatMapWith.
266+--
267+-- /Pre-release/
268+{-# INLINE interleaveSuffix #-}
269+interleaveSuffix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
270+interleaveSuffix m1 m2 =
271+ fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
272+
273+-- | Interleaves the outputs of two streams, yielding elements from each stream
274+-- alternately, starting from the first stream and ending at the first stream.
275+-- If the second stream is longer than the first, elements from the second
276+-- stream are infixed with elements from the first stream. If the first stream
277+-- is longer then it continues yielding elements even after the second stream
278+-- has finished.
279+--
280+-- >>> :set -XOverloadedStrings
281+-- >>> import Data.Functor.Identity (Identity)
282+-- >>> Stream.interleaveInfix "abc" ",,,," :: Stream.SerialT Identity Char
283+-- fromList "a,b,c"
284+-- >>> Stream.interleaveInfix "abc" "," :: Stream.SerialT Identity Char
285+-- fromList "a,bc"
286+--
287+-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
288+--
289+-- Do not use at scale in concatMapWith.
290+--
291+-- /Pre-release/
292+{-# INLINE interleaveInfix #-}
293+interleaveInfix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
294+interleaveInfix m1 m2 =
295+ fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
296+
297+-- | Interleaves the outputs of two streams, yielding elements from each stream
298+-- alternately, starting from the first stream. The output stops as soon as any
299+-- of the two streams finishes, discarding the remaining part of the other
300+-- stream. The last element of the resulting stream would be from the longer
301+-- stream.
302+--
303+-- >>> :set -XOverloadedStrings
304+-- >>> import Data.Functor.Identity (Identity)
305+-- >>> Stream.interleaveMin "ab" ",,,," :: Stream.SerialT Identity Char
306+-- fromList "a,b,"
307+-- >>> Stream.interleaveMin "abcd" ",," :: Stream.SerialT Identity Char
308+-- fromList "a,b,c"
309+--
310+-- 'interleaveMin' is dual to 'interleave'.
311+--
312+-- Do not use at scale in concatMapWith.
313+--
314+-- /Pre-release/
315+{-# INLINE interleaveMin #-}
316+interleaveMin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
317+interleaveMin m1 m2 = fromStreamD $ D.interleaveMin (toStreamD m1) (toStreamD m2)
318+
319+-------------------------------------------------------------------------------
320+-- Scheduling
321+-------------------------------------------------------------------------------
322+
323+-- | Schedule the execution of two streams in a fair round-robin manner,
324+-- executing each stream once, alternately. Execution of a stream may not
325+-- necessarily result in an output, a stream may chose to @Skip@ producing an
326+-- element until later giving the other stream a chance to run. Therefore, this
327+-- combinator fairly interleaves the execution of two streams rather than
328+-- fairly interleaving the output of the two streams. This can be useful in
329+-- co-operative multitasking without using explicit threads. This can be used
330+-- as an alternative to `async`.
331+--
332+-- Do not use at scale in concatMapWith.
333+--
334+-- /Pre-release/
335+{-# INLINE roundrobin #-}
336+roundrobin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
337+roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)
338+
339+------------------------------------------------------------------------------
340+-- Merging (sorted streams)
341+------------------------------------------------------------------------------
342+
343+-- | Merge two streams using a comparison function. The head elements of both
344+-- the streams are compared and the smaller of the two elements is emitted, if
345+-- both elements are equal then the element from the first stream is used
346+-- first.
347+--
348+-- If the streams are sorted in ascending order, the resulting stream would
349+-- also remain sorted in ascending order.
350+--
351+-- @
352+-- >>> Stream.toList $ Stream.mergeBy compare (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
353+-- [1,2,3,4,5,6,8]
354+--
355+-- @
356+--
357+-- @since 0.6.0
358+{-# INLINABLE mergeBy #-}
359+mergeBy ::
360+ (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
361+mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
362+
363+-- | Like 'mergeBy' but with a monadic comparison function.
364+--
365+-- Merge two streams randomly:
366+--
367+-- @
368+-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
369+-- > S.toList $ S.mergeByM randomly (S.fromList [1,1,1,1]) (S.fromList [2,2,2,2])
370+-- [2,1,2,2,2,1,1,1]
371+-- @
372+--
373+-- Merge two streams in a proportion of 2:1:
374+--
375+-- @
376+-- proportionately m n = do
377+-- ref <- newIORef $ cycle $ concat [replicate m LT, replicate n GT]
378+-- return $ \\_ _ -> do
379+-- r <- readIORef ref
380+-- writeIORef ref $ tail r
381+-- return $ head r
382+--
383+-- main = do
384+-- f <- proportionately 2 1
385+-- xs <- S.toList $ S.mergeByM f (S.fromList [1,1,1,1,1,1]) (S.fromList [2,2,2])
386+-- print xs
387+-- @
388+-- @
389+-- [1,1,2,1,1,2,1,1,2]
390+-- @
391+--
392+-- @since 0.6.0
393+{-# INLINABLE mergeByM #-}
394+mergeByM
395+ :: (IsStream t, Monad m)
396+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
397+mergeByM f m1 m2 = fromStreamS $ S.mergeByM f (toStreamS m1) (toStreamS m2)
398+
399+{-
400+-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
401+{-# INLINABLE mergeEndByAny #-}
402+mergeEndByAny
403+ :: (IsStream t, Monad m)
404+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
405+mergeEndByAny f m1 m2 = fromStreamD $
406+ D.mergeEndByAny f (toStreamD m1) (toStreamD m2)
407+
408+-- Like 'mergeByM' but stops merging as soon as the first stream stops.
409+{-# INLINABLE mergeEndByFirst #-}
410+mergeEndByFirst
411+ :: (IsStream t, Monad m)
412+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
413+mergeEndByFirst f m1 m2 = fromStreamS $
414+ D.mergeEndByFirst f (toStreamD m1) (toStreamD m2)
415+
416+-- Holding this back for now, we may want to use the name "merge" differently
417+-- | Same as @'mergeBy' 'compare'@.
418+--
419+-- @
420+-- > S.toList $ S.merge (S.fromList [1,3,5]) (S.fromList [2,4,6,8])
421+-- [1,2,3,4,5,6,8]
422+-- @
423+--
424+-- @since 0.6.0
425+{-# INLINABLE merge #-}
426+merge ::
427+ (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
428+merge = mergeBy compare
429+-}
430+
431+-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
432+-- merged are generated concurrently).
433+--
434+-- @since 0.6.0
435+{-# INLINE mergeAsyncBy #-}
436+mergeAsyncBy :: (IsStream t, MonadAsync m)
437+ => (a -> a -> Ordering) -> t m a -> t m a -> t m a
438+mergeAsyncBy f = mergeAsyncByM (\a b -> return $ f a b)
439+
440+-- | Like 'mergeByM' but merges concurrently (i.e. both the elements being
441+-- merged are generated concurrently).
442+--
443+-- @since 0.6.0
444+{-# INLINE mergeAsyncByM #-}
445+mergeAsyncByM :: (IsStream t, MonadAsync m)
446+ => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
447+mergeAsyncByM f m1 m2 =
448+ fromStreamD $
449+ let par = Par.mkParallelD . toStreamD
450+ in D.mergeByM f (par m1) (par m2)
451+
452+------------------------------------------------------------------------------
453+-- Combine N Streams - concatUnfold
454+------------------------------------------------------------------------------
455+
456+-- XXX Rename to unfoldMany
457+--
458+-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
459+-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
460+-- therefore provide many times better performance.
461+--
462+-- @since 0.7.0
463+{-# INLINE concatUnfold #-}
464+concatUnfold ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
465+concatUnfold u m = fromStreamD $ D.concatUnfold u (toStreamD m)
466+
467+-- | Like 'concatUnfold' but interleaves the streams in the same way as
468+-- 'interleave' behaves instead of appending them.
469+--
470+-- /Pre-release/
471+{-# INLINE concatUnfoldInterleave #-}
472+concatUnfoldInterleave ::(IsStream t, Monad m)
473+ => Unfold m a b -> t m a -> t m b
474+concatUnfoldInterleave u m =
475+ fromStreamD $ D.concatUnfoldInterleave u (toStreamD m)
476+
477+-- | Like 'concatUnfold' but executes the streams in the same way as
478+-- 'roundrobin'.
479+--
480+-- /Pre-release/
481+{-# INLINE concatUnfoldRoundrobin #-}
482+concatUnfoldRoundrobin ::(IsStream t, Monad m)
483+ => Unfold m a b -> t m a -> t m b
484+concatUnfoldRoundrobin u m =
485+ fromStreamD $ D.concatUnfoldRoundrobin u (toStreamD m)
486+
487+------------------------------------------------------------------------------
488+-- Combine N Streams - interpose
489+------------------------------------------------------------------------------
490+
491+-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
492+--
493+-- | Unfold the elements of a stream, intersperse the given element between the
494+-- unfolded streams and then concat them into a single stream.
495+--
496+-- > unwords = S.interpose ' '
497+--
498+-- /Pre-release/
499+{-# INLINE interpose #-}
500+interpose :: (IsStream t, Monad m)
501+ => c -> Unfold m b c -> t m b -> t m c
502+interpose x unf str =
503+ D.fromStreamD $ D.interpose (return x) unf (D.toStreamD str)
504+
505+-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
506+--
507+-- | Unfold the elements of a stream, append the given element after each
508+-- unfolded stream and then concat them into a single stream.
509+--
510+-- > unlines = S.interposeSuffix '\n'
511+--
512+-- /Pre-release/
513+{-# INLINE interposeSuffix #-}
514+interposeSuffix :: (IsStream t, Monad m)
515+ => c -> Unfold m b c -> t m b -> t m c
516+interposeSuffix x unf str =
517+ D.fromStreamD $ D.interposeSuffix (return x) unf (D.toStreamD str)
518+
519+------------------------------------------------------------------------------
520+-- Combine N Streams - intercalate
521+------------------------------------------------------------------------------
522+
523+-- XXX we can swap the order of arguments to gintercalate so that the
524+-- definition of concatUnfold becomes simpler? The first stream should be
525+-- infixed inside the second one. However, if we change the order in
526+-- "interleave" as well similarly, then that will make it a bit unintuitive.
527+--
528+-- > concatUnfold unf str =
529+-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
530+--
531+-- | 'interleaveInfix' followed by unfold and concat.
532+--
533+-- /Pre-release/
534+{-# INLINE gintercalate #-}
535+gintercalate
536+ :: (IsStream t, Monad m)
537+ => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
538+gintercalate unf1 str1 unf2 str2 =
539+ D.fromStreamD $ D.gintercalate
540+ unf1 (D.toStreamD str1)
541+ unf2 (D.toStreamD str2)
542+
543+-- XXX The order of arguments in "intercalate" is consistent with the list
544+-- intercalate but inconsistent with gintercalate and other stream interleaving
545+-- combinators. We can change the order of the arguments in other combinators
546+-- but then 'interleave' combinator may become a bit unintuitive because we
547+-- will be starting with the second stream.
548+
549+-- > intercalate seed unf str = gintercalate unf str unf (repeatM seed)
550+-- > intercalate a unf str = concatUnfold unf $ intersperse a str
551+--
552+-- | 'intersperse' followed by unfold and concat.
553+--
554+-- > unwords = intercalate " " Unfold.fromList
555+--
556+-- >>> Stream.toList $ Stream.intercalate " " Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
557+-- "abc def ghi"
558+--
559+-- /Pre-release/
560+{-# INLINE intercalate #-}
561+intercalate :: (IsStream t, Monad m)
562+ => b -> Unfold m b c -> t m b -> t m c
563+intercalate seed unf str = D.fromStreamD $
564+ D.concatUnfold unf $ D.intersperse seed (toStreamD str)
565+
566+-- | 'interleaveSuffix' followed by unfold and concat.
567+--
568+-- /Pre-release/
569+{-# INLINE gintercalateSuffix #-}
570+gintercalateSuffix
571+ :: (IsStream t, Monad m)
572+ => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
573+gintercalateSuffix unf1 str1 unf2 str2 =
574+ D.fromStreamD $ D.gintercalateSuffix
575+ unf1 (D.toStreamD str1)
576+ unf2 (D.toStreamD str2)
577+
578+-- > intercalateSuffix seed unf str = gintercalateSuffix unf str unf (repeatM seed)
579+-- > intercalateSuffix a unf str = concatUnfold unf $ intersperseSuffix a str
580+--
581+-- | 'intersperseSuffix' followed by unfold and concat.
582+--
583+-- > unlines = intercalateSuffix "\n" Unfold.fromList
584+--
585+-- >>> Stream.toList $ Stream.intercalateSuffix "\n" Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
586+-- "abc\ndef\nghi\n"
587+--
588+-- /Pre-release/
589+{-# INLINE intercalateSuffix #-}
590+intercalateSuffix :: (IsStream t, Monad m)
591+ => b -> Unfold m b c -> t m b -> t m c
592+intercalateSuffix seed unf str = fromStreamD $ D.concatUnfold unf
593+ $ D.intersperseSuffix (return seed) (D.toStreamD str)
594+
595+{-
596+{-# INLINE iterateUnfold #-}
597+iterateUnfold :: (IsStream t, MonadAsync m)
598+ => Unfold m a a -> t m a -> t m a
599+iterateUnfold unf xs = undefined
600+-}
601+
602+------------------------------------------------------------------------------
603+-- Combine N Streams - concatMap
604+------------------------------------------------------------------------------
605+
606+-- | Flatten a stream of streams to a single stream.
607+--
608+-- @
609+-- concat = concatMap id
610+-- @
611+--
612+-- /Pre-release/
613+{-# INLINE concat #-}
614+concat :: (IsStream t, Monad m) => t m (t m a) -> t m a
615+concat = concatMap id
616+
617+------------------------------------------------------------------------------
618+-- Combine N Streams - concatMap
619+------------------------------------------------------------------------------
620+
621+-- | @concatMapWith mixer generator stream@ is a two dimensional looping
622+-- combinator. The @generator@ function is used to generate streams from the
623+-- elements in the input @stream@ and the @mixer@ function is used to merge
624+-- those streams.
625+--
626+-- Note we can merge streams concurrently by using a concurrent merge function.
627+--
628+-- /Since: 0.7.0/
629+--
630+-- /Since: 0.8.0 (signature change)/
631+{-# INLINE concatMapWith #-}
632+concatMapWith
633+ :: IsStream t
634+ => (t m b -> t m b -> t m b)
635+ -> (a -> t m b)
636+ -> t m a
637+ -> t m b
638+concatMapWith = K.concatMapBy
639+
640+-- | Like 'concatMapWith' but carries a state which can be used to share
641+-- information across multiple steps of concat.
642+--
643+-- @
644+-- concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
645+-- @
646+--
647+-- /Pre-release/
648+--
649+{-# INLINE concatSmapMWith #-}
650+concatSmapMWith
651+ :: (IsStream t, Monad m)
652+ => (t m b -> t m b -> t m b)
653+ -> (s -> a -> m (s, t m b))
654+ -> m s
655+ -> t m a
656+ -> t m b
657+concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
658+
659+-- Keep concating either streams as long as rights are generated, stop as soon
660+-- as a left is generated and concat the left stream.
661+--
662+-- See also: 'handle'
663+--
664+-- /Unimplemented/
665+--
666+{-
667+concatMapEitherWith
668+ :: -- (IsStream t, MonadAsync m) =>
669+ (forall x. t m x -> t m x -> t m x)
670+ -> (a -> t m (Either (t m b) b))
671+ -> t m a
672+ -> t m b
673+concatMapEitherWith = undefined
674+-}
675+
676+-- XXX Implement a StreamD version for fusion.
677+--
678+-- | Combine streams in pairs using a binary stream combinator, then combine
679+-- the resulting streams in pairs recursively until we get to a single combined
680+-- stream.
681+--
682+-- For example, you can sort a stream using merge sort like this:
683+--
684+-- >>> Stream.toList $ Stream.concatPairsWith (Stream.mergeBy compare) Stream.yield $ Stream.fromList [5,1,7,9,2]
685+-- [1,2,5,7,9]
686+--
687+-- /Caution: the stream of streams must be finite/
688+--
689+-- /Pre-release/
690+--
691+{-# INLINE concatPairsWith #-}
692+concatPairsWith :: IsStream t =>
693+ (t m b -> t m b -> t m b)
694+ -> (a -> t m b)
695+ -> t m a
696+ -> t m b
697+concatPairsWith = K.concatPairsWith
698+
699+------------------------------------------------------------------------------
700+-- IterateMap - Map and flatten Trees of Streams
701+------------------------------------------------------------------------------
702+
703+-- | Like 'iterateM' but iterates after mapping a stream generator on the
704+-- output.
705+--
706+-- Yield an input element in the output stream, map a stream generator on it
707+-- and then do the same on the resulting stream. This can be used for a depth
708+-- first traversal of a tree like structure.
709+--
710+-- Note that 'iterateM' is a special case of 'iterateMapWith':
711+--
712+-- @
713+-- iterateM f = iterateMapWith serial (yieldM . f) . yieldM
714+-- @
715+--
716+-- It can be used to traverse a tree structure. For example, to list a
717+-- directory tree:
718+--
719+-- @
720+-- Stream.iterateMapWith Stream.serial
721+-- (either Dir.toEither (const nil))
722+-- (yield (Left "tmp"))
723+-- @
724+--
725+-- /Pre-release/
726+--
727+{-# INLINE iterateMapWith #-}
728+iterateMapWith
729+ :: IsStream t
730+ => (t m a -> t m a -> t m a)
731+ -> (a -> t m a)
732+ -> t m a
733+ -> t m a
734+iterateMapWith combine f = concatMapWith combine go
735+ where
736+ go x = yield x `combine` concatMapWith combine go (f x)
737+
738+{-
739+{-# INLINE iterateUnfold #-}
740+iterateUnfold :: (IsStream t, MonadAsync m)
741+ => Unfold m a a -> t m a -> t m a
742+iterateUnfold unf xs = undefined
743+-}
744+
745+------------------------------------------------------------------------------
746+-- Flattening Graphs
747+------------------------------------------------------------------------------
748+
749+-- To traverse graphs we need a state to be carried around in the traversal.
750+-- For example, we can use a hashmap to store the visited status of nodes.
751+
752+-- | Like 'iterateMap' but carries a state in the stream generation function.
753+-- This can be used to traverse graph like structures, we can remember the
754+-- visited nodes in the state to avoid cycles.
755+--
756+-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
757+-- traverse graphs. However, this function provides a more localized state
758+-- instead of using a global state.
759+--
760+-- See also: 'mfix'
761+--
762+-- /Pre-release/
763+--
764+{-# INLINE iterateSmapMWith #-}
765+iterateSmapMWith
766+ :: (IsStream t, Monad m)
767+ => (t m a -> t m a -> t m a)
768+ -> (b -> a -> m (b, t m a))
769+ -> m b
770+ -> t m a
771+ -> t m a
772+iterateSmapMWith combine f initial stream =
773+ concatMap (\b -> concatMapWith combine (go b) stream) (yieldM initial)
774+
775+ where
776+
777+ go b a = yield a `combine` feedback b a
778+
779+ feedback b a =
780+ concatMap
781+ (\(b1, s) -> concatMapWith combine (go b1) s)
782+ (yieldM $ f b a)
783+
784+------------------------------------------------------------------------------
785+-- iterateMap - Either streams
786+------------------------------------------------------------------------------
787+
788+-- | In an 'Either' stream iterate on 'Left's. This is a special case of
789+-- 'iterateMapWith':
790+--
791+-- @
792+-- iterateMapLeftsWith combine f = iterateMapWith combine (either f (const nil))
793+-- @
794+--
795+-- To traverse a directory tree:
796+--
797+-- @
798+-- iterateMapLeftsWith serial Dir.toEither (yield (Left "tmp"))
799+-- @
800+--
801+-- /Pre-release/
802+--
803+{-# INLINE iterateMapLeftsWith #-}
804+iterateMapLeftsWith
805+ :: IsStream t
806+ => (t m (Either a b) -> t m (Either a b) -> t m (Either a b))
807+ -> (a -> t m (Either a b))
808+ -> t m (Either a b)
809+ -> t m (Either a b)
810+iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))
src/Streamly/Internal/Data/Stream/IsStream/Nesting.hs → src/Streamly/Internal/Data/Stream/IsStream/Reduce.hsrenamed+75−808
@@ -1,171 +1,43 @@
11-- |
2−-- Module : Streamly.Internal.Data.Stream.IsStream.Nesting
2+-- Module : Streamly.Internal.Data.Stream.IsStream.Reduce
33-- Copyright : (c) 2017 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
66-- Stability : experimental
77-- Portability : GHC
88--
9−-- Transformations involving multiple streams, streams and unfolds or streams
10−-- and folds.
9+-- Reduce streams by streams, folds or parsers.
1110
12−module Streamly.Internal.Data.Stream.IsStream.Nesting
11+module Streamly.Internal.Data.Stream.IsStream.Reduce
1312 (
14− -- * Generate
15− -- | Combining streams to generate streams.
16−
17− -- ** Combine Two Streams
18− -- | Functions ending in the shape:
19− --
20− -- @t m a -> t m a -> t m a@.
21−
22− -- *** Appending
23− serial
24− , append
25−
26− -- *** Interleaving
27− , interleave
28− , interleaveMin
29− , interleaveSuffix
30− , interleaveInfix
31−
32− , wSerial
33− , Serial.wSerialFst
34− , Serial.wSerialMin
35−
36− -- *** Scheduling
37− , ahead
38− , async
39− , wAsync
40− , roundrobin
41−
42− -- *** Parallel
43− , parallel
44− , Par.parallelFst
45− , Par.parallelMin
46−
47− -- *** Zipping
48− , Z.zipWith
49− , Z.zipWithM
50− , Z.zipAsyncWith
51− , Z.zipAsyncWithM
52−
53− -- *** Merging
54− -- , merge
55− , mergeBy
56− , mergeByM
57− , mergeAsyncBy
58− , mergeAsyncByM
59−
60− -- *** Trimming
61− , dropPrefix
13+ -- * Reduce By Streams
14+ dropPrefix
6215 , dropInfix
6316 , dropSuffix
6417
65− -- ** Combine N Streams
66− -- | Functions generally ending in these shapes:
67− --
68− -- @
69− -- concat: f (t m a) -> t m a
70− -- concatMap: (a -> t m b) -> t m a -> t m b
71− -- concatUnfold: Unfold m a b -> t m a -> t m b
72− -- @
73−
74− -- *** Flatten Containers of Streams
75− , concatM
76− , concat
77− , concatFoldableWith
78−
79− -- *** ConcatMap
80− -- | Map and flatten streams.
81− , concatMapFoldableWith
82− , concatForFoldableWith
83− , concatMap
84− , concatMapM
85− , concatMapWith
86− , concatSmapMWith
87− , K.bindWith
88− , concatPairsWith
89−
90− -- *** ConcatUnfold
91− -- | Unfold and flatten streams.
92− , concatUnfold
93− , concatUnfoldInterleave
94− , concatUnfoldRoundrobin
95−
96− -- *** Interpose
97− -- | Insert effects between streams. Like concatUnfold but intersperses an
98− -- effect between the streams. A
99− -- special case of gintercalate.
100− , interpose
101− , interposeSuffix
102− -- , interposeBy
103−
104− -- *** Intercalate
105− -- | Insert Streams between Streams.
106− -- Like concatUnfold but intersperses streams from another source between
107− -- the streams from the first source.
108− , intercalate
109− , intercalateSuffix
110− , gintercalate
111− , gintercalateSuffix
112−
113− -- *** IterateMap
114− -- | Map and flatten Trees of Streams
115− , iterateMapWith
116− , iterateSmapMWith
117− , iterateMapLeftsWith
118−
119− -- * Eliminate
120− -- | Folding and Parsing chunks of streams to eliminate nested streams.
121− -- Functions generally ending in these shapes:
18+ -- * Reduce By Folds
19+ -- |
20+ -- Reduce a stream by folding or parsing chunks of the stream. Functions
21+ -- generally ending in these shapes:
12222 --
12323 -- @
12424 -- f (Fold m a b) -> t m a -> t m b
12525 -- f (Parser m a b) -> t m a -> t m b
12626 -- @
12727
128− -- ** Folding
28+ -- ** Generic Folding
12929 -- | Apply folds on a stream.
13030 , foldMany
13131 , foldManyPost
13232 , foldSequence
13333 , foldIterateM
13434
135− -- ** Parsing
136− -- | Apply parsers on a stream.
137− , parseMany
138− , parseManyD
139− , parseManyTill
140− , parseSequence
141− , parseIterate
142−
14335 -- ** Chunking
14436 -- | Element unaware grouping.
14537 , chunksOf
146− , chunksOf2
14738 , arraysOf
14839 , intervalsOf
14940
150− -- ** Grouping
151− -- In imperative terms, grouped folding can be considered as a nested loop
152− -- where we loop over the stream to group elements and then loop over
153− -- individual groups to fold them to a single value that is yielded in the
154− -- output stream.
155−
156− , groups
157− , groupsBy
158− , groupsByRolling
159−
160− -- -- *** Searching Sequences
161− -- , seqIndices -- search a sequence in the stream
162−
163− -- -- *** Searching Multiple Sequences
164− -- , seqIndicesAny -- search any of the given sequence in the stream
165−
166− -- -- -- ** Searching Streams
167− -- -- | Finding a stream within another stream.
168−
16941 -- ** Splitting
17042 -- | Streams can be sliced into segments in space or in time. We use the
17143 -- term @chunk@ to refer to a spatial length of the stream (spatial window)
@@ -179,7 +51,6 @@ module Streamly.Internal.Data.Stream.IsStream.Nesting
17951 -- , splitBy
18052 , splitWithSuffix
18153 -- , splitByPrefix
182− , wordsBy -- stripAndCompactBy
18354
18455 -- -- *** Splitting By Sequences
18556 , splitBySeq
@@ -235,10 +106,41 @@ module Streamly.Internal.Data.Stream.IsStream.Nesting
235106 -- , slidingSessionBuffer
236107 -}
237108
238− -- * Transform (Nested Containers)
239− -- Nested splitting
109+ -- * Reduce By Parsers
110+ -- ** Generic Parsing
111+ -- | Apply parsers on a stream.
112+ , parseMany
113+ , parseManyD
114+ , parseManyTill
115+ , parseSequence
116+ , parseIterate
117+
118+ -- ** Grouping
119+ -- In imperative terms, grouped folding can be considered as a nested loop
120+ -- where we loop over the stream to group elements and then loop over
121+ -- individual groups to fold them to a single value that is yielded in the
122+ -- output stream.
123+
124+ , wordsBy -- stripAndCompactBy
125+ , groups
126+ , groupsBy
127+ , groupsByRolling
128+
129+ -- -- *** Searching Sequences
130+ -- , seqIndices -- search a sequence in the stream
131+
132+ -- -- *** Searching Multiple Sequences
133+ -- , seqIndicesAny -- search any of the given sequence in the stream
134+
135+ -- -- -- ** Searching Streams
136+ -- -- | Finding a stream within another stream.
137+
138+ -- * Nested splitting
240139 , splitInnerBy
241140 , splitInnerBySuffix
141+
142+ -- * Fold2
143+ , chunksOf2
242144 )
243145where
244146
@@ -254,30 +156,18 @@ import Data.Maybe (isNothing)
254156import Foreign.Storable (Storable)
255157import Streamly.Internal.Data.Fold.Type (Fold (..), Fold2 (..))
256158import Streamly.Internal.Data.Parser (Parser (..))
257−import Streamly.Internal.Data.Unfold.Type (Unfold)
258159import Streamly.Internal.Data.Array.Foreign.Type (Array)
259160import Streamly.Internal.Data.SVar (MonadAsync)
260−import Streamly.Internal.Data.Stream.Ahead (ahead)
261−import Streamly.Internal.Data.Stream.Async (async, wAsync)
262161import Streamly.Internal.Data.Stream.IsStream.Common
263− ( concatM
264− , concatMapM
162+ ( concatMap
265163 , fold
266164 , interjectSuffix
267165 , intersperseM
268166 , repeatM
269167 , scanlMAfter'
270− , smapM
271168 , splitOnSeq
272− , yield
273− , yieldM)
274−import Streamly.Internal.Data.Stream.Parallel (parallel)
275−import Streamly.Internal.Data.Stream.Prelude
276− ( fromStreamS, toStreamS, concatFoldableWith, concatMapFoldableWith
277− , concatForFoldableWith)
278−import Streamly.Internal.Data.Stream.StreamD (fromStreamD, toStreamD)
169+ , yield)
279170import Streamly.Internal.Data.Stream.StreamK (IsStream)
280−import Streamly.Internal.Data.Stream.Serial (serial, wSerial)
281171import Streamly.Internal.Data.Time.Units
282172 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
283173 , toAbsTime)
@@ -293,14 +183,8 @@ import qualified Streamly.Internal.Data.Stream.Parallel as Par
293183import qualified Streamly.Internal.Data.Stream.Serial as Serial
294184import qualified Streamly.Internal.Data.Stream.StreamD as D
295185import qualified Streamly.Internal.Data.Stream.StreamK as K
296−import qualified Streamly.Internal.Data.Stream.Zip as Z
297−#ifdef USE_STREAMK_ONLY
298−import qualified Streamly.Internal.Data.Stream.StreamK as S
299−#else
300−import qualified Streamly.Internal.Data.Stream.StreamD as S
301−#endif
302186
303−import Prelude hiding (zipWith, concatMap, concat)
187+import Prelude hiding (concatMap)
304188
305189-- $setup
306190-- >>> :m
@@ -313,624 +197,44 @@ import Prelude hiding (zipWith, concatMap, concat)
313197-- >>> import qualified Streamly.Internal.Data.Parser as Parser
314198-- >>> import qualified Streamly.Data.Array.Foreign as Array
315199
316−-------------------------------------------------------------------------------
317−-- Appending
318−-------------------------------------------------------------------------------
319−
320−-- | Append the outputs of two streams, yielding all the elements from the
321−-- first stream and then yielding all the elements from the second stream.
322−--
323−-- IMPORTANT NOTE: This could be 100x faster than @serial/<>@ for appending a
324−-- few (say 100) streams because it can fuse via stream fusion. However, it
325−-- does not scale for a large number of streams (say 1000s) and becomes
326−-- qudartically slow. Therefore use this for custom appending of a few streams
327−-- but use 'concatMap' or 'concatMapWith serial' for appending @n@ streams or
328−-- infinite containers of streams.
329−--
330−-- /Pre-release/
331−{-# INLINE append #-}
332−append ::(IsStream t, Monad m) => t m b -> t m b -> t m b
333−append m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
334−
335−-------------------------------------------------------------------------------
336−-- Interleaving
337−-------------------------------------------------------------------------------
338−
339−-- XXX Same as 'wSerial'. We should perhaps rename wSerial to interleave.
340−-- XXX Document the interleaving behavior of side effects in all the
341−-- interleaving combinators.
342−-- XXX Write time-domain equivalents of these. In the time domain we can
343−-- interleave two streams such that the value of second stream is always taken
344−-- from its last value even if no new value is being yielded, like
345−-- zipWithLatest. It would be something like interleaveWithLatest.
346−--
347−-- | Interleaves the outputs of two streams, yielding elements from each stream
348−-- alternately, starting from the first stream. If any of the streams finishes
349−-- early the other stream continues alone until it too finishes.
350−--
351−-- >>> :set -XOverloadedStrings
352−-- >>> import Data.Functor.Identity (Identity)
353−-- >>> Stream.interleave "ab" ",,,," :: Stream.SerialT Identity Char
354−-- fromList "a,b,,,"
355−-- >>> Stream.interleave "abcd" ",," :: Stream.SerialT Identity Char
356−-- fromList "a,b,cd"
357−--
358−-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
359−--
360−-- Do not use at scale in concatMapWith.
361−--
362−-- /Pre-release/
363−{-# INLINE interleave #-}
364−interleave ::(IsStream t, Monad m) => t m b -> t m b -> t m b
365−interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
366−
367−-- | Interleaves the outputs of two streams, yielding elements from each stream
368−-- alternately, starting from the first stream. As soon as the first stream
369−-- finishes, the output stops, discarding the remaining part of the second
370−-- stream. In this case, the last element in the resulting stream would be from
371−-- the second stream. If the second stream finishes early then the first stream
372−-- still continues to yield elements until it finishes.
373−--
374−-- >>> :set -XOverloadedStrings
375−-- >>> import Data.Functor.Identity (Identity)
376−-- >>> Stream.interleaveSuffix "abc" ",,,," :: Stream.SerialT Identity Char
377−-- fromList "a,b,c,"
378−-- >>> Stream.interleaveSuffix "abc" "," :: Stream.SerialT Identity Char
379−-- fromList "a,bc"
380−--
381−-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
382−--
383−-- Do not use at scale in concatMapWith.
384−--
385−-- /Pre-release/
386−{-# INLINE interleaveSuffix #-}
387−interleaveSuffix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
388−interleaveSuffix m1 m2 =
389− fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
390−
391−-- | Interleaves the outputs of two streams, yielding elements from each stream
392−-- alternately, starting from the first stream and ending at the first stream.
393−-- If the second stream is longer than the first, elements from the second
394−-- stream are infixed with elements from the first stream. If the first stream
395−-- is longer then it continues yielding elements even after the second stream
396−-- has finished.
397−--
398−-- >>> :set -XOverloadedStrings
399−-- >>> import Data.Functor.Identity (Identity)
400−-- >>> Stream.interleaveInfix "abc" ",,,," :: Stream.SerialT Identity Char
401−-- fromList "a,b,c"
402−-- >>> Stream.interleaveInfix "abc" "," :: Stream.SerialT Identity Char
403−-- fromList "a,bc"
404−--
405−-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
406−--
407−-- Do not use at scale in concatMapWith.
408−--
409−-- /Pre-release/
410−{-# INLINE interleaveInfix #-}
411−interleaveInfix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
412−interleaveInfix m1 m2 =
413− fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
414−
415−-- | Interleaves the outputs of two streams, yielding elements from each stream
416−-- alternately, starting from the first stream. The output stops as soon as any
417−-- of the two streams finishes, discarding the remaining part of the other
418−-- stream. The last element of the resulting stream would be from the longer
419−-- stream.
420−--
421−-- >>> :set -XOverloadedStrings
422−-- >>> import Data.Functor.Identity (Identity)
423−-- >>> Stream.interleaveMin "ab" ",,,," :: Stream.SerialT Identity Char
424−-- fromList "a,b,"
425−-- >>> Stream.interleaveMin "abcd" ",," :: Stream.SerialT Identity Char
426−-- fromList "a,b,c"
427−--
428−-- 'interleaveMin' is dual to 'interleave'.
429−--
430−-- Do not use at scale in concatMapWith.
431−--
432−-- /Pre-release/
433−{-# INLINE interleaveMin #-}
434−interleaveMin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
435−interleaveMin m1 m2 = fromStreamD $ D.interleaveMin (toStreamD m1) (toStreamD m2)
436−
437−-------------------------------------------------------------------------------
438−-- Scheduling
439−-------------------------------------------------------------------------------
440−
441−-- | Schedule the execution of two streams in a fair round-robin manner,
442−-- executing each stream once, alternately. Execution of a stream may not
443−-- necessarily result in an output, a stream may chose to @Skip@ producing an
444−-- element until later giving the other stream a chance to run. Therefore, this
445−-- combinator fairly interleaves the execution of two streams rather than
446−-- fairly interleaving the output of the two streams. This can be useful in
447−-- co-operative multitasking without using explicit threads. This can be used
448−-- as an alternative to `async`.
449−--
450−-- Do not use at scale in concatMapWith.
451−--
452−-- /Pre-release/
453−{-# INLINE roundrobin #-}
454−roundrobin ::(IsStream t, Monad m) => t m b -> t m b -> t m b
455−roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)
456−
457−------------------------------------------------------------------------------
458−-- Merging (sorted streams)
459−------------------------------------------------------------------------------
460−
461−-- | Merge two streams using a comparison function. The head elements of both
462−-- the streams are compared and the smaller of the two elements is emitted, if
463−-- both elements are equal then the element from the first stream is used
464−-- first.
465−--
466−-- If the streams are sorted in ascending order, the resulting stream would
467−-- also remain sorted in ascending order.
468−--
469−-- @
470−-- >>> Stream.toList $ Stream.mergeBy compare (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
471−-- [1,2,3,4,5,6,8]
472−--
473−-- @
474−--
475−-- @since 0.6.0
476−{-# INLINABLE mergeBy #-}
477−mergeBy ::
478− (IsStream t, Monad m) => (a -> a -> Ordering) -> t m a -> t m a -> t m a
479−mergeBy f m1 m2 = fromStreamS $ S.mergeBy f (toStreamS m1) (toStreamS m2)
480−
481−-- | Like 'mergeBy' but with a monadic comparison function.
482−--
483−-- Merge two streams randomly:
484−--
485−-- @
486−-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
487−-- > S.toList $ S.mergeByM randomly (S.fromList [1,1,1,1]) (S.fromList [2,2,2,2])
488−-- [2,1,2,2,2,1,1,1]
489−-- @
490−--
491−-- Merge two streams in a proportion of 2:1:
492−--
493−-- @
494−-- proportionately m n = do
495−-- ref <- newIORef $ cycle $ concat [replicate m LT, replicate n GT]
496−-- return $ \\_ _ -> do
497−-- r <- readIORef ref
498−-- writeIORef ref $ tail r
499−-- return $ head r
500−--
501−-- main = do
502−-- f <- proportionately 2 1
503−-- xs <- S.toList $ S.mergeByM f (S.fromList [1,1,1,1,1,1]) (S.fromList [2,2,2])
504−-- print xs
505−-- @
506−-- @
507−-- [1,1,2,1,1,2,1,1,2]
508−-- @
509−--
510−-- @since 0.6.0
511−{-# INLINABLE mergeByM #-}
512−mergeByM
513− :: (IsStream t, Monad m)
514− => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
515−mergeByM f m1 m2 = fromStreamS $ S.mergeByM f (toStreamS m1) (toStreamS m2)
516−
517−{-
518−-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
519−{-# INLINABLE mergeEndByAny #-}
520−mergeEndByAny
521− :: (IsStream t, Monad m)
522− => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
523−mergeEndByAny f m1 m2 = fromStreamD $
524− D.mergeEndByAny f (toStreamD m1) (toStreamD m2)
525−
526−-- Like 'mergeByM' but stops merging as soon as the first stream stops.
527−{-# INLINABLE mergeEndByFirst #-}
528−mergeEndByFirst
529− :: (IsStream t, Monad m)
530− => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
531−mergeEndByFirst f m1 m2 = fromStreamS $
532− D.mergeEndByFirst f (toStreamD m1) (toStreamD m2)
533−
534−-- Holding this back for now, we may want to use the name "merge" differently
535−-- | Same as @'mergeBy' 'compare'@.
536−--
537−-- @
538−-- > S.toList $ S.merge (S.fromList [1,3,5]) (S.fromList [2,4,6,8])
539−-- [1,2,3,4,5,6,8]
540−-- @
541−--
542−-- @since 0.6.0
543−{-# INLINABLE merge #-}
544−merge ::
545− (IsStream t, Monad m, Ord a) => t m a -> t m a -> t m a
546−merge = mergeBy compare
547−-}
548−
549−-- | Like 'mergeBy' but merges concurrently (i.e. both the elements being
550−-- merged are generated concurrently).
551−--
552−-- @since 0.6.0
553−{-# INLINE mergeAsyncBy #-}
554−mergeAsyncBy :: (IsStream t, MonadAsync m)
555− => (a -> a -> Ordering) -> t m a -> t m a -> t m a
556−mergeAsyncBy f = mergeAsyncByM (\a b -> return $ f a b)
557−
558−-- | Like 'mergeByM' but merges concurrently (i.e. both the elements being
559−-- merged are generated concurrently).
560−--
561−-- @since 0.6.0
562−{-# INLINE mergeAsyncByM #-}
563−mergeAsyncByM :: (IsStream t, MonadAsync m)
564− => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
565−mergeAsyncByM f m1 m2 =
566− fromStreamD $
567− let par = Par.mkParallelD . toStreamD
568− in D.mergeByM f (par m1) (par m2)
569−
570−------------------------------------------------------------------------------
571−-- Combine N Streams - concatMap
572−------------------------------------------------------------------------------
573−
574−-- | @concatMapWith merge map stream@ is a two dimensional looping combinator.
575−-- The first argument specifies a merge or concat function that is used to
576−-- merge the streams generated by applying the second argument i.e. the @map@
577−-- function to each element of the input stream. The concat function could be
578−-- 'serial', 'parallel', 'async', 'ahead' or any other zip or merge function
579−-- and the second argument could be any stream generation function using a
580−-- seed.
581−--
582−-- /Compare 'foldMapWith'/
583−--
584−-- /Since: 0.7.0/
585−--
586−-- /Since: 0.8.0 (signature change)/
587−{-# INLINE concatMapWith #-}
588−concatMapWith
589− :: IsStream t
590− => (t m b -> t m b -> t m b)
591− -> (a -> t m b)
592− -> t m a
593− -> t m b
594−concatMapWith = K.concatMapBy
595−
596−-- | Like 'concatMapWith' but carries a state which can be used to share
597−-- information across multiple steps of concat.
598−--
599−-- @
600−-- concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
601−-- @
602−--
603−-- /Pre-release/
604−--
605−{-# INLINE concatSmapMWith #-}
606−concatSmapMWith
607− :: (IsStream t, Monad m)
608− => (t m b -> t m b -> t m b)
609− -> (s -> a -> m (s, t m b))
610− -> m s
611− -> t m a
612− -> t m b
613−concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
614−
615−-- | Map a stream producing function on each element of the stream and then
616−-- flatten the results into a single stream.
617−--
618−-- @
619−-- concatMap f = 'concat . map f'
620−-- concatMap = 'concatMapWith' 'Serial.serial'
621−-- concatMap f = 'concatMapM' (return . f)
622−-- concatMap f = 'concatUnfold' (UF.lmap f UF.fromStream)
623−-- @
624−--
625−-- @since 0.6.0
626−{-# INLINE concatMap #-}
627−concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
628−concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
629−
630−-- | Flatten a stream of streams to a single stream.
631−--
632−-- @
633−-- concat = concatMap id
634−-- @
635−--
636−-- /Pre-release/
637−{-# INLINE concat #-}
638−concat :: (IsStream t, Monad m) => t m (t m a) -> t m a
639−concat = concatMap id
640−
641−-- Keep concating either streams as long as rights are generated, stop as soon
642−-- as a left is generated and concat the left stream.
643−--
644−-- See also: 'handle'
645−--
646−-- /Unimplemented/
647−--
648−{-
649−concatMapEitherWith
650− :: -- (IsStream t, MonadAsync m) =>
651− (forall x. t m x -> t m x -> t m x)
652− -> (a -> t m (Either (t m b) b))
653− -> t m a
654− -> t m b
655−concatMapEitherWith = undefined
656−-}
657−
658−-- XXX Implement a StreamD version for fusion.
659−--
660−-- | Combine streams in pairs using a binary stream combinator, then combine
661−-- the resulting streams in pairs recursively until we get to a single combined
662−-- stream.
663−--
664−-- For example, you can sort a stream using merge sort like this:
665−--
666−-- >>> Stream.toList $ Stream.concatPairsWith (Stream.mergeBy compare) Stream.yield $ Stream.fromList [5,1,7,9,2]
667−-- [1,2,5,7,9]
668−--
669−-- /Caution: the stream of streams must be finite/
670−--
671−-- /Pre-release/
672−--
673−{-# INLINE concatPairsWith #-}
674−concatPairsWith :: IsStream t =>
675− (t m b -> t m b -> t m b)
676− -> (a -> t m b)
677− -> t m a
678− -> t m b
679−concatPairsWith = K.concatPairsWith
680−
681−------------------------------------------------------------------------------
682−-- Combine N Streams - concatUnfold
683−------------------------------------------------------------------------------
684−
685−-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
686−-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
687−-- therefore provide many times better performance.
688−--
689−-- @since 0.7.0
690−{-# INLINE concatUnfold #-}
691−concatUnfold ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
692−concatUnfold u m = fromStreamD $ D.concatUnfold u (toStreamD m)
693−
694−-- | Like 'concatUnfold' but interleaves the streams in the same way as
695−-- 'interleave' behaves instead of appending them.
696−--
697−-- /Pre-release/
698−{-# INLINE concatUnfoldInterleave #-}
699−concatUnfoldInterleave ::(IsStream t, Monad m)
700− => Unfold m a b -> t m a -> t m b
701−concatUnfoldInterleave u m =
702− fromStreamD $ D.concatUnfoldInterleave u (toStreamD m)
703−
704−-- | Like 'concatUnfold' but executes the streams in the same way as
705−-- 'roundrobin'.
706−--
707−-- /Pre-release/
708−{-# INLINE concatUnfoldRoundrobin #-}
709−concatUnfoldRoundrobin ::(IsStream t, Monad m)
710− => Unfold m a b -> t m a -> t m b
711−concatUnfoldRoundrobin u m =
712− fromStreamD $ D.concatUnfoldRoundrobin u (toStreamD m)
713−
714−------------------------------------------------------------------------------
715−-- Combine N Streams - interpose
716−------------------------------------------------------------------------------
717−
718−-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
719−--
720−-- | Unfold the elements of a stream, intersperse the given element between the
721−-- unfolded streams and then concat them into a single stream.
722−--
723−-- > unwords = S.interpose ' '
724−--
725−-- /Pre-release/
726−{-# INLINE interpose #-}
727−interpose :: (IsStream t, Monad m)
728− => c -> Unfold m b c -> t m b -> t m c
729−interpose x unf str =
730− D.fromStreamD $ D.interpose (return x) unf (D.toStreamD str)
731−
732−-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
733−--
734−-- | Unfold the elements of a stream, append the given element after each
735−-- unfolded stream and then concat them into a single stream.
736−--
737−-- > unlines = S.interposeSuffix '\n'
738−--
739−-- /Pre-release/
740−{-# INLINE interposeSuffix #-}
741−interposeSuffix :: (IsStream t, Monad m)
742− => c -> Unfold m b c -> t m b -> t m c
743−interposeSuffix x unf str =
744− D.fromStreamD $ D.interposeSuffix (return x) unf (D.toStreamD str)
745−
746200------------------------------------------------------------------------------
747−-- Combine N Streams - intercalate
748−------------------------------------------------------------------------------
749−
750−-- XXX we can swap the order of arguments to gintercalate so that the
751−-- definition of concatUnfold becomes simpler? The first stream should be
752−-- infixed inside the second one. However, if we change the order in
753−-- "interleave" as well similarly, then that will make it a bit unintuitive.
754−--
755−-- > concatUnfold unf str =
756−-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
757−--
758−-- | 'interleaveInfix' followed by unfold and concat.
759−--
760−-- /Pre-release/
761−{-# INLINE gintercalate #-}
762−gintercalate
763− :: (IsStream t, Monad m)
764− => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
765−gintercalate unf1 str1 unf2 str2 =
766− D.fromStreamD $ D.gintercalate
767− unf1 (D.toStreamD str1)
768− unf2 (D.toStreamD str2)
769−
770−-- XXX The order of arguments in "intercalate" is consistent with the list
771−-- intercalate but inconsistent with gintercalate and other stream interleaving
772−-- combinators. We can change the order of the arguments in other combinators
773−-- but then 'interleave' combinator may become a bit unintuitive because we
774−-- will be starting with the second stream.
775−
776−-- > intercalate seed unf str = gintercalate unf str unf (repeatM seed)
777−-- > intercalate a unf str = concatUnfold unf $ intersperse a str
778−--
779−-- | 'intersperse' followed by unfold and concat.
780−--
781−-- > unwords = intercalate " " Unfold.fromList
782−--
783−-- >>> Stream.toList $ Stream.intercalate " " Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
784−-- "abc def ghi"
785−--
786−-- /Pre-release/
787−{-# INLINE intercalate #-}
788−intercalate :: (IsStream t, Monad m)
789− => b -> Unfold m b c -> t m b -> t m c
790−intercalate seed unf str = D.fromStreamD $
791− D.concatUnfold unf $ D.intersperse seed (toStreamD str)
792−
793−-- | 'interleaveSuffix' followed by unfold and concat.
794−--
795−-- /Pre-release/
796−{-# INLINE gintercalateSuffix #-}
797−gintercalateSuffix
798− :: (IsStream t, Monad m)
799− => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
800−gintercalateSuffix unf1 str1 unf2 str2 =
801− D.fromStreamD $ D.gintercalateSuffix
802− unf1 (D.toStreamD str1)
803− unf2 (D.toStreamD str2)
804−
805−-- > intercalateSuffix seed unf str = gintercalateSuffix unf str unf (repeatM seed)
806−-- > intercalateSuffix a unf str = concatUnfold unf $ intersperseSuffix a str
807−--
808−-- | 'intersperseSuffix' followed by unfold and concat.
809−--
810−-- > unlines = intercalateSuffix "\n" Unfold.fromList
811−--
812−-- >>> Stream.toList $ Stream.intercalateSuffix "\n" Unfold.fromList $ Stream.fromList ["abc", "def", "ghi"]
813−-- "abc\ndef\nghi\n"
814−--
815−-- /Pre-release/
816−{-# INLINE intercalateSuffix #-}
817−intercalateSuffix :: (IsStream t, Monad m)
818− => b -> Unfold m b c -> t m b -> t m c
819−intercalateSuffix seed unf str = fromStreamD $ D.concatUnfold unf
820− $ D.intersperseSuffix (return seed) (D.toStreamD str)
821−
822−------------------------------------------------------------------------------
823−-- IterateMap - Map and flatten Trees of Streams
201+-- Trimming
824202------------------------------------------------------------------------------
825203
826−-- | Like 'iterateM' but iterates after mapping a stream generator on the
827−-- output.
828−--
829−-- Yield an input element in the output stream, map a stream generator on it
830−-- and then do the same on the resulting stream. This can be used for a depth
831−-- first traversal of a tree like structure.
832−--
833−-- Note that 'iterateM' is a special case of 'iterateMapWith':
834−--
835−-- @
836−-- iterateM f = iterateMapWith serial (yieldM . f) . yieldM
837−-- @
838−--
839−-- It can be used to traverse a tree structure. For example, to list a
840−-- directory tree:
841−--
842−-- @
843−-- Stream.iterateMapWith Stream.serial
844−-- (either Dir.toEither (const nil))
845−-- (yield (Left "tmp"))
846−-- @
204+-- | Drop prefix from the input stream if present.
847205--
848−-- /Pre-release/
206+-- Space: @O(1)@
849207--
850−{-# INLINE iterateMapWith #-}
851−iterateMapWith
852− :: IsStream t
853− => (t m a -> t m a -> t m a)
854− -> (a -> t m a)
855− -> t m a
856− -> t m a
857−iterateMapWith combine f = concatMapWith combine go
858− where
859− go x = yield x `combine` concatMapWith combine go (f x)
860−
861−{-
862−{-# INLINE iterateUnfold #-}
863−iterateUnfold :: (IsStream t, MonadAsync m)
864− => Unfold m a a -> t m a -> t m a
865−iterateUnfold unf xs = undefined
866−-}
867−
868−------------------------------------------------------------------------------
869−-- Flattening Graphs
870−------------------------------------------------------------------------------
871−
872−-- To traverse graphs we need a state to be carried around in the traversal.
873−-- For example, we can use a hashmap to store the visited status of nodes.
208+-- /Unimplemented/ - Help wanted.
209+{-# INLINE dropPrefix #-}
210+dropPrefix ::
211+ -- (Eq a, IsStream t, Monad m) =>
212+ t m a -> t m a -> t m a
213+dropPrefix = error "Not implemented yet!"
874214
875−-- | Like 'iterateMap' but carries a state in the stream generation function.
876−-- This can be used to traverse graph like structures, we can remember the
877−-- visited nodes in the state to avoid cycles.
878−--
879−-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
880−-- traverse graphs. However, this function provides a more localized state
881−-- instead of using a global state.
882−--
883−-- See also: 'mfix'
215+-- | Drop all matching infix from the input stream if present. Infix stream
216+-- may be consumed multiple times.
884217--
885−-- /Pre-release/
218+-- Space: @O(n)@ where n is the length of the infix.
886219--
887−{-# INLINE iterateSmapMWith #-}
888−iterateSmapMWith
889− :: (IsStream t, Monad m)
890− => (t m a -> t m a -> t m a)
891− -> (b -> a -> m (b, t m a))
892− -> m b
893− -> t m a
894− -> t m a
895−iterateSmapMWith combine f initial stream =
896− concatMap (\b -> concatMapWith combine (go b) stream) (yieldM initial)
897−
898− where
899−
900− go b a = yield a `combine` feedback b a
901−
902− feedback b a =
903− concatMap
904− (\(b1, s) -> concatMapWith combine (go b1) s)
905− (yieldM $ f b a)
906−
907−------------------------------------------------------------------------------
908−-- iterateMap - Either streams
909−------------------------------------------------------------------------------
220+-- /Unimplemented/ - Help wanted.
221+{-# INLINE dropInfix #-}
222+dropInfix ::
223+ -- (Eq a, IsStream t, Monad m) =>
224+ t m a -> t m a -> t m a
225+dropInfix = error "Not implemented yet!"
910226
911−-- | In an 'Either' stream iterate on 'Left's. This is a special case of
912−-- 'iterateMapWith':
913−--
914−-- @
915−-- iterateMapLeftsWith combine f = iterateMapWith combine (either f (const nil))
916−-- @
917−--
918−-- To traverse a directory tree:
919−--
920−-- @
921−-- iterateMapLeftsWith serial Dir.toEither (yield (Left "tmp"))
922−-- @
227+-- | Drop suffix from the input stream if present. Suffix stream may be
228+-- consumed multiple times.
923229--
924−-- /Pre-release/
230+-- Space: @O(n)@ where n is the length of the suffix.
925231--
926−{-# INLINE iterateMapLeftsWith #-}
927−iterateMapLeftsWith
928− :: IsStream t
929− => (t m (Either a b) -> t m (Either a b) -> t m (Either a b))
930− -> (a -> t m (Either a b))
931− -> t m (Either a b)
932− -> t m (Either a b)
933−iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))
232+-- /Unimplemented/ - Help wanted.
233+{-# INLINE dropSuffix #-}
234+dropSuffix ::
235+ -- (Eq a, IsStream t, Monad m) =>
236+ t m a -> t m a -> t m a
237+dropSuffix = error "Not implemented yet!"
934238
935239------------------------------------------------------------------------------
936240-- Folding
@@ -1085,6 +389,8 @@ parseSequence
1085389 -> t m b
1086390parseSequence _f _m = undefined
1087391
392+-- XXX Change the parser arguments' order
393+--
1088394-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
1089395-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
1090396-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
@@ -1125,45 +431,6 @@ parseIterate
1125431parseIterate f i m = D.fromStreamD $
1126432 D.parseIterate (PRK.fromParserK . f) i (D.toStreamD m)
1127433
1128−------------------------------------------------------------------------------
1129−-- Trimming
1130−------------------------------------------------------------------------------
1131−
1132−-- | Drop prefix from the input stream if present.
1133−--
1134−-- Space: @O(1)@
1135−--
1136−-- /Unimplemented/ - Help wanted.
1137−{-# INLINE dropPrefix #-}
1138−dropPrefix ::
1139− -- (Eq a, IsStream t, Monad m) =>
1140− t m a -> t m a -> t m a
1141−dropPrefix = error "Not implemented yet!"
1142−
1143−-- | Drop all matching infix from the input stream if present. Infix stream
1144−-- may be consumed multiple times.
1145−--
1146−-- Space: @O(n)@ where n is the length of the infix.
1147−--
1148−-- /Unimplemented/ - Help wanted.
1149−{-# INLINE dropInfix #-}
1150−dropInfix ::
1151− -- (Eq a, IsStream t, Monad m) =>
1152− t m a -> t m a -> t m a
1153−dropInfix = error "Not implemented yet!"
1154−
1155−-- | Drop suffix from the input stream if present. Suffix stream may be
1156−-- consumed multiple times.
1157−--
1158−-- Space: @O(n)@ where n is the length of the suffix.
1159−--
1160−-- /Unimplemented/ - Help wanted.
1161−{-# INLINE dropSuffix #-}
1162−dropSuffix ::
1163− -- (Eq a, IsStream t, Monad m) =>
1164− t m a -> t m a -> t m a
1165−dropSuffix = error "Not implemented yet!"
1166−
1167434------------------------------------------------------------------------------
1168435-- Generalized grouping
1169436------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+2−1
@@ -72,7 +72,8 @@ import qualified Streamly.Internal.Data.Fold as Fold
7272import qualified Streamly.Internal.Data.Stream.IsStream.Lift as Stream
7373import qualified Streamly.Internal.Data.Stream.IsStream.Eliminate as Stream
7474import qualified Streamly.Internal.Data.Stream.IsStream.Generate as Stream
75−import qualified Streamly.Internal.Data.Stream.IsStream.Nesting as Stream
75+import qualified Streamly.Internal.Data.Stream.IsStream.Expand as Stream
76+import qualified Streamly.Internal.Data.Stream.IsStream.Reduce as Stream
7677import qualified Streamly.Internal.Data.Stream.IsStream.Transform as Stream
7778import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
7879
src/Streamly/Internal/Data/Stream/Prelude.hs+3
@@ -300,6 +300,7 @@ foldbWith f = K.foldb f K.nil
300300-- Equivalent to:
301301--
302302-- @
303+-- concatFoldableWith f = Prelude.foldr f S.nil
303304-- concatFoldableWith f = S.concatMapFoldableWith f id
304305-- @
305306--
@@ -320,6 +321,7 @@ concatFoldableWith f = Prelude.foldr f K.nil
320321-- Equivalent to:
321322--
322323-- @
324+-- concatMapFoldableWith f g = Prelude.foldr (f . g) S.nil
323325-- concatMapFoldableWith f g xs = S.concatMapWith f g (S.fromFoldable xs)
324326-- @
325327--
@@ -337,6 +339,7 @@ concatMapFoldableWith f g = Prelude.foldr (f . g) K.nil
337339-- Equivalent to:
338340--
339341-- @
342+-- concatForFoldableWith f xs g = Prelude.foldr (f . g) S.nil xs
340343-- concatForFoldableWith = flip S.concatMapFoldableWith
341344-- @
342345--
streamly.cabal+2−1
@@ -502,7 +502,8 @@ library
502502 , Streamly.Internal.Data.Stream.IsStream.Generate
503503 , Streamly.Internal.Data.Stream.IsStream.Eliminate
504504 , Streamly.Internal.Data.Stream.IsStream.Transform
505− , Streamly.Internal.Data.Stream.IsStream.Nesting
505+ , Streamly.Internal.Data.Stream.IsStream.Expand
506+ , Streamly.Internal.Data.Stream.IsStream.Reduce
506507 , Streamly.Internal.Data.Stream.IsStream.Exception
507508 , Streamly.Internal.Data.Stream.IsStream.Lift
508509 , Streamly.Internal.Data.Stream.IsStream.Top