on master help sign in
Split IsStream.Nesting into Expand and Reduce
8be2c1cHarendra Kumar
src/Streamly/Internal/Data/Stream/IsStream.hs+4−2
@@ -15,7 +15,8 @@ module Streamly.Internal.Data.Stream.IsStream
15 , module Streamly.Internal.Data.Stream.IsStream.Generate
15 , module Streamly.Internal.Data.Stream.IsStream.Generate
16 , module Streamly.Internal.Data.Stream.IsStream.Eliminate
16 , module Streamly.Internal.Data.Stream.IsStream.Eliminate
17 , module Streamly.Internal.Data.Stream.IsStream.Transform
17 , 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
19 , module Streamly.Internal.Data.Stream.IsStream.Exception
20 , module Streamly.Internal.Data.Stream.IsStream.Exception
20 , module Streamly.Internal.Data.Stream.IsStream.Lift
21 , module Streamly.Internal.Data.Stream.IsStream.Lift
21 , module Streamly.Internal.Data.Stream.IsStream.Top
22 , module Streamly.Internal.Data.Stream.IsStream.Top
@@ -27,6 +28,7 @@ import Streamly.Internal.Data.Stream.IsStream.Eliminate
27import Streamly.Internal.Data.Stream.IsStream.Exception
28import Streamly.Internal.Data.Stream.IsStream.Exception
28import Streamly.Internal.Data.Stream.IsStream.Generate
29import Streamly.Internal.Data.Stream.IsStream.Generate
29import Streamly.Internal.Data.Stream.IsStream.Lift
30import 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
31import Streamly.Internal.Data.Stream.IsStream.Transform
33import Streamly.Internal.Data.Stream.IsStream.Transform
32import Streamly.Internal.Data.Stream.IsStream.Types
34import 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
42 -- * Nesting
42 -- * Nesting
43 , concatM
43 , concatM
44 , concatMapM
44 , concatMapM
45+ , concatMap
45 , splitOnSeq
46 , splitOnSeq
46 )
47 )
47where
48where
@@ -72,7 +73,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK as S
72import qualified Streamly.Internal.Data.Stream.StreamD as S
73import qualified Streamly.Internal.Data.Stream.StreamD as S
73#endif
74#endif
74
75
75−import Prelude hiding (take, takeWhile, drop, reverse)
76+import Prelude hiding (take, takeWhile, drop, reverse, concatMap)
76
77
77--
78--
78-- $setup
79-- $setup
@@ -452,6 +453,21 @@ reverse' s = fromStreamD $ D.reverse' $ toStreamD s
452concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
453concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
453concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
454concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
454
455
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+
455-- | Given a stream value in the underlying monad, lift and join the underlying
471-- | Given a stream value in the underlying monad, lift and join the underlying
456-- monad with the stream monad.
472-- monad with the stream monad.
457--
473--
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 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Stream.IsStream.Nesting
2+-- Module : Streamly.Internal.Data.Stream.IsStream.Reduce
3-- Copyright : (c) 2017 Composewell Technologies
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
6-- Stability : experimental
7-- Portability : GHC
7-- Portability : GHC
8--
8--
9−-- Transformations involving multiple streams, streams and unfolds or streams
9+-- Reduce streams by streams, folds or parsers.
10−-- and folds.
11
10
12−module Streamly.Internal.Data.Stream.IsStream.Nesting
11+module Streamly.Internal.Data.Stream.IsStream.Reduce
13 (
12 (
14− -- * Generate
15− -- | Combining streams to generate streams.
16−
17− -- ** Combine Two Streams
13+ -- * Reduce By 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
14+ dropPrefix
62 , dropInfix
15 , dropInfix
63 , dropSuffix
16 , dropSuffix
64
17
65− -- ** Combine N Streams
18+ -- * Reduce By Folds
66− -- | Functions generally ending in these shapes:
19+ -- |
67− --
20+ -- Reduce a stream by folding or parsing chunks of the stream. Functions
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:
21+ -- generally ending in these shapes:
122 --
22 --
123 -- @
23 -- @
124 -- f (Fold m a b) -> t m a -> t m b
24 -- f (Fold m a b) -> t m a -> t m b
125 -- f (Parser m a b) -> t m a -> t m b
25 -- f (Parser m a b) -> t m a -> t m b
126 -- @
26 -- @
127
27
128− -- ** Folding
28+ -- ** Generic Folding
129 -- | Apply folds on a stream.
29 -- | Apply folds on a stream.
130 , foldMany
30 , foldMany
131 , foldManyPost
31 , foldManyPost
132 , foldSequence
32 , foldSequence
133 , foldIterateM
33 , foldIterateM
134
34
135− -- ** Parsing
136− -- | Apply parsers on a stream.
137− , parseMany
138− , parseManyD
139− , parseManyTill
140− , parseSequence
141− , parseIterate
142−
143 -- ** Chunking
35 -- ** Chunking
144 -- | Element unaware grouping.
36 -- | Element unaware grouping.
145 , chunksOf
37 , chunksOf
146− , chunksOf2
147 , arraysOf
38 , arraysOf
148 , intervalsOf
39 , intervalsOf
149
40
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−
169 -- ** Splitting
41 -- ** Splitting
170 -- | Streams can be sliced into segments in space or in time. We use the
42 -- | Streams can be sliced into segments in space or in time. We use the
171 -- term @chunk@ to refer to a spatial length of the stream (spatial window)
43 -- term @chunk@ to refer to a spatial length of the stream (spatial window)
@@ -179,7 +51,6 @@ module Streamly.Internal.Data.Stream.IsStream.Nesting
179 -- , splitBy
51 -- , splitBy
180 , splitWithSuffix
52 , splitWithSuffix
181 -- , splitByPrefix
53 -- , splitByPrefix
182− , wordsBy -- stripAndCompactBy
183
54
184 -- -- *** Splitting By Sequences
55 -- -- *** Splitting By Sequences
185 , splitBySeq
56 , splitBySeq
@@ -235,10 +106,41 @@ module Streamly.Internal.Data.Stream.IsStream.Nesting
235 -- , slidingSessionBuffer
106 -- , slidingSessionBuffer
236 -}
107 -}
237
108
238− -- * Transform (Nested Containers)
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+
239− -- Nested splitting
138+ -- * Nested splitting
240 , splitInnerBy
139 , splitInnerBy
241 , splitInnerBySuffix
140 , splitInnerBySuffix
141+
142+ -- * Fold2
143+ , chunksOf2
242 )
144 )
243where
145where
244
146
@@ -254,30 +156,18 @@ import Data.Maybe (isNothing)
254import Foreign.Storable (Storable)
156import Foreign.Storable (Storable)
255import Streamly.Internal.Data.Fold.Type (Fold (..), Fold2 (..))
157import Streamly.Internal.Data.Fold.Type (Fold (..), Fold2 (..))
256import Streamly.Internal.Data.Parser (Parser (..))
158import Streamly.Internal.Data.Parser (Parser (..))
257−import Streamly.Internal.Data.Unfold.Type (Unfold)
258import Streamly.Internal.Data.Array.Foreign.Type (Array)
159import Streamly.Internal.Data.Array.Foreign.Type (Array)
259import Streamly.Internal.Data.SVar (MonadAsync)
160import Streamly.Internal.Data.SVar (MonadAsync)
260−import Streamly.Internal.Data.Stream.Ahead (ahead)
261−import Streamly.Internal.Data.Stream.Async (async, wAsync)
262import Streamly.Internal.Data.Stream.IsStream.Common
161import Streamly.Internal.Data.Stream.IsStream.Common
263− ( concatM
162+ ( concatMap
264− , concatMapM
265 , fold
163 , fold
266 , interjectSuffix
164 , interjectSuffix
267 , intersperseM
165 , intersperseM
268 , repeatM
166 , repeatM
269 , scanlMAfter'
167 , scanlMAfter'
270− , smapM
271 , splitOnSeq
168 , splitOnSeq
272− , yield
169+ , 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)
279import Streamly.Internal.Data.Stream.StreamK (IsStream)
170import Streamly.Internal.Data.Stream.StreamK (IsStream)
280−import Streamly.Internal.Data.Stream.Serial (serial, wSerial)
281import Streamly.Internal.Data.Time.Units
171import Streamly.Internal.Data.Time.Units
282 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
172 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
283 , toAbsTime)
173 , toAbsTime)
@@ -293,14 +183,8 @@ import qualified Streamly.Internal.Data.Stream.Parallel as Par
293import qualified Streamly.Internal.Data.Stream.Serial as Serial
183import qualified Streamly.Internal.Data.Stream.Serial as Serial
294import qualified Streamly.Internal.Data.Stream.StreamD as D
184import qualified Streamly.Internal.Data.Stream.StreamD as D
295import qualified Streamly.Internal.Data.Stream.StreamK as K
185import 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
302
186
303−import Prelude hiding (zipWith, concatMap, concat)
187+import Prelude hiding (concatMap)
304
188
305-- $setup
189-- $setup
306-- >>> :m
190-- >>> :m
@@ -313,624 +197,44 @@ import Prelude hiding (zipWith, concatMap, concat)
313-- >>> import qualified Streamly.Internal.Data.Parser as Parser
197-- >>> import qualified Streamly.Internal.Data.Parser as Parser
314-- >>> import qualified Streamly.Data.Array.Foreign as Array
198-- >>> import qualified Streamly.Data.Array.Foreign as Array
315
199
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−
746------------------------------------------------------------------------------
200------------------------------------------------------------------------------
747−-- Combine N Streams - intercalate
201+-- Trimming
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
824------------------------------------------------------------------------------
202------------------------------------------------------------------------------
825
203
826−-- | Like 'iterateM' but iterates after mapping a stream generator on the
204+-- | Drop prefix from the input stream if present.
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−-- @
847--
205--
848−-- /Pre-release/
206+-- Space: @O(1)@
849--
207--
850−{-# INLINE iterateMapWith #-}
208+-- /Unimplemented/ - Help wanted.
851−iterateMapWith
209+{-# INLINE dropPrefix #-}
852− :: IsStream t
210+dropPrefix ::
853− => (t m a -> t m a -> t m a)
211+ -- (Eq a, IsStream t, Monad m) =>
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
212+ t m a -> t m a -> t m a
865−iterateUnfold unf xs = undefined
213+dropPrefix = error "Not implemented yet!"
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.
874
214
875−-- | Like 'iterateMap' but carries a state in the stream generation function.
215+-- | Drop all matching infix from the input stream if present. Infix stream
876−-- This can be used to traverse graph like structures, we can remember the
216+-- may be consumed multiple times.
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'
884--
217--
885−-- /Pre-release/
218+-- Space: @O(n)@ where n is the length of the infix.
886--
219--
887−{-# INLINE iterateSmapMWith #-}
220+-- /Unimplemented/ - Help wanted.
888−iterateSmapMWith
221+{-# INLINE dropInfix #-}
222+dropInfix ::
889− :: (IsStream t, Monad m)
223+ -- (Eq a, 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
224+ t m a -> t m a -> t m a
894− -> t m a
225+dropInfix = error "Not implemented yet!"
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−------------------------------------------------------------------------------
910
226
911−-- | In an 'Either' stream iterate on 'Left's. This is a special case of
227+-- | Drop suffix from the input stream if present. Suffix stream may be
912−-- 'iterateMapWith':
228+-- consumed multiple times.
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−-- @
923--
229--
924−-- /Pre-release/
230+-- Space: @O(n)@ where n is the length of the suffix.
925--
231--
926−{-# INLINE iterateMapLeftsWith #-}
232+-- /Unimplemented/ - Help wanted.
927−iterateMapLeftsWith
233+{-# INLINE dropSuffix #-}
234+dropSuffix ::
928− :: IsStream t
235+ -- (Eq a, IsStream t, Monad m) =>
929− => (t m (Either a b) -> t m (Either a b) -> t m (Either a b))
236+ t m a -> t m a -> t m a
930− -> (a -> t m (Either a b))
237+dropSuffix = error "Not implemented yet!"
931− -> t m (Either a b)
932− -> t m (Either a b)
933−iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))
934
238
935------------------------------------------------------------------------------
239------------------------------------------------------------------------------
936-- Folding
240-- Folding
@@ -1085,6 +389,8 @@ parseSequence
1085 -> t m b
389 -> t m b
1086parseSequence _f _m = undefined
390parseSequence _f _m = undefined
1087
391
392+-- XXX Change the parser arguments' order
393+--
1088-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
394-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
1089-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
395-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
1090-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
396-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
@@ -1125,45 +431,6 @@ parseIterate
1125parseIterate f i m = D.fromStreamD $
431parseIterate f i m = D.fromStreamD $
1126 D.parseIterate (PRK.fromParserK . f) i (D.toStreamD m)
432 D.parseIterate (PRK.fromParserK . f) i (D.toStreamD m)
1127
433
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−
1167------------------------------------------------------------------------------
434------------------------------------------------------------------------------
1168-- Generalized grouping
435-- Generalized grouping
1169------------------------------------------------------------------------------
436------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+2−1
@@ -72,7 +72,8 @@ import qualified Streamly.Internal.Data.Fold as Fold
72import qualified Streamly.Internal.Data.Stream.IsStream.Lift as Stream
72import qualified Streamly.Internal.Data.Stream.IsStream.Lift as Stream
73import qualified Streamly.Internal.Data.Stream.IsStream.Eliminate as Stream
73import qualified Streamly.Internal.Data.Stream.IsStream.Eliminate as Stream
74import qualified Streamly.Internal.Data.Stream.IsStream.Generate as Stream
74import 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
76import qualified Streamly.Internal.Data.Stream.IsStream.Transform as Stream
77import qualified Streamly.Internal.Data.Stream.IsStream.Transform as Stream
77import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
78import qualified Streamly.Internal.Data.Stream.StreamK as StreamK
78
79
src/Streamly/Internal/Data/Stream/Prelude.hs+3
@@ -300,6 +300,7 @@ foldbWith f = K.foldb f K.nil
300-- Equivalent to:
300-- Equivalent to:
301--
301--
302-- @
302-- @
303+-- concatFoldableWith f = Prelude.foldr f S.nil
303-- concatFoldableWith f = S.concatMapFoldableWith f id
304-- concatFoldableWith f = S.concatMapFoldableWith f id
304-- @
305-- @
305--
306--
@@ -320,6 +321,7 @@ concatFoldableWith f = Prelude.foldr f K.nil
320-- Equivalent to:
321-- Equivalent to:
321--
322--
322-- @
323-- @
324+-- concatMapFoldableWith f g = Prelude.foldr (f . g) S.nil
323-- concatMapFoldableWith f g xs = S.concatMapWith f g (S.fromFoldable xs)
325-- concatMapFoldableWith f g xs = S.concatMapWith f g (S.fromFoldable xs)
324-- @
326-- @
325--
327--
@@ -337,6 +339,7 @@ concatMapFoldableWith f g = Prelude.foldr (f . g) K.nil
337-- Equivalent to:
339-- Equivalent to:
338--
340--
339-- @
341-- @
342+-- concatForFoldableWith f xs g = Prelude.foldr (f . g) S.nil xs
340-- concatForFoldableWith = flip S.concatMapFoldableWith
343-- concatForFoldableWith = flip S.concatMapFoldableWith
341-- @
344-- @
342--
345--
streamly.cabal+2−1
@@ -502,7 +502,8 @@ library
502 , Streamly.Internal.Data.Stream.IsStream.Generate
502 , Streamly.Internal.Data.Stream.IsStream.Generate
503 , Streamly.Internal.Data.Stream.IsStream.Eliminate
503 , Streamly.Internal.Data.Stream.IsStream.Eliminate
504 , Streamly.Internal.Data.Stream.IsStream.Transform
504 , 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
506 , Streamly.Internal.Data.Stream.IsStream.Exception
507 , Streamly.Internal.Data.Stream.IsStream.Exception
507 , Streamly.Internal.Data.Stream.IsStream.Lift
508 , Streamly.Internal.Data.Stream.IsStream.Lift
508 , Streamly.Internal.Data.Stream.IsStream.Top
509 , Streamly.Internal.Data.Stream.IsStream.Top