on master help sign in
Merge pull request #1644 from composewell/NewStream_more_functions

New Stream more functions

commit4912b1d923af6aaa2ddd3ee9c43eea90771af660 authorHarendra Kumar <harendra@composewell.com> dateThu 23 Jun 2022, 18:45 (4 years ago) parent24f34476cfe06b
16 files changed+3174−186
core/src/Streamly/Internal/Data/Stream.hs+8 core/src/Streamly/Internal/Data/Stream/Bottom.hs+410 core/src/Streamly/Internal/Data/Stream/Common.hs+1−49 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+343−25 core/src/Streamly/Internal/Data/Stream/Exception.hs+262 core/src/Streamly/Internal/Data/Stream/Expand.hs+581 core/src/Streamly/Internal/Data/Stream/Generate.hs+94−63 core/src/Streamly/Internal/Data/Stream/Lift.hs+136 core/src/Streamly/Internal/Data/Stream/Reduce.hs+318 core/src/Streamly/Internal/Data/Stream/Serial.hs+4−2 core/src/Streamly/Internal/Data/Stream/Transform.hs+831−28 core/src/Streamly/Internal/Data/Stream/Type.hs+166−11 core/src/Streamly/Internal/Data/Stream/Zip.hs−7 core/src/Streamly/Internal/Data/Unfold/Type.hs+4 core/streamly-core.cabal+5 streamly.cabal+11−1
core/src/Streamly/Internal/Data/Stream.hs+8
@@ -9,12 +9,20 @@
99module Streamly.Internal.Data.Stream
1010 ( module Streamly.Internal.Data.Stream.Type
1111 , module Streamly.Internal.Data.Stream.Eliminate
12+ , module Streamly.Internal.Data.Stream.Exception
13+ , module Streamly.Internal.Data.Stream.Expand
1214 , module Streamly.Internal.Data.Stream.Generate
15+ , module Streamly.Internal.Data.Stream.Lift
16+ , module Streamly.Internal.Data.Stream.Reduce
1317 , module Streamly.Internal.Data.Stream.Transform
1418 )
1519where
1620
1721import Streamly.Internal.Data.Stream.Eliminate
22+import Streamly.Internal.Data.Stream.Exception
23+import Streamly.Internal.Data.Stream.Expand
1824import Streamly.Internal.Data.Stream.Generate
25+import Streamly.Internal.Data.Stream.Lift
26+import Streamly.Internal.Data.Stream.Reduce
1927import Streamly.Internal.Data.Stream.Transform
2028import Streamly.Internal.Data.Stream.Type
core/src/Streamly/Internal/Data/Stream/Bottom.hsnew file+410
@@ -0,0 +1,410 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Bottom
3+-- Copyright : (c) 2017 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+-- Bottom level Stream module that can be used by all other upper level
10+-- Stream modules.
11+
12+module Streamly.Internal.Data.Stream.Bottom
13+ (
14+ -- * Generation
15+ fromPure
16+ , fromEffect
17+
18+ -- * Folds
19+ , fold
20+ , foldContinue
21+ , foldBreak
22+
23+ -- * Scans
24+ , smapM
25+ -- $smapM_Notes
26+ , postscan
27+ , take
28+ , takeWhile
29+ , takeEndBy
30+ , drop
31+ , findIndices
32+
33+ -- * Merge
34+ , intersperseM
35+
36+ -- * Fold and Unfold
37+ , reverse
38+ , reverse'
39+
40+ -- * Expand
41+ , concatM
42+ , concatMapM
43+ , concatMap
44+
45+ -- * Reduce
46+ , foldManyPost
47+
48+ -- * Zipping
49+ , zipWithM
50+ , zipWith
51+ )
52+where
53+
54+#include "inline.hs"
55+
56+import Control.Monad.IO.Class (MonadIO(..))
57+import Streamly.Internal.Data.Fold.Type (Fold (..))
58+import Streamly.Internal.Data.Unboxed (Storable)
59+import Streamly.Internal.System.IO (defaultChunkSize)
60+
61+import qualified Streamly.Internal.Data.Array.Foreign.Type as A
62+import qualified Streamly.Internal.Data.Fold as Fold
63+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
64+import qualified Streamly.Internal.Data.Stream.StreamK as K
65+import qualified Streamly.Internal.Data.Stream.StreamD as D
66+
67+import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
68+
69+import Streamly.Internal.Data.Stream.Type
70+
71+--
72+-- $setup
73+-- >>> :m
74+-- >>> import Control.Monad (join)
75+-- >>> import Control.Monad.Trans.Class (lift)
76+-- >>> import Data.Function (fix, (&))
77+-- >>> import Data.Semigroup (cycle1)
78+-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
79+-- >>> import System.IO.Unsafe (unsafePerformIO)
80+-- >>> import Streamly.Internal.Data.Stream as Stream
81+-- >>> import qualified Streamly.Data.Array.Foreign as Array
82+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
83+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
84+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
85+
86+------------------------------------------------------------------------------
87+-- Elimination - Running a Fold
88+------------------------------------------------------------------------------
89+
90+-- | We can create higher order folds using 'foldContinue'. We can fold a
91+-- number of streams to a given fold efficiently with full stream fusion. For
92+-- example, to fold a list of streams on the same sum fold:
93+--
94+-- >>> concatFold = Prelude.foldl Stream.foldContinue Fold.sum
95+--
96+-- >>> fold f = Fold.finish . Stream.foldContinue f
97+--
98+-- /Internal/
99+{-# INLINE foldContinue #-}
100+foldContinue :: Monad m => Fold m a b -> Stream m a -> Fold m a b
101+foldContinue f s = D.foldContinue f $ toStreamD s
102+
103+-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
104+-- expression strictly at each step. The behavior is similar to 'foldl''. A
105+-- 'Fold' can terminate early without consuming the full stream. See the
106+-- documentation of individual 'Fold's for termination behavior.
107+--
108+-- >>> Stream.fold Fold.sum (Stream.unfold Unfold.enumerateFromTo (1, 100))
109+-- 5050
110+--
111+-- Folds never fail, therefore, they produce a default value even when no input
112+-- is provided. It means we can always fold an empty stream and get a valid
113+-- result. For example:
114+--
115+-- >>> Stream.fold Fold.sum Stream.nil
116+-- 0
117+--
118+-- However, 'foldMany' on an empty stream results in an empty stream.
119+-- Therefore, @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany
120+-- f@.
121+--
122+-- >>> fold f = Stream.parse (Parser.fromFold f)
123+--
124+{-# INLINE fold #-}
125+fold :: Monad m => Fold m a b -> Stream m a -> m b
126+fold fl strm = D.fold fl $ D.fromStreamK $ toStreamK strm
127+
128+-- | Like 'fold' but also returns the remaining stream.
129+--
130+-- /Inhibits stream fusion/
131+--
132+{-# INLINE foldBreak #-}
133+foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
134+{-
135+-- XXX This shows quadratic performance when used recursively perhaps because
136+-- of StreamK to StreamD conversions not getting eliminated sue to recursion.
137+foldBreak fl (Stream strm) = fmap f $ D.foldBreak fl $ D.fromStreamK strm
138+
139+ where
140+
141+ f (b, str) = (b, Stream (D.toStreamK str))
142+-}
143+foldBreak fl strm = fmap f $ K.foldBreak fl (toStreamK strm)
144+
145+ where
146+
147+ f (b, str) = (b, fromStreamK str)
148+
149+------------------------------------------------------------------------------
150+-- Transformation
151+------------------------------------------------------------------------------
152+
153+{-
154+-- |
155+-- >>> map = fmap
156+--
157+-- Same as 'fmap'.
158+--
159+-- >>> Stream.fold Fold.toList $ fmap (+1) $ Stream.unfold Unfold.fromList [1,2,3]
160+-- [2,3,4]
161+--
162+{-# INLINE map #-}
163+map :: Monad m => (a -> b) -> Stream m a -> Stream m b
164+map f = fromStreamD . D.map f . toStreamD
165+-}
166+
167+-- | Postscan a stream using the given monadic fold.
168+--
169+-- The following example extracts the input stream up to a point where the
170+-- running average of elements is no more than 10:
171+--
172+-- >>> import Data.Maybe (fromJust)
173+-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
174+-- >>> s = Stream.unfold Unfold.enumerateFromTo (1.0, 100.0)
175+-- >>> :{
176+-- Stream.fold Fold.toList
177+-- $ fmap (fromJust . fst)
178+-- $ Stream.takeWhile (\(_,x) -> x <= 10)
179+-- $ Stream.postscan (Fold.tee Fold.last avg) s
180+-- :}
181+-- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]
182+--
183+{-# INLINE postscan #-}
184+postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b
185+postscan fld = fromStreamD . D.postscanOnce fld . toStreamD
186+
187+-- $smapM_Notes
188+--
189+-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
190+-- a read-only environment. However, that would just be 'mapM'.
191+--
192+-- The initial action could be @m (s, Maybe b)@, and we can also add a final
193+-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
194+-- functionality and also to flush the state in the end like scanlMAfter'.
195+-- We can also use it along with a fusible version of bracket to get
196+-- scanlMAfter' like functionality. See issue #677.
197+--
198+-- This can be further generalized to a type similar to Fold/Parser, giving it
199+-- filtering and parsing capability as well (this is in fact equivalent to
200+-- parseMany):
201+--
202+-- smapM :: (s -> a -> m (Step s b)) -> m s -> Stream m a -> Stream m b
203+--
204+
205+-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
206+-- Hopefully, this is a better alternative to @scan@. Separation of state from
207+-- the output makes it easier to think in terms of a shared state, and also
208+-- makes it easier to keep the state fully strict and the output lazy.
209+--
210+-- See also: 'postscan'
211+--
212+-- /Pre-release/
213+--
214+{-# INLINE smapM #-}
215+smapM :: Monad m =>
216+ (s -> a -> m (s, b))
217+ -> m s
218+ -> Stream m a
219+ -> Stream m b
220+smapM step initial stream =
221+ -- XXX implement this directly instead of using postscan
222+ let f = Fold.foldlM'
223+ (\(s, _) a -> step s a)
224+ (fmap (,undefined) initial)
225+ in fmap snd $ postscan f stream
226+
227+------------------------------------------------------------------------------
228+-- Transformation - Trimming
229+------------------------------------------------------------------------------
230+
231+-- | Take first 'n' elements from the stream and discard the rest.
232+--
233+{-# INLINE take #-}
234+take :: Monad m => Int -> Stream m a -> Stream m a
235+take n m = fromStreamD $ D.take n $ toStreamD m
236+
237+-- | End the stream as soon as the predicate fails on an element.
238+--
239+{-# INLINE takeWhile #-}
240+takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
241+takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m
242+
243+{-# INLINE takeEndBy #-}
244+takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
245+takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
246+
247+-- | Discard first 'n' elements from the stream and take the rest.
248+--
249+{-# INLINE drop #-}
250+drop :: Monad m => Int -> Stream m a -> Stream m a
251+drop n m = fromStreamD $ D.drop n $ toStreamD m
252+
253+------------------------------------------------------------------------------
254+-- Searching
255+------------------------------------------------------------------------------
256+
257+-- | Find all the indices where the element in the stream satisfies the given
258+-- predicate.
259+--
260+-- >>> findIndices p = Stream.fold (Fold.findIndices p)
261+--
262+{-# INLINE findIndices #-}
263+findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
264+findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
265+
266+------------------------------------------------------------------------------
267+-- Transformation by Inserting
268+------------------------------------------------------------------------------
269+
270+-- intersperseM = intersperseBySpan 1
271+
272+-- | Insert an effect and its output before consuming an element of a stream
273+-- except the first one.
274+--
275+-- >>> input = Stream.unfold Unfold.fromList "hello"
276+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input
277+-- h.,e.,l.,l.,o"h,e,l,l,o"
278+--
279+-- Be careful about the order of effects. In the above example we used trace
280+-- after the intersperse, if we use it before the intersperse the output would
281+-- be he.l.l.o."h,e,l,l,o".
282+--
283+-- >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input
284+-- he.l.l.o."h,e,l,l,o"
285+--
286+{-# INLINE intersperseM #-}
287+intersperseM :: Monad m => m a -> Stream m a -> Stream m a
288+intersperseM m = fromStreamD . D.intersperseM m . toStreamD
289+
290+------------------------------------------------------------------------------
291+-- Transformation by Reordering
292+------------------------------------------------------------------------------
293+
294+-- XXX Use a compact region list to temporarily store the list, in both reverse
295+-- as well as in reverse'.
296+--
297+-- /Note:/ 'reverse'' is much faster than this, use that when performance
298+-- matters.
299+--
300+-- | Returns the elements of the stream in reverse order. The stream must be
301+-- finite. Note that this necessarily buffers the entire stream in memory.
302+--
303+-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
304+--
305+{-# INLINE reverse #-}
306+reverse :: Monad m => Stream m a -> Stream m a
307+reverse s = fromStreamD $ D.reverse $ toStreamD s
308+
309+-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
310+--
311+-- /Pre-release/
312+{-# INLINE reverse' #-}
313+reverse' :: (MonadIO m, Storable a) => Stream m a -> Stream m a
314+-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
315+reverse' =
316+ fromStreamD
317+ . A.flattenArraysRev -- unfoldMany A.readRev
318+ . D.fromStreamK
319+ . K.reverse
320+ . D.toStreamK
321+ . A.arraysOf defaultChunkSize
322+ . toStreamD
323+
324+------------------------------------------------------------------------------
325+-- Combine streams and flatten
326+------------------------------------------------------------------------------
327+
328+-- | Map a stream producing monadic function on each element of the stream
329+-- and then flatten the results into a single stream. Since the stream
330+-- generation function is monadic, unlike 'concatMap', it can produce an
331+-- effect at the beginning of each iteration of the inner loop.
332+--
333+{-# INLINE concatMapM #-}
334+concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
335+concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
336+
337+-- | Map a stream producing function on each element of the stream and then
338+-- flatten the results into a single stream.
339+--
340+-- >>> concatMap f = Stream.concatMapM (return . f)
341+-- >>> concatMap f = Stream.concatMapWith Stream.append f
342+-- >>> concatMap f = Stream.concat . fmap f
343+-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
344+--
345+{-# INLINE concatMap #-}
346+concatMap ::Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
347+concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
348+
349+-- | Given a stream value in the underlying monad, lift and join the underlying
350+-- monad with the stream monad.
351+--
352+-- >>> concatM = Stream.concat . Stream.fromEffect
353+-- >>> concatM = Stream.concat . lift -- requires (MonadTrans t)
354+-- >>> concatM = join . lift -- requires (MonadTrans t, Monad (Stream m))
355+--
356+-- See also: 'concat', 'sequence'
357+--
358+-- /Internal/
359+--
360+{-# INLINE concatM #-}
361+concatM :: Monad m => m (Stream m a) -> Stream m a
362+concatM generator = concatMapM (\() -> generator) (fromPure ())
363+
364+-- | Like 'foldMany' but appends empty fold output if the fold and stream
365+-- termination aligns:
366+--
367+-- >>> f = Fold.take 2 Fold.sum
368+-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList []
369+-- [0]
370+-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList [1..9]
371+-- [3,7,11,15,9]
372+-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList [1..10]
373+-- [3,7,11,15,19,0]
374+--
375+-- /Pre-release/
376+--
377+{-# INLINE foldManyPost #-}
378+foldManyPost
379+ :: Monad m
380+ => Fold m a b
381+ -> Stream m a
382+ -> Stream m b
383+foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
384+
385+------------------------------------------------------------------------------
386+-- Zipping
387+------------------------------------------------------------------------------
388+
389+-- | Like 'zipWith' but using a monadic zipping function.
390+--
391+{-# INLINE zipWithM #-}
392+zipWithM :: Monad m =>
393+ (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
394+zipWithM f m1 m2 = fromStreamD $ D.zipWithM f (toStreamD m1) (toStreamD m2)
395+
396+-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
397+-- elements @a@ and @b@ are then zipped using the supplied zip function and the
398+-- result @c@ is yielded to the consumer.
399+--
400+-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
401+-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
402+--
403+-- >>> s1 = Stream.unfold Unfold.fromList [1,2,3]
404+-- >>> s2 = Stream.unfold Unfold.fromList [4,5,6]
405+-- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2
406+-- [5,7,9]
407+--
408+{-# INLINE zipWith #-}
409+zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
410+zipWith f m1 m2 = fromStreamD $ D.zipWith f (toStreamD m1) (toStreamD m2)
core/src/Streamly/Internal/Data/Stream/Common.hs+1−49
@@ -14,20 +14,12 @@
1414--
1515module Streamly.Internal.Data.Stream.Common
1616 (
17− -- * Running Effects
18− drain
19−
2017 -- * Conversion operations
21− , fromList
18+ fromList
2219 , toList
2320
2421 -- * Fold operations
25− , foldrM
26− , foldrMx
2722 , foldr
28−
29− , foldlx'
30− , foldlMx'
3123 , foldl'
3224 , fold
3325
@@ -57,16 +49,6 @@ import Prelude hiding (foldr, repeat)
5749-- Conversions
5850------------------------------------------------------------------------------
5951
60−{-# INLINE_EARLY drain #-}
61−drain :: Monad m => K.Stream m a -> m ()
62−drain m = D.drain $ D.fromStreamK m
63−{-# RULES "drain fallback to CPS" [1]
64− forall a. D.drain (D.fromStreamK a) = K.drain a #-}
65−
66−------------------------------------------------------------------------------
67−-- Conversions
68−------------------------------------------------------------------------------
69−
7052-- |
7153-- @
7254-- fromList = 'Prelude.foldr' 'K.cons' 'K.nil'
@@ -75,7 +57,6 @@ drain m = D.drain $ D.fromStreamK m
7557-- Construct a stream from a list of pure values. This is more efficient than
7658-- 'K.fromFoldable' for serial streams.
7759--
78−-- @since 0.4.0
7960{-# INLINE_EARLY fromList #-}
8061fromList :: Monad m => [a] -> K.Stream m a
8162fromList = S.toStreamK . S.fromList
@@ -84,7 +65,6 @@ fromList = S.toStreamK . S.fromList
8465
8566-- | Convert a stream into a list in the underlying monad.
8667--
87−-- @since 0.1.0
8868{-# INLINE toList #-}
8969toList :: Monad m => K.Stream m a -> m [a]
9070toList m = S.toList $ S.fromStreamK m
@@ -97,38 +77,12 @@ toList m = S.toList $ S.fromStreamK m
9777foldrM :: Monad m => (a -> m b -> m b) -> m b -> K.Stream m a -> m b
9878foldrM step acc m = S.foldrM step acc $ S.fromStreamK m
9979
100−{-# INLINE foldrMx #-}
101−foldrMx :: Monad m
102− => (a -> m x -> m x) -> m x -> (m x -> m b) -> K.Stream m a -> m b
103−foldrMx step final project m = D.foldrMx step final project $ D.fromStreamK m
104−
10580{-# INLINE foldr #-}
10681foldr :: Monad m => (a -> b -> b) -> b -> K.Stream m a -> m b
10782foldr f z = foldrM (\a b -> f a <$> b) (return z)
10883
109−-- | Like 'foldlx'', but with a monadic step function.
110−--
111−-- @since 0.7.0
112−{-# INLINE foldlMx' #-}
113−foldlMx' ::
114− Monad m
115− => (x -> a -> m x) -> m x -> (x -> m b) -> K.Stream m a -> m b
116−foldlMx' step begin done m = S.foldlMx' step begin done $ S.fromStreamK m
117−
118−-- | Strict left fold with an extraction function. Like the standard strict
119−-- left fold, but applies a user supplied extraction function (the third
120−-- argument) to the folded value at the end. This is designed to work with the
121−-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
122−--
123−-- @since 0.7.0
124−{-# INLINE foldlx' #-}
125−foldlx' ::
126− Monad m => (x -> a -> x) -> x -> (x -> b) -> K.Stream m a -> m b
127−foldlx' step begin done m = S.foldlx' step begin done $ S.fromStreamK m
128−
12984-- | Strict left associative fold.
13085--
131−-- @since 0.2.0
13286{-# INLINE foldl' #-}
13387foldl' ::
13488 Monad m => (b -> a -> b) -> b -> K.Stream m a -> m b
@@ -145,7 +99,6 @@ fold fld m = S.fold fld $ S.fromStreamK m
14599
146100-- | Compare two streams for equality
147101--
148−-- @since 0.5.3
149102{-# INLINE eqBy #-}
150103eqBy :: Monad m =>
151104 (a -> b -> Bool) -> K.Stream m a -> K.Stream m b -> m Bool
@@ -153,7 +106,6 @@ eqBy f m1 m2 = D.eqBy f (D.fromStreamK m1) (D.fromStreamK m2)
153106
154107-- | Compare two streams
155108--
156−-- @since 0.5.3
157109{-# INLINE cmpBy #-}
158110cmpBy
159111 :: Monad m
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+343−25
@@ -6,45 +6,363 @@
66-- Stability : experimental
77-- Portability : GHC
88--
9+-- This module contains functions ending in the shape:
10+--
11+-- @
12+-- Stream m a -> m b
13+-- @
14+--
15+-- We call them stream folding functions, they reduce a stream @Stream m a@ to
16+-- a monadic value @m b@.
17+
918module Streamly.Internal.Data.Stream.Eliminate
1019 (
11− fold
20+ -- * Running Examples
21+ -- $setup
22+
23+ -- * Running a 'Fold'
24+ -- See "Streamly.Internal.Data.Fold".
25+ fold
26+ , foldBreak
27+ , foldContinue
28+
29+ -- * Running a 'Parser'
30+ -- "Streamly.Internal.Data.Parser".
31+ , parse
32+ , parseK
33+ , parseD
34+ , parseBreak
35+ , parseBreakD
36+
37+ -- * Stream Deconstruction
38+ -- | foldr and foldl do not provide the remaining stream. 'uncons' is more
39+ -- general, as it can be used to implement those as well. It allows to use
40+ -- the stream one element at a time, and we have the remaining stream all
41+ -- the time.
42+ , uncons
43+
44+ -- * Right Folds
45+ , foldrM
46+ , foldr
47+
48+ -- * Left Folds
49+ -- Lazy left folds are useful only for reversing the stream
50+ , foldlS
51+ , foldlT
52+
53+ -- * Multi-Stream folds
54+ -- Full equivalence
55+ , eqBy
56+ , cmpBy
57+
58+ -- finding subsequences
59+ , isPrefixOf
60+ , isInfixOf
61+ , isSuffixOf
62+ , isSubsequenceOf
63+
64+ -- trimming sequences
65+ , stripPrefix
66+ -- , stripInfix
67+ , stripSuffix
1268 )
1369where
1470
15−import Streamly.Internal.Data.Fold.Type (Fold)
16−import Streamly.Internal.Data.Stream.Type (Stream)
71+#include "inline.hs"
1772
18−import qualified Streamly.Internal.Data.Stream.Type as Stream
19−import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD
73+import Control.Monad.Catch (MonadThrow)
74+import Control.Monad.IO.Class (MonadIO(..))
75+import Control.Monad.Trans.Class (MonadTrans(..))
76+import Streamly.Internal.Data.Parser (Parser (..))
77+import Streamly.Internal.Data.Unboxed (Storable)
78+
79+import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
80+import qualified Streamly.Internal.Data.Fold as Fold
81+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
82+import qualified Streamly.Internal.Data.Parser.ParserK.Type as PRK
83+import qualified Streamly.Internal.Data.Stream.StreamD as D
84+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
85+import qualified Streamly.Internal.Data.Stream.StreamK as K
86+
87+import Streamly.Internal.Data.Stream.Bottom
88+import Streamly.Internal.Data.Stream.Type
89+
90+import Prelude hiding (foldr, reverse)
2091
2192-- $setup
22−-- >>> import qualified Streamly.Data.Fold as Fold
23−-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
93+-- >>> :m
94+-- >>> import Streamly.Internal.Data.Stream (Stream)
2495-- >>> import qualified Streamly.Internal.Data.Stream as Stream
96+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
97+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
98+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
99+
100+------------------------------------------------------------------------------
101+-- Deconstruction
102+------------------------------------------------------------------------------
103+
104+-- | Decompose a stream into its head and tail. If the stream is empty, returns
105+-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
106+-- the head of the stream and @ma@ its tail.
107+--
108+-- This can be used to do pretty much anything in an imperative manner, as it
109+-- just breaks down the stream into individual elements and we can loop over
110+-- them as we deem fit. For example, this can be used to convert a streamly
111+-- stream into other stream types.
112+--
113+-- All the folds in this module can be expressed in terms of 'uncons', however,
114+-- this is generally less efficient than specific folds because it takes apart
115+-- the stream one element at a time, therefore, does not take adavantage of
116+-- stream fusion.
117+--
118+-- 'foldBreak' is a more general way of consuming a stream piecemeal.
119+--
120+-- >>> :{
121+-- uncons xs = do
122+-- r <- Stream.foldBreak Fold.one xs
123+-- return $ case r of
124+-- (Nothing, _) -> Nothing
125+-- (Just h, t) -> Just (h, t)
126+-- :}
127+--
128+{-# INLINE uncons #-}
129+uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
130+uncons m = fmap (fmap (fmap fromStreamK)) $ K.uncons (toStreamK m)
131+
132+------------------------------------------------------------------------------
133+-- Right Folds
134+------------------------------------------------------------------------------
135+
136+-- | Right associative/lazy pull fold. @foldrM build final stream@ constructs
137+-- an output structure using the step function @build@. @build@ is invoked with
138+-- the next input element and the remaining (lazy) tail of the output
139+-- structure. It builds a lazy output expression using the two. When the "tail
140+-- structure" in the output expression is evaluated it calls @build@ again thus
141+-- lazily consuming the input @stream@ until either the output expression built
142+-- by @build@ is free of the "tail" or the input is exhausted in which case
143+-- @final@ is used as the terminating case for the output structure. For more
144+-- details see the description in the previous section.
145+--
146+-- Example, determine if any element is 'odd' in a stream:
147+--
148+-- >>> s = Stream.unfold Unfold.fromList (2:4:5:undefined)
149+-- >>> step x xs = if odd x then return True else xs
150+-- >>> Stream.foldrM step (return False) s
151+-- True
152+--
153+{-# INLINE foldrM #-}
154+foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
155+foldrM step acc m = D.foldrM step acc $ toStreamD m
156+
157+-- | Right fold, lazy for lazy monads and pure streams, and strict for strict
158+-- monads.
159+--
160+-- Please avoid using this routine in strict monads like IO unless you need a
161+-- strict right fold. This is provided only for use in lazy monads (e.g.
162+-- Identity) or pure streams. Note that with this signature it is not possible
163+-- to implement a lazy foldr when the monad @m@ is strict. In that case it
164+-- would be strict in its accumulator and therefore would necessarily consume
165+-- all its input.
166+--
167+{-# INLINE foldr #-}
168+foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
169+foldr f z = foldrM (\a b -> f a <$> b) (return z)
170+
171+------------------------------------------------------------------------------
172+-- Left Folds
173+------------------------------------------------------------------------------
174+
175+-- | Lazy left fold to a stream.
176+{-# INLINE foldlS #-}
177+foldlS ::
178+ (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b
179+foldlS f z =
180+ fromStreamK
181+ . K.foldlS
182+ (\xs x -> toStreamK $ f (fromStreamK xs) x)
183+ (toStreamK z)
184+ . toStreamK
185+
186+-- | Lazy left fold to a transformer monad.
187+--
188+-- For example, to reverse a stream:
189+--
190+-- >>> input = Stream.unfold Unfold.fromList [1..5] :: Stream IO Int
191+-- >>> rev = Stream.fold Fold.toList $ Stream.foldlT (flip Stream.cons) Stream.nil input
192+--
193+{-# INLINE foldlT #-}
194+foldlT :: (Monad m, Monad (s m), MonadTrans s)
195+ => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
196+foldlT f z s = D.foldlT f z (toStreamD s)
197+
198+------------------------------------------------------------------------------
199+-- Running a Parser
200+------------------------------------------------------------------------------
201+
202+-- | Parse a stream using the supplied ParserD 'PRD.Parser'.
203+--
204+-- /Internal/
205+--
206+{-# INLINE_NORMAL parseD #-}
207+parseD :: MonadThrow m => PRD.Parser m a b -> Stream m a -> m b
208+parseD p = D.parse p . toStreamD
25209
26−-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
27−-- expression strictly at each step. The behavior is similar to 'foldl''. A
28−-- 'Fold' can terminate early without consuming the full stream. See the
29−-- documentation of individual 'Fold's for termination behavior.
210+-- | Parse a stream using the supplied ParserK 'PRK.Parser'.
30211--
31−-- >>> Stream.fold Fold.sum (Stream.unfold Unfold.enumerateFromTo (1,100))
32−-- 5050
212+-- /Internal/
213+{-# INLINE parseK #-}
214+parseK :: MonadThrow m => PRK.Parser m a b -> Stream m a -> m b
215+parseK = parse
216+
217+-- | Parse a stream using the supplied 'Parser'.
218+--
219+-- Unlike folds, parsers may not always result in a valid output, they may
220+-- result in an error. For example:
221+--
222+-- >>> Stream.parse (Parser.takeEQ 1 Fold.drain) Stream.nil
223+-- *** Exception: ParseError "takeEQ: Expecting exactly 1 elements, input terminated on 0"
224+--
225+-- Note: @parse p@ is not the same as @head . parseMany p@ on an empty stream.
226+--
227+-- /Pre-release/
33228--
34−-- Folds never fail, therefore, they produce a default value even when no input
35−-- is provided. It means we can always fold an empty stream and get a valid
36−-- result. For example:
229+{-# INLINE [3] parse #-}
230+parse :: MonadThrow m => Parser m a b -> Stream m a -> m b
231+parse = parseD . PRD.fromParserK
232+
233+{-# INLINE_NORMAL parseBreakD #-}
234+parseBreakD :: MonadThrow m =>
235+ PRD.Parser m a b -> Stream m a -> m (b, Stream m a)
236+parseBreakD parser strm = do
237+ (b, strmD) <- D.parseBreak parser (toStreamD strm)
238+ return $! (b, fromStreamD strmD)
239+
240+-- | Parse a stream using the supplied 'Parser'.
241+--
242+-- /Internal/
243+--
244+{-# INLINE parseBreak #-}
245+parseBreak :: MonadThrow m => Parser m a b -> Stream m a -> m (b, Stream m a)
246+parseBreak p strm = fmap f $ K.parseBreak (PRD.fromParserK p) (toStreamK strm)
247+
248+ where
249+
250+ f (b, str) = (b, fromStreamK str)
251+
252+------------------------------------------------------------------------------
253+-- Multi-stream folds
254+------------------------------------------------------------------------------
255+
256+-- | Returns 'True' if the first stream is the same as or a prefix of the
257+-- second. A stream is a prefix of itself.
258+--
259+-- >>> Stream.isPrefixOf (Stream.unfold Unfold.fromList "hello") (Stream.unfold Unfold.fromList "hello" :: Stream IO Char)
260+-- True
261+--
262+{-# INLINE isPrefixOf #-}
263+isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
264+isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
265+
266+-- | Returns 'True' if the first stream is an infix of the second. A stream is
267+-- considered an infix of itself.
268+--
269+-- >>> s = Stream.unfold Unfold.fromList "hello" :: Stream IO Char
270+-- >>> Stream.isInfixOf s s
271+-- True
272+--
273+-- Space: @O(n)@ worst case where @n@ is the length of the infix.
274+--
275+-- /Pre-release/
276+--
277+-- /Requires 'Storable' constraint/
278+--
279+{-# INLINE isInfixOf #-}
280+isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a)
281+ => Stream m a -> Stream m a -> m Bool
282+isInfixOf infx stream = do
283+ arr <- fold Array.write infx
284+ -- XXX can use breakOnSeq instead (when available)
285+ r <- D.null $ D.drop 1 $ D.splitOnSeq arr Fold.drain $ toStreamD stream
286+ return (not r)
287+
288+-- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf
289+-- may use the suffix stream many times. To run in optimal memory we do not
290+-- want to buffer the suffix stream in memory therefore we need an ability to
291+-- clone (or consume it multiple times) the suffix stream without any side
292+-- effects so that multiple potential suffix matches can proceed in parallel
293+-- without buffering the suffix stream. For example, we may create the suffix
294+-- stream from a file handle, however, if we evaluate the stream multiple
295+-- times, once for each match, we will need a different file handle each time
296+-- which may exhaust the file descriptors. Instead, we want to share the same
297+-- underlying file descriptor, use pread on it to generate the stream and clone
298+-- the stream for each match. Therefore the suffix stream should be built in
299+-- such a way that it can be consumed multiple times without any problems.
300+
301+-- XXX Can be implemented with better space/time complexity.
302+-- Space: @O(n)@ worst case where @n@ is the length of the suffix.
303+
304+-- | Returns 'True' if the first stream is a suffix of the second. A stream is
305+-- considered a suffix of itself.
306+--
307+-- >>> Stream.isSuffixOf (Stream.unfold Unfold.fromList "hello") (Stream.unfold Unfold.fromList "hello" :: Stream IO Char)
308+-- True
309+--
310+-- Space: @O(n)@, buffers entire input stream and the suffix.
311+--
312+-- /Pre-release/
313+--
314+-- /Suboptimal/ - Help wanted.
315+--
316+{-# INLINE isSuffixOf #-}
317+isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
318+isSuffixOf suffix stream = reverse suffix `isPrefixOf` reverse stream
319+
320+-- | Returns 'True' if all the elements of the first stream occur, in order, in
321+-- the second stream. The elements do not have to occur consecutively. A stream
322+-- is a subsequence of itself.
323+--
324+-- >>> Stream.isSubsequenceOf (Stream.unfold Unfold.fromList "hlo") (Stream.unfold Unfold.fromList "hello" :: Stream IO Char)
325+-- True
326+--
327+{-# INLINE isSubsequenceOf #-}
328+isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
329+isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)
330+
331+-- Note: If we want to return a Maybe value to know whether the
332+-- suffix/infix was present or not along with the stripped stream then
333+-- we need to buffer the whole input stream.
334+
335+-- | @stripPrefix prefix stream@ strips @prefix@ from @stream@ if it is a
336+-- prefix of stream. Returns 'Nothing' if the stream does not start with the
337+-- given prefix, stripped stream otherwise. Returns @Just nil@ when the prefix
338+-- is the same as the stream.
339+--
340+-- See also "Streamly.Internal.Data.Stream.Nesting.dropPrefix".
341+--
342+-- Space: @O(1)@
343+--
344+{-# INLINE stripPrefix #-}
345+stripPrefix
346+ :: (Monad m, Eq a)
347+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
348+stripPrefix m1 m2 = fmap fromStreamD <$>
349+ D.stripPrefix (toStreamD m1) (toStreamD m2)
350+
351+-- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does
352+-- not end with the given suffix. Returns @Just nil@ when the suffix is the
353+-- same as the stream.
37354--
38−-- >> Stream.fold Fold.sum Stream.nil
39−-- >0
355+-- It may be more efficient to convert the stream to an Array and use
356+-- stripSuffix on that especially if the elements have a Storable or Prim
357+-- instance.
40358--
41−-- However, 'foldMany' on an empty stream results in an empty stream.
42−-- Therefore, @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany
43−-- f@.
359+-- See also "Streamly.Internal.Data.Stream.Nesting.dropSuffix".
44360--
45−-- @fold f = Stream.parse (Parser.fromFold f)@
361+-- Space: @O(n)@, buffers the entire input stream as well as the suffix
46362--
47363-- /Pre-release/
48−{-# INLINE fold #-}
49−fold :: Monad m => Fold m a b -> Stream m a -> m b
50−fold fld = StreamD.fold fld . Stream.toStreamD
364+{-# INLINE stripSuffix #-}
365+stripSuffix
366+ :: (Monad m, Eq a)
367+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
368+stripSuffix m1 m2 = fmap reverse <$> stripPrefix (reverse m1) (reverse m2)
core/src/Streamly/Internal/Data/Stream/Exception.hsnew file+262
@@ -0,0 +1,262 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Exception
3+-- Copyright : (c) 2019 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Internal.Data.Stream.Exception
10+ (
11+ before
12+ , after_
13+ , after
14+ , bracket_
15+ , bracket
16+ , bracket'
17+ , onException
18+ , finally_
19+ , finally
20+ , ghandle
21+ , handle
22+ , retry
23+ )
24+where
25+
26+import Control.Exception (Exception)
27+import Control.Monad.Catch (MonadCatch)
28+import Data.Map.Strict (Map)
29+import Streamly.Internal.Control.Concurrent (MonadAsync)
30+import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
31+
32+import qualified Streamly.Internal.Data.Stream.StreamD as D
33+
34+-- $setup
35+-- >>> :m
36+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
37+
38+------------------------------------------------------------------------------
39+-- Exceptions
40+------------------------------------------------------------------------------
41+
42+-- | Run the action @m b@ before the stream yields its first element.
43+--
44+-- Same as the following but more efficient due to fusion:
45+--
46+-- >>> before action xs = Stream.nilM action <> xs
47+-- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)
48+--
49+{-# INLINE before #-}
50+before :: Monad m => m b -> Stream m a -> Stream m a
51+before action xs = fromStreamD $ D.before action $ toStreamD xs
52+
53+-- | Like 'after', with following differences:
54+--
55+-- * action @m b@ won't run if the stream is garbage collected
56+-- after partial evaluation.
57+-- * Monad @m@ does not require any other constraints.
58+-- * has slightly better performance than 'after'.
59+--
60+-- Same as the following, but with stream fusion:
61+--
62+-- >>> after_ action xs = xs <> Stream.nilM action
63+--
64+-- /Pre-release/
65+--
66+{-# INLINE after_ #-}
67+after_ :: Monad m => m b -> Stream m a -> Stream m a
68+after_ action xs = fromStreamD $ D.after_ action $ toStreamD xs
69+
70+-- | Run the action @m b@ whenever the stream @Stream m a@ stops normally, or
71+-- if it is garbage collected after a partial lazy evaluation.
72+--
73+-- The semantics of the action @m b@ are similar to the semantics of cleanup
74+-- action in 'bracket'.
75+--
76+-- /See also 'after_'/
77+--
78+{-# INLINE after #-}
79+after :: MonadAsync m
80+ => m b -> Stream m a -> Stream m a
81+after action xs = fromStreamD $ D.after action $ toStreamD xs
82+
83+-- | Run the action @m b@ if the stream aborts due to an exception. The
84+-- exception is not caught, simply rethrown.
85+--
86+-- /Inhibits stream fusion/
87+--
88+{-# INLINE onException #-}
89+onException :: MonadCatch m => m b -> Stream m a -> Stream m a
90+onException action xs = fromStreamD $ D.onException action $ toStreamD xs
91+
92+-- | Like 'finally' with following differences:
93+--
94+-- * action @m b@ won't run if the stream is garbage collected
95+-- after partial evaluation.
96+-- * does not require a 'MonadAsync' constraint.
97+-- * has slightly better performance than 'finally'.
98+--
99+-- /Inhibits stream fusion/
100+--
101+-- /Pre-release/
102+--
103+{-# INLINE finally_ #-}
104+finally_ :: MonadCatch m => m b -> Stream m a -> Stream m a
105+finally_ action xs = fromStreamD $ D.finally_ action $ toStreamD xs
106+
107+-- | Run the action @m b@ whenever the stream @Stream m a@ stops normally,
108+-- aborts due to an exception or if it is garbage collected after a partial
109+-- lazy evaluation.
110+--
111+-- The semantics of running the action @m b@ are similar to the cleanup action
112+-- semantics described in 'bracket'.
113+--
114+-- >>> finally release = Stream.bracket (return ()) (const release)
115+--
116+-- /See also 'finally_'/
117+--
118+-- /Inhibits stream fusion/
119+--
120+{-# INLINE finally #-}
121+finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
122+finally action xs = fromStreamD $ D.finally action $ toStreamD xs
123+
124+-- | Like 'bracket' but with following differences:
125+--
126+-- * alloc action @m b@ runs with async exceptions enabled
127+-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
128+-- after partial evaluation.
129+-- * does not require a 'MonadAsync' constraint.
130+-- * has slightly better performance than 'bracket'.
131+--
132+-- /Inhibits stream fusion/
133+--
134+-- /Pre-release/
135+--
136+{-# INLINE bracket_ #-}
137+bracket_ :: MonadCatch m
138+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
139+bracket_ bef aft bet = fromStreamD $
140+ D.bracket_ bef aft (toStreamD . bet)
141+
142+-- | Run the alloc action @m b@ with async exceptions disabled but keeping
143+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
144+-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
145+--
146+-- @b@ is usually a resource under the state of monad @m@, e.g. a file
147+-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
148+-- runs whenever the stream ends normally, due to a sync or async exception or
149+-- if it gets garbage collected after a partial lazy evaluation.
150+--
151+-- 'bracket' only guarantees that the cleanup action runs, and it runs with
152+-- async exceptions enabled. The action must ensure that it can successfully
153+-- cleanup the resource in the face of sync or async exceptions.
154+--
155+-- When the stream ends normally or on a sync exception, cleanup action runs
156+-- immediately in the current thread context, whereas in other cases it runs in
157+-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
158+--
159+-- /See also: 'bracket_'/
160+--
161+-- /Inhibits stream fusion/
162+--
163+{-# INLINE bracket #-}
164+bracket :: (MonadAsync m, MonadCatch m)
165+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
166+bracket bef aft = bracket' bef aft aft aft
167+
168+-- For a use case of this see the "streamly-process" package. It needs to kill
169+-- the process in case of exception or garbage collection, but waits for the
170+-- process to terminate in normal cases.
171+
172+-- | Like 'bracket' but can use separate cleanup actions depending on the mode
173+-- of termination. @bracket' before onStop onGC onException action@ runs
174+-- @action@ using the result of @before@. If the stream stops, @onStop@ action
175+-- is executed, if the stream is abandoned @onGC@ is executed, if the stream
176+-- encounters an exception @onException@ is executed.
177+--
178+-- /Pre-release/
179+{-# INLINE bracket' #-}
180+bracket' :: (MonadAsync m, MonadCatch m)
181+ => m b
182+ -> (b -> m c)
183+ -> (b -> m d)
184+ -> (b -> m e)
185+ -> (b -> Stream m a)
186+ -> Stream m a
187+bracket' bef aft gc exc bet = fromStreamD $
188+ D.bracket' bef aft exc gc (toStreamD . bet)
189+
190+-- | Like 'handle' but the exception handler is also provided with the stream
191+-- that generated the exception as input. The exception handler can thus
192+-- re-evaluate the stream to retry the action that failed. The exception
193+-- handler can again call 'ghandle' on it to retry the action multiple times.
194+--
195+-- This is highly experimental. In a stream of actions we can map the stream
196+-- with a retry combinator to retry each action on failure.
197+--
198+-- /Inhibits stream fusion/
199+--
200+-- /Pre-release/
201+--
202+{-# INLINE ghandle #-}
203+ghandle :: (MonadCatch m, Exception e)
204+ => (e -> Stream m a -> Stream m a) -> Stream m a -> Stream m a
205+ghandle handler =
206+ fromStreamD
207+ . D.ghandle (\e xs -> toStreamD $ handler e (fromStreamD xs))
208+ . toStreamD
209+
210+-- | When evaluating a stream if an exception occurs, stream evaluation aborts
211+-- and the specified exception handler is run with the exception as argument.
212+--
213+-- /Inhibits stream fusion/
214+--
215+{-# INLINE handle #-}
216+handle :: (MonadCatch m, Exception e)
217+ => (e -> Stream m a) -> Stream m a -> Stream m a
218+handle handler xs =
219+ fromStreamD $ D.handle (toStreamD . handler) $ toStreamD xs
220+
221+
222+-- | @retry@ takes 3 arguments
223+--
224+-- 1. A map @m@ whose keys are exceptions and values are the number of times to
225+-- retry the action given that the exception occurs.
226+--
227+-- 2. A handler @han@ that decides how to handle an exception when the exception
228+-- cannot be retried.
229+--
230+-- 3. The stream itself that we want to run this mechanism on.
231+--
232+-- When evaluating a stream if an exception occurs,
233+--
234+-- 1. The stream evaluation aborts
235+--
236+-- 2. The exception is looked up in @m@
237+--
238+-- a. If the exception exists and the mapped value is > 0 then,
239+--
240+-- i. The value is decreased by 1.
241+--
242+-- ii. The stream is resumed from where the exception was called, retrying
243+-- the action.
244+--
245+-- b. If the exception exists and the mapped value is == 0 then the stream
246+-- evaluation stops.
247+--
248+-- c. If the exception does not exist then we handle the exception using
249+-- @han@.
250+--
251+-- /Internal/
252+--
253+{-# INLINE retry #-}
254+retry :: (MonadCatch m, Exception e, Ord e)
255+ => Map e Int
256+ -- ^ map from exception to retry count
257+ -> (e -> Stream m a)
258+ -- ^ default handler for those exceptions that are not in the map
259+ -> Stream m a
260+ -> Stream m a
261+retry emap handler inp =
262+ fromStreamD $ D.retry emap (toStreamD . handler) $ toStreamD inp
core/src/Streamly/Internal/Data/Stream/Expand.hsnew file+581
@@ -0,0 +1,581 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.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.Expand
13+ (
14+ -- * Binary Combinators (Linear)
15+ -- | Functions ending in the shape:
16+ --
17+ -- @Stream m a -> Stream m a -> Stream 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+ append
29+ -- * Binary Combinators (Pair Wise)
30+ -- | Like the functions in the section above these functions also combine
31+ -- two streams into a single stream but when used @n@ times linearly they
32+ -- exhibit O(n^2) complexity. They are best combined in a binary tree
33+ -- fashion using 'concatPairsWith' giving a @n * log n@ complexity. Avoid
34+ -- using these with 'concatMapWith' when combining a large or infinite
35+ -- number of streams.
36+
37+ -- ** Append
38+ , append2
39+
40+ -- ** Zip
41+ , zipWith
42+ , zipWithM
43+
44+ -- ** Merge
45+ , mergeBy
46+ , mergeByM
47+ , mergeByM2
48+ , mergeMinBy
49+ , mergeFstBy
50+
51+ -- * Combine Streams and Unfolds
52+ -- |
53+ -- Expand a stream by repeatedly using an unfold and merging the resulting
54+ -- streams. Functions generally ending in the shape:
55+ --
56+ -- @Unfold m a b -> Stream m a -> Stream m b@
57+
58+ -- ** Append Many (Unfold)
59+ -- | Unfold and flatten streams.
60+ , unfoldMany
61+
62+ -- ** Interpose
63+ -- | Insert effects between streams. Like unfoldMany but intersperses an
64+ -- effect between the streams. A special case of gintercalate.
65+ , interpose
66+ , interposeSuffix
67+ -- , interposeBy
68+
69+ -- ** Intercalate
70+ -- | Insert Streams between Streams.
71+ -- Like unfoldMany but intersperses streams from another source between
72+ -- the streams from the first source.
73+ , intercalate
74+ , intercalateSuffix
75+ , gintercalate
76+ , gintercalateSuffix
77+
78+ -- * Append Many (concatMap)
79+ -- | Map and serially append streams. 'concatMapM' is a generalization of
80+ -- the binary append operation to append many streams.
81+ , concatMapM
82+ , concatMap
83+ , concatM
84+ , concat
85+
86+ -- * ConcatMapWith
87+ -- | Map and flatten a stream like 'concatMap' but using a custom binary
88+ -- stream merging combinator instead of just appending the streams. The
89+ -- merging occurs sequentially, it works efficiently for 'serial', 'async',
90+ -- 'ahead' like merge operations where we consume one stream before the
91+ -- next or in case of 'wAsync' or 'parallel' where we consume all streams
92+ -- simultaneously anyway.
93+ --
94+ -- However, in cases where the merging consumes streams in a round robin
95+ -- fashion, a pair wise merging using 'concatPairsWith' would be more
96+ -- efficient. These cases include operations like 'mergeBy' or 'zipWith'.
97+
98+ , concatMapWith
99+ , bindWith
100+ , concatSmapMWith
101+
102+ -- * ConcatPairsWith
103+ -- | See the notes about suitable merge functions in the 'concatMapWith'
104+ -- section.
105+ , concatPairsWith
106+
107+ -- * IterateMap
108+ -- | Map and flatten Trees of Streams
109+ , iterateMapWith
110+ , iterateSmapMWith
111+ , iterateMapLeftsWith
112+ , iterateUnfold
113+ )
114+where
115+
116+#include "inline.hs"
117+
118+import Streamly.Internal.Data.Stream.Bottom
119+ ( concatM, concatMapM, concatMap, smapM, fromPure, fromEffect
120+ , zipWith, zipWithM)
121+import Streamly.Internal.Data.Stream.Type
122+ ( Stream, fromStreamD, fromStreamK, toStreamD, toStreamK
123+ , bindWith, concatMapWith, nil)
124+import Streamly.Internal.Data.Unfold.Type (Unfold)
125+
126+import qualified Streamly.Internal.Data.Stream.StreamD as D
127+import qualified Streamly.Internal.Data.Stream.StreamK as K (mergeBy, mergeByM)
128+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
129+
130+import Prelude hiding (concat, concatMap, zipWith)
131+
132+-- $setup
133+-- >>> :m
134+-- >>> import Data.Either (either)
135+-- >>> import Data.IORef
136+-- >>> import Prelude hiding (zipWith, concatMap, concat)
137+-- >>> import qualified Streamly.Data.Array.Foreign as Array
138+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
139+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
140+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
141+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
142+-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
143+--
144+
145+------------------------------------------------------------------------------
146+-- Appending
147+------------------------------------------------------------------------------
148+
149+infixr 6 `append2`
150+
151+-- | Append the outputs of two streams, yielding all the elements from the
152+-- first stream and then yielding all the elements from the second stream.
153+--
154+-- IMPORTANT NOTE: This could be 100x faster than @serial/<>@ for appending a
155+-- few (say 100) streams because it can fuse via stream fusion. However, it
156+-- does not scale for a large number of streams (say 1000s) and becomes
157+-- qudartically slow. Therefore use this for custom appending of a few streams
158+-- but use 'concatMap' or 'concatMapWith serial' for appending @n@ streams or
159+-- infinite containers of streams.
160+--
161+-- /Pre-release/
162+{-# INLINE append2 #-}
163+append2 ::Monad m => Stream m b -> Stream m b -> Stream m b
164+append2 m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
165+
166+infixr 6 `append`
167+
168+-- | Appends two streams sequentially, yielding all elements from the first
169+-- stream, and then all elements from the second stream.
170+--
171+-- >>> s1 = Stream.unfold Unfold.fromList [1,2]
172+-- >>> s2 = Stream.unfold Unfold.fromList [3,4]
173+-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
174+-- [1,2,3,4]
175+--
176+-- This operation can be used to fold an infinite lazy container of streams.
177+--
178+-- /Pre-release/
179+--
180+{-# INLINE append #-}
181+append :: Stream m a -> Stream m a -> Stream m a
182+append = (<>)
183+
184+------------------------------------------------------------------------------
185+-- Merging (sorted streams)
186+------------------------------------------------------------------------------
187+
188+-- | Merge two streams using a comparison function. The head elements of both
189+-- the streams are compared and the smaller of the two elements is emitted, if
190+-- both elements are equal then the element from the first stream is used
191+-- first.
192+--
193+-- If the streams are sorted in ascending order, the resulting stream would
194+-- also remain sorted in ascending order.
195+--
196+-- >>> s1 = Stream.unfold Unfold.fromList [1,3,5]
197+-- >>> s2 = Stream.unfold Unfold.fromList [2,4,6,8]
198+-- >>> Stream.fold Fold.toList $ Stream.mergeBy compare s1 s2
199+-- [1,2,3,4,5,6,8]
200+--
201+-- See also: 'mergeByM2'
202+--
203+{-# INLINE mergeBy #-}
204+mergeBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
205+mergeBy f m1 m2 = fromStreamK $ K.mergeBy f (toStreamK m1) (toStreamK m2)
206+
207+-- | Like 'mergeBy' but with a monadic comparison function.
208+--
209+-- Merge two streams randomly:
210+--
211+-- @
212+-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
213+-- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])
214+-- [2,1,2,2,2,1,1,1]
215+-- @
216+--
217+-- Merge two streams in a proportion of 2:1:
218+--
219+-- >>> :{
220+-- do
221+-- let s1 = Stream.unfold Unfold.fromList [1,1,1,1,1,1]
222+-- s2 = Stream.unfold Unfold.fromList [2,2,2]
223+-- let proportionately m n = do
224+-- ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]
225+-- return $ \_ _ -> do
226+-- r <- readIORef ref
227+-- writeIORef ref $ Prelude.tail r
228+-- return $ Prelude.head r
229+-- f <- proportionately 2 1
230+-- xs <- Stream.fold Fold.toList $ Stream.mergeByM f s1 s2
231+-- print xs
232+-- :}
233+-- [1,1,2,1,1,2,1,1,2]
234+--
235+-- See also: 'mergeByM2'
236+--
237+{-# INLINE mergeByM #-}
238+mergeByM
239+ :: Monad m
240+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
241+mergeByM f m1 m2 = fromStreamK $ K.mergeByM f (toStreamK m1) (toStreamK m2)
242+
243+-- | Like 'mergeByM' but much faster, works best when merging statically known
244+-- number of streams. When merging more than two streams try to merge pairs and
245+-- pair of pairs in a tree like structure.'mergeByM' works better with variable
246+-- number of streams being merged using 'concatPairsWith'.
247+--
248+-- /Internal/
249+{-# INLINE mergeByM2 #-}
250+mergeByM2
251+ :: Monad m
252+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
253+mergeByM2 f m1 m2 =
254+ fromStreamD $ D.mergeByM f (toStreamD m1) (toStreamD m2)
255+
256+-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
257+--
258+-- /Unimplemented/
259+{-# INLINABLE mergeMinBy #-}
260+mergeMinBy :: -- Monad m =>
261+ (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
262+mergeMinBy _f _m1 _m2 = undefined
263+ -- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)
264+
265+-- | Like 'mergeByM' but stops merging as soon as the first stream stops.
266+--
267+-- /Unimplemented/
268+{-# INLINABLE mergeFstBy #-}
269+mergeFstBy :: -- Monad m =>
270+ (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
271+mergeFstBy _f _m1 _m2 = undefined
272+ -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
273+
274+------------------------------------------------------------------------------
275+-- Combine N Streams - unfoldMany
276+------------------------------------------------------------------------------
277+
278+-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
279+-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
280+-- therefore provide many times better performance.
281+--
282+{-# INLINE unfoldMany #-}
283+unfoldMany ::Monad m => Unfold m a b -> Stream m a -> Stream m b
284+unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
285+
286+------------------------------------------------------------------------------
287+-- Combine N Streams - interpose
288+------------------------------------------------------------------------------
289+
290+-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
291+
292+-- | Unfold the elements of a stream, intersperse the given element between the
293+-- unfolded streams and then concat them into a single stream.
294+--
295+-- >>> unwords = Stream.interpose ' '
296+--
297+-- /Pre-release/
298+{-# INLINE interpose #-}
299+interpose :: Monad m
300+ => c -> Unfold m b c -> Stream m b -> Stream m c
301+interpose x unf str =
302+ fromStreamD $ D.interpose (return x) unf (toStreamD str)
303+
304+-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
305+
306+-- | Unfold the elements of a stream, append the given element after each
307+-- unfolded stream and then concat them into a single stream.
308+--
309+-- >>> unlines = Stream.interposeSuffix '\n'
310+--
311+-- /Pre-release/
312+{-# INLINE interposeSuffix #-}
313+interposeSuffix :: Monad m
314+ => c -> Unfold m b c -> Stream m b -> Stream m c
315+interposeSuffix x unf str =
316+ fromStreamD $ D.interposeSuffix (return x) unf (toStreamD str)
317+
318+------------------------------------------------------------------------------
319+-- Combine N Streams - intercalate
320+------------------------------------------------------------------------------
321+
322+-- XXX we can swap the order of arguments to gintercalate so that the
323+-- definition of unfoldMany becomes simpler? The first stream should be
324+-- infixed inside the second one. However, if we change the order in
325+-- "interleave" as well similarly, then that will make it a bit unintuitive.
326+--
327+-- > unfoldMany unf str =
328+-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
329+
330+-- | 'interleaveInfix' followed by unfold and concat.
331+--
332+-- /Pre-release/
333+{-# INLINE gintercalate #-}
334+gintercalate
335+ :: Monad m
336+ => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
337+gintercalate unf1 str1 unf2 str2 =
338+ fromStreamD $ D.gintercalate
339+ unf1 (toStreamD str1)
340+ unf2 (toStreamD str2)
341+
342+-- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)
343+
344+-- | 'intersperse' followed by unfold and concat.
345+--
346+-- >>> intercalate u a = Stream.unfoldMany u . Stream.intersperse a
347+-- >>> intersperse = Stream.intercalate Unfold.identity
348+-- >>> unwords = Stream.intercalate Unfold.fromList " "
349+--
350+-- >>> input = Stream.unfold Unfold.fromList ["abc", "def", "ghi"]
351+-- >>> Stream.fold Fold.toList $ Stream.intercalate Unfold.fromList " " input
352+-- "abc def ghi"
353+--
354+{-# INLINE intercalate #-}
355+intercalate :: Monad m
356+ => Unfold m b c -> b -> Stream m b -> Stream m c
357+intercalate unf seed str = fromStreamD $
358+ D.unfoldMany unf $ D.intersperse seed (toStreamD str)
359+
360+-- | 'interleaveSuffix' followed by unfold and concat.
361+--
362+-- /Pre-release/
363+{-# INLINE gintercalateSuffix #-}
364+gintercalateSuffix
365+ :: Monad m
366+ => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
367+gintercalateSuffix unf1 str1 unf2 str2 =
368+ fromStreamD $ D.gintercalateSuffix
369+ unf1 (toStreamD str1)
370+ unf2 (toStreamD str2)
371+
372+-- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)
373+
374+-- | 'intersperseSuffix' followed by unfold and concat.
375+--
376+-- >>> intercalateSuffix u a = Stream.unfoldMany u . Stream.intersperseSuffix a
377+-- >>> intersperseSuffix = Stream.intercalateSuffix Unfold.identity
378+-- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"
379+--
380+-- >>> input = Stream.unfold Unfold.fromList ["abc", "def", "ghi"]
381+-- >>> Stream.fold Fold.toList $ Stream.intercalateSuffix Unfold.fromList "\n" input
382+-- "abc\ndef\nghi\n"
383+--
384+{-# INLINE intercalateSuffix #-}
385+intercalateSuffix :: Monad m
386+ => Unfold m b c -> b -> Stream m b -> Stream m c
387+intercalateSuffix unf seed str = fromStreamD $ D.unfoldMany unf
388+ $ D.intersperseSuffix (return seed) (toStreamD str)
389+
390+------------------------------------------------------------------------------
391+-- Combine N Streams - concatMap
392+------------------------------------------------------------------------------
393+
394+-- | Flatten a stream of streams to a single stream.
395+--
396+-- >>> concat = Stream.concatMap id
397+--
398+-- /Pre-release/
399+{-# INLINE concat #-}
400+concat :: Monad m => Stream m (Stream m a) -> Stream m a
401+concat = concatMap id
402+
403+------------------------------------------------------------------------------
404+-- Combine N Streams - concatMap
405+------------------------------------------------------------------------------
406+
407+-- | Like 'concatMapWith' but carries a state which can be used to share
408+-- information across multiple steps of concat.
409+--
410+-- >>> concatSmapMWith combine f initial = Stream.concatMapWith combine id . Stream.smapM f initial
411+--
412+-- /Pre-release/
413+--
414+{-# INLINE concatSmapMWith #-}
415+concatSmapMWith
416+ :: Monad m
417+ => (Stream m b -> Stream m b -> Stream m b)
418+ -> (s -> a -> m (s, Stream m b))
419+ -> m s
420+ -> Stream m a
421+ -> Stream m b
422+concatSmapMWith combine f initial =
423+ concatMapWith combine id . smapM f initial
424+
425+-- XXX Implement a StreamD version for fusion.
426+
427+-- | Combine streams in pairs using a binary stream combinator, then combine
428+-- the resulting streams in pairs recursively until we get to a single combined
429+-- stream.
430+--
431+-- For example, you can sort a stream using merge sort like this:
432+--
433+-- >>> s = Stream.unfold Unfold.fromList [5,1,7,9,2]
434+-- >>> generate = Stream.fromPure
435+-- >>> combine = Stream.mergeBy compare
436+-- >>> Stream.fold Fold.toList $ Stream.concatPairsWith combine generate s
437+-- [1,2,5,7,9]
438+--
439+-- /Caution: the stream of streams must be finite/
440+--
441+-- /Pre-release/
442+--
443+{-# INLINE concatPairsWith #-}
444+concatPairsWith ::
445+ (Stream m b -> Stream m b -> Stream m b)
446+ -> (a -> Stream m b)
447+ -> Stream m a
448+ -> Stream m b
449+concatPairsWith par f m =
450+ fromStreamK
451+ $ K.concatPairsWith
452+ (\s1 s2 -> toStreamK $ fromStreamK s1 `par` fromStreamK s2)
453+ (toStreamK . f)
454+ (toStreamK m)
455+
456+------------------------------------------------------------------------------
457+-- IterateMap - Map and flatten Trees of Streams
458+------------------------------------------------------------------------------
459+
460+-- | Like 'iterateM' but iterates after mapping a stream generator on the
461+-- output.
462+--
463+-- Yield an input element in the output stream, map a stream generator on it
464+-- and then do the same on the resulting stream. This can be used for a depth
465+-- first traversal of a tree like structure.
466+--
467+-- Note that 'iterateM' is a special case of 'iterateMapWith':
468+--
469+-- >>> iterateM f = Stream.iterateMapWith Stream.append (Stream.fromEffect . f) . Stream.fromEffect
470+--
471+-- It can be used to traverse a tree structure. For example, to list a
472+-- directory tree:
473+--
474+-- >> input = Stream.fromPure (Left ".")
475+-- >> f = either Dir.toEither (const Stream.nil)
476+-- >> ls = Stream.iterateMapWith Stream.append f input
477+--
478+-- /Pre-release/
479+--
480+{-# INLINE iterateMapWith #-}
481+iterateMapWith ::
482+ (Stream m a -> Stream m a -> Stream m a)
483+ -> (a -> Stream m a)
484+ -> Stream m a
485+ -> Stream m a
486+iterateMapWith combine f = concatMapWith combine go
487+ where
488+ go x = fromPure x `combine` concatMapWith combine go (f x)
489+
490+-- | Same as @iterateMapWith Stream.serial@ but more efficient due to stream
491+-- fusion.
492+--
493+-- /Unimplemented/
494+{-# INLINE iterateUnfold #-}
495+iterateUnfold :: -- (MonadAsync m) =>
496+ Unfold m a a -> Stream m a -> Stream m a
497+iterateUnfold = undefined
498+
499+------------------------------------------------------------------------------
500+-- Flattening Graphs
501+------------------------------------------------------------------------------
502+
503+-- To traverse graphs we need a state to be carried around in the traversal.
504+-- For example, we can use a hashmap to store the visited status of nodes.
505+
506+-- | Like 'iterateMap' but carries a state in the stream generation function.
507+-- This can be used to traverse graph like structures, we can remember the
508+-- visited nodes in the state to avoid cycles.
509+--
510+-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
511+-- traverse graphs. However, this function provides a more localized state
512+-- instead of using a global state.
513+--
514+-- See also: 'mfix'
515+--
516+-- /Pre-release/
517+--
518+{-# INLINE iterateSmapMWith #-}
519+iterateSmapMWith
520+ :: Monad m
521+ => (Stream m a -> Stream m a -> Stream m a)
522+ -> (b -> a -> m (b, Stream m a))
523+ -> m b
524+ -> Stream m a
525+ -> Stream m a
526+iterateSmapMWith combine f initial stream =
527+ concatMap
528+ (\b -> concatMapWith combine (go b) stream)
529+ (fromEffect initial)
530+
531+ where
532+
533+ go b a = fromPure a `combine` feedback b a
534+
535+ feedback b a =
536+ concatMap
537+ (\(b1, s) -> concatMapWith combine (go b1) s)
538+ (fromEffect $ f b a)
539+
540+------------------------------------------------------------------------------
541+-- Either streams
542+------------------------------------------------------------------------------
543+
544+-- Keep concating either streams as long as rights are generated, stop as soon
545+-- as a left is generated and concat the left stream.
546+--
547+-- See also: 'handle'
548+--
549+-- /Unimplemented/
550+--
551+{-
552+concatMapEitherWith
553+ :: -- (MonadAsync m) =>
554+ (forall x. t m x -> t m x -> t m x)
555+ -> (a -> t m (Either (Stream m b) b))
556+ -> Stream m a
557+ -> Stream m b
558+concatMapEitherWith = undefined
559+-}
560+
561+-- | In an 'Either' stream iterate on 'Left's. This is a special case of
562+-- 'iterateMapWith':
563+--
564+-- >>> iterateMapLeftsWith combine f = Stream.iterateMapWith combine (either f (const Stream.nil))
565+--
566+-- To traverse a directory tree:
567+--
568+-- >> input = fromPure (Left ".")
569+-- >> ls = Stream.iterateMapLeftsWith Stream.append Dir.toEither input
570+--
571+-- /Pre-release/
572+--
573+{-# INLINE iterateMapLeftsWith #-}
574+iterateMapLeftsWith
575+ :: (b ~ Either a c)
576+ => (Stream m b -> Stream m b -> Stream m b)
577+ -> (a -> Stream m b)
578+ -> Stream m b
579+ -> Stream m b
580+iterateMapLeftsWith combine f =
581+ iterateMapWith combine (either f (const nil))
core/src/Streamly/Internal/Data/Stream/Generate.hs+94−63
@@ -8,25 +8,34 @@
88--
99module Streamly.Internal.Data.Stream.Generate
1010 (
11− cons
12− , consM
11+ -- * Primitives
12+ Stream.nil
13+ , Stream.nilM
14+ , Stream.cons
15+ , Stream.consM
16+
17+ -- * From 'Unfold'
1318 , unfold
14− , append
19+
20+ -- * From Values
21+ , Stream.fromPure
22+ , Stream.fromEffect
23+
24+ -- * Cyclic Elements
25+ , mfix
26+
27+ -- * From Containers
28+ , fromFoldable
29+ , fromFoldableM
1530 )
1631where
1732
18−#if __GLASGOW_HASKELL__ < 808
19−import Data.Semigroup (Semigroup(..))
20−#endif
21−#if __GLASGOW_HASKELL__ < 808
22−import Data.Semigroup (Semigroup(..))
23−#endif
24−import Streamly.Internal.Data.Stream.Type (Stream)
33+import Streamly.Internal.Data.Stream.Type (Stream, fromStreamK, toStreamK)
2534import Streamly.Internal.Data.Unfold (Unfold)
2635
36+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
37+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
2738import qualified Streamly.Internal.Data.Stream.Type as Stream
28−import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD
29−import qualified Streamly.Internal.Data.Stream.StreamK.Type as StreamK
3039
3140-- $setup
3241-- >>> :m
@@ -34,42 +43,6 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as StreamK
3443-- >>> import qualified Streamly.Data.Unfold as Unfold
3544-- >>> import qualified Streamly.Internal.Data.Stream as Stream
3645
37−infixr 5 `cons`
38−
39−-- | Construct a stream by adding a pure value at the head of an existing
40−-- stream. Same as the following but more efficient:
41−--
42−-- For example:
43−--
44−-- >> s = 1 `Stream.cons` 2 `Stream.cons` 3 `Stream.cons` Stream.nil
45−-- >> Stream.fold Fold.toList s
46−-- >[1,2,3]
47−--
48−-- >>> cons x xs = return x `Stream.consM` xs
49−--
50−-- /Pre-release/
51−--
52−{-# INLINE cons #-}
53−cons :: a -> Stream m a -> Stream m a
54−cons x = Stream.fromStreamK . StreamK.cons x . Stream.toStreamK
55−
56−infixr 5 `consM`
57−
58−-- | Constructs a stream by adding a monadic action at the head of an
59−-- existing stream. For example:
60−--
61−-- >> s = putChar 'h' `Stream.consM` putChar 'i' `Stream.consM` Stream.nil
62−-- >> Stream.fold Fold.toList s
63−-- >hi
64−--
65−-- >> consM x xs = Stream.fromEffect x `Stream.append` xs
66−--
67−-- /Pre-release/
68−--
69−{-# INLINE consM #-}
70−{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
71−consM :: Monad m => m a -> Stream m a -> Stream m a
72−consM m = Stream.fromStreamK . StreamK.consM m . Stream.toStreamK
7346
7447------------------------------------------------------------------------------
7548-- From Unfold
@@ -86,26 +59,84 @@ consM m = Stream.fromStreamK . StreamK.consM m . Stream.toStreamK
8659-- /Pre-release/
8760{-# INLINE unfold #-}
8861unfold :: Monad m => Unfold m a b -> a -> Stream m b
89−unfold unf = Stream.fromStreamD . StreamD.unfold unf
62+unfold unf = Stream.fromStreamD . D.unfold unf
63+
64+-- | We can define cyclic structures using @let@:
65+--
66+-- >>> let (a, b) = ([1, b], head a) in (a, b)
67+-- ([1,1],1)
68+--
69+-- The function @fix@ defined as:
70+--
71+-- >>> fix f = let x = f x in x
72+--
73+-- ensures that the argument of a function and its output refer to the same
74+-- lazy value @x@ i.e. the same location in memory. Thus @x@ can be defined
75+-- in terms of itself, creating structures with cyclic references.
76+--
77+-- >>> f ~(a, b) = ([1, b], head a)
78+-- >>> fix f
79+-- ([1,1],1)
80+--
81+-- 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic
82+-- values.
83+--
84+-- Using 'mfix' for streams we can construct a stream in which each element of
85+-- the stream is defined in a cyclic fashion. The argument of the function
86+-- being fixed represents the current element of the stream which is being
87+-- returned by the stream monad. Thus, we can use the argument to construct
88+-- itself.
89+--
90+-- In the following example, the argument @action@ of the function @f@
91+-- represents the tuple @(x,y)@ returned by it in a given iteration. We define
92+-- the first element of the tuple in terms of the second.
93+--
94+-- >>> import Streamly.Internal.Data.Stream as Stream
95+-- >>> import System.IO.Unsafe (unsafeInterleaveIO)
96+--
97+-- >>> :{
98+-- main = Stream.fold (Fold.drainBy print) $ Stream.mfix f
99+-- where
100+-- f action = do
101+-- let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act
102+-- x <- Stream.unfold Unfold.fromListM [incr 1 action, incr 2 action]
103+-- y <- Stream.unfold Unfold.fromList [4,5]
104+-- return (x, y)
105+-- :}
106+--
107+-- Note: you cannot achieve this by just changing the order of the monad
108+-- statements because that would change the order in which the stream elements
109+-- are generated.
110+--
111+-- Note that the function @f@ must be lazy in its argument, that's why we use
112+-- 'unsafeInterleaveIO' on @action@ because IO monad is strict.
113+--
114+-- /Pre-release/
115+{-# INLINE mfix #-}
116+mfix :: Monad m => (m a -> Stream m a) -> Stream m a
117+mfix f = fromStreamK $ K.mfix (toStreamK . f)
90118
91119------------------------------------------------------------------------------
92−-- Combining
120+-- Conversions
93121------------------------------------------------------------------------------
94122
95−infixr 6 `append`
96−
97−-- | Appends two streams sequentially, yielding all elements from the first
98−-- stream, and then all elements from the second stream.
123+-- |
124+-- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil
99125--
100−-- >>> s1 = Stream.unfold Unfold.fromList [1,2]
101−-- >>> s2 = Stream.unfold Unfold.fromList [3,4]
102−-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
103−-- [1,2,3,4]
126+-- Construct a stream from a 'Foldable' containing pure values:
104127--
105−-- This operation can be used to fold an infinite lazy container of streams.
128+{-# INLINE fromFoldable #-}
129+fromFoldable :: Foldable f => f a -> Stream m a
130+fromFoldable = fromStreamK . K.fromFoldable
131+
132+-- |
133+-- >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil
106134--
107−-- /Pre-release/
135+-- Construct a stream from a 'Foldable' containing monadic actions.
136+--
137+-- >>> Stream.fold Fold.toList $ Stream.fromFoldableM $ map return [1,2,3]
138+-- [1,2,3]
108139--
109−{-# INLINE append #-}
110−append :: Stream m a -> Stream m a -> Stream m a
111−append = (<>)
140+{-# INLINE fromFoldableM #-}
141+fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a
142+fromFoldableM = Prelude.foldr Stream.consM Stream.nil
core/src/Streamly/Internal/Data/Stream/Lift.hsnew file+136
@@ -0,0 +1,136 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Lift
3+-- Copyright : (c) 2019 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Internal.Data.Stream.Lift
10+ (
11+ -- * Generalize Inner Monad
12+ hoist
13+ , generally
14+
15+ -- * Transform Inner Monad
16+ , liftInner
17+ , usingReaderT
18+ , runReaderT
19+ , evalStateT
20+ , usingStateT
21+ , runStateT
22+ )
23+where
24+
25+#include "inline.hs"
26+
27+import Control.Monad.Trans.Reader (ReaderT)
28+import Control.Monad.Trans.State.Strict (StateT)
29+import Control.Monad.Trans.Class (MonadTrans(..))
30+import Data.Functor.Identity (Identity (..))
31+import Streamly.Internal.Data.Stream.Type
32+ (Stream, fromStreamK, toStreamK, fromStreamD, toStreamD)
33+
34+import qualified Streamly.Internal.Data.Stream.StreamD as D
35+import qualified Streamly.Internal.Data.Stream.StreamK as K
36+
37+-- $setup
38+-- >>> :m
39+-- >>> import Streamly.Internal.Data.Stream as Stream
40+
41+------------------------------------------------------------------------------
42+-- Generalize the underlying monad
43+------------------------------------------------------------------------------
44+
45+-- | Transform the inner monad of a stream using a natural transformation.
46+--
47+-- / Internal/
48+--
49+{-# INLINE hoist #-}
50+hoist :: (Monad m, Monad n)
51+ => (forall x. m x -> n x) -> Stream m a -> Stream n a
52+hoist f xs = fromStreamK $ K.hoist f (toStreamK xs)
53+
54+-- | Generalize the inner monad of the stream from 'Identity' to any monad.
55+--
56+-- / Internal/
57+--
58+{-# INLINE generally #-}
59+generally :: Monad m => Stream Identity a -> Stream m a
60+generally xs = fromStreamK $ K.hoist (return . runIdentity) (toStreamK xs)
61+
62+------------------------------------------------------------------------------
63+-- Add and remove a monad transformer
64+------------------------------------------------------------------------------
65+
66+-- | Lift the inner monad @m@ of a stream @Stream m a@ to @tr m@ using the monad
67+-- transformer @tr@.
68+--
69+{-# INLINE liftInner #-}
70+liftInner :: (Monad m, MonadTrans tr, Monad (tr m))
71+ => Stream m a -> Stream (tr m) a
72+liftInner xs = fromStreamD $ D.liftInner (toStreamD xs)
73+
74+------------------------------------------------------------------------------
75+-- Sharing read only state in a stream
76+------------------------------------------------------------------------------
77+
78+-- | Evaluate the inner monad of a stream as 'ReaderT'.
79+--
80+{-# INLINE runReaderT #-}
81+runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a
82+runReaderT s xs = fromStreamD $ D.runReaderT s (toStreamD xs)
83+
84+-- | Run a stream transformation using a given environment.
85+--
86+-- See also: 'Serial.map'
87+--
88+-- / Internal/
89+--
90+{-# INLINE usingReaderT #-}
91+usingReaderT
92+ :: Monad m
93+ => m r
94+ -> (Stream (ReaderT r m) a -> Stream (ReaderT r m) a)
95+ -> Stream m a
96+ -> Stream m a
97+usingReaderT r f xs = runReaderT r $ f $ liftInner xs
98+
99+------------------------------------------------------------------------------
100+-- Sharing read write state in a stream
101+------------------------------------------------------------------------------
102+
103+-- | Evaluate the inner monad of a stream as 'StateT'.
104+--
105+-- >>> evalStateT s = fmap snd . Stream.runStateT s
106+--
107+-- / Internal/
108+--
109+{-# INLINE evalStateT #-}
110+evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a
111+-- evalStateT s = fmap snd . runStateT s
112+evalStateT s xs = fromStreamD $ D.evalStateT s (toStreamD xs)
113+
114+-- | Run a stateful (StateT) stream transformation using a given state.
115+--
116+-- >>> usingStateT s f = Stream.evalStateT s . f . Stream.liftInner
117+--
118+-- See also: 'scan'
119+--
120+-- / Internal/
121+--
122+{-# INLINE usingStateT #-}
123+usingStateT
124+ :: Monad m
125+ => m s
126+ -> (Stream (StateT s m) a -> Stream (StateT s m) a)
127+ -> Stream m a
128+ -> Stream m a
129+usingStateT s f = evalStateT s . f . liftInner
130+
131+-- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting
132+-- state and value pair after each step.
133+--
134+{-# INLINE runStateT #-}
135+runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)
136+runStateT s xs = fromStreamD $ D.runStateT s (toStreamD xs)
core/src/Streamly/Internal/Data/Stream/Reduce.hsnew file+318
@@ -0,0 +1,318 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Reduce
3+-- Copyright : (c) 2017 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+-- Reduce streams by streams, folds or parsers.
10+
11+module Streamly.Internal.Data.Stream.Reduce
12+ (
13+ -- * Reduce By Streams
14+ dropPrefix
15+ , dropInfix
16+ , dropSuffix
17+
18+ -- * Reduce By Folds
19+ -- |
20+ -- Reduce a stream by folding or parsing chunks of the stream. Functions
21+ -- generally ending in these shapes:
22+ --
23+ -- @
24+ -- f (Fold m a b) -> Stream m a -> Stream m b
25+ -- f (Parser m a b) -> Stream m a -> Stream m b
26+ -- @
27+
28+ -- ** Generic Folding
29+ -- | Apply folds on a stream.
30+ , foldMany
31+ , foldManyPost
32+ , refoldMany
33+ , foldSequence
34+ , foldIterateM
35+ , refoldIterateM
36+
37+ -- ** Chunking
38+ -- | Element unaware grouping.
39+ , arraysOf
40+
41+ -- * Reduce By Parsers
42+ -- ** Generic Parsing
43+ -- | Apply parsers on a stream.
44+ , parseMany
45+ , parseManyD
46+ , parseManyTill
47+ , parseSequence
48+ , parseIterate
49+
50+ )
51+where
52+
53+import Control.Monad.Catch (MonadThrow)
54+import Control.Monad.IO.Class (MonadIO(..))
55+import Streamly.Internal.Data.Array.Foreign.Type (Array)
56+import Streamly.Internal.Data.Fold.Type (Fold (..))
57+import Streamly.Internal.Data.Parser (Parser (..))
58+import Streamly.Internal.Data.Refold.Type (Refold (..))
59+import Streamly.Internal.Data.Stream.Bottom (foldManyPost)
60+import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
61+import Streamly.Internal.Data.Unboxed (Storable)
62+
63+import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
64+import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
65+import qualified Streamly.Internal.Data.Stream.StreamD as D
66+
67+import Prelude hiding (concatMap, map)
68+
69+-- $setup
70+-- >>> :m
71+-- >>> import Prelude hiding (zipWith, concatMap, concat)
72+-- >>> import Streamly.Internal.Data.Stream as Stream
73+-- >>> import qualified Streamly.Data.Fold as Fold
74+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
75+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
76+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
77+-- >>> import qualified Streamly.Data.Array.Foreign as Array
78+
79+------------------------------------------------------------------------------
80+-- Trimming
81+------------------------------------------------------------------------------
82+
83+-- | Drop prefix from the input stream if present.
84+--
85+-- Space: @O(1)@
86+--
87+-- /Unimplemented/
88+{-# INLINE dropPrefix #-}
89+dropPrefix ::
90+ -- (Monad m, Eq a) =>
91+ Stream m a -> Stream m a -> Stream m a
92+dropPrefix = error "Not implemented yet!"
93+
94+-- | Drop all matching infix from the input stream if present. Infix stream
95+-- may be consumed multiple times.
96+--
97+-- Space: @O(n)@ where n is the length of the infix.
98+--
99+-- /Unimplemented/
100+{-# INLINE dropInfix #-}
101+dropInfix ::
102+ -- (Monad m, Eq a) =>
103+ Stream m a -> Stream m a -> Stream m a
104+dropInfix = error "Not implemented yet!"
105+
106+-- | Drop suffix from the input stream if present. Suffix stream may be
107+-- consumed multiple times.
108+--
109+-- Space: @O(n)@ where n is the length of the suffix.
110+--
111+-- /Unimplemented/
112+{-# INLINE dropSuffix #-}
113+dropSuffix ::
114+ -- (Monad m, Eq a) =>
115+ Stream m a -> Stream m a -> Stream m a
116+dropSuffix = error "Not implemented yet!"
117+
118+------------------------------------------------------------------------------
119+-- Folding
120+------------------------------------------------------------------------------
121+
122+-- Splitting operations that take a predicate and a Fold can be
123+-- expressed using parseMany. Operations like chunksOf, intervalsOf, split*,
124+-- can be expressed using parseMany when used with an appropriate Parser.
125+--
126+-- XXX We need takeGE/takeBetween to implement "some" using "many".
127+
128+-- | Apply a 'Fold' repeatedly on a stream and emit the fold outputs in the
129+-- output stream.
130+--
131+-- To sum every two contiguous elements in a stream:
132+--
133+-- >>> f = Fold.take 2 Fold.sum
134+-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.unfold Unfold.fromList [1..10]
135+-- [3,7,11,15,19]
136+--
137+-- On an empty stream the output is empty:
138+--
139+-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.unfold Unfold.fromList []
140+-- []
141+--
142+-- Note @Stream.foldMany (Fold.take 0)@ would result in an infinite loop in a
143+-- non-empty stream.
144+--
145+{-# INLINE foldMany #-}
146+foldMany
147+ :: Monad m
148+ => Fold m a b
149+ -> Stream m a
150+ -> Stream m b
151+foldMany f m = fromStreamD $ D.foldMany f (toStreamD m)
152+
153+-- | Like 'foldMany' but using the 'Refold' type instead of 'Fold'.
154+--
155+-- /Pre-release/
156+{-# INLINE refoldMany #-}
157+refoldMany :: Monad m =>
158+ Refold m c a b -> m c -> Stream m a -> Stream m b
159+refoldMany f action = fromStreamD . D.refoldMany f action . toStreamD
160+
161+-- | Apply a stream of folds to an input stream and emit the results in the
162+-- output stream.
163+--
164+-- /Unimplemented/
165+--
166+{-# INLINE foldSequence #-}
167+foldSequence
168+ :: -- Monad m =>
169+ Stream m (Fold m a b)
170+ -> Stream m a
171+ -> Stream m b
172+foldSequence _f _m = undefined
173+
174+-- | Iterate a fold generator on a stream. The initial value @b@ is used to
175+-- generate the first fold, the fold is applied on the stream and the result of
176+-- the fold is used to generate the next fold and so on.
177+--
178+-- >>> import Data.Monoid (Sum(..))
179+-- >>> f x = return (Fold.take 2 (Fold.sconcat x))
180+-- >>> s = fmap Sum $ Stream.unfold Unfold.fromList [1..10]
181+-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s
182+-- [3,10,21,36,55,55]
183+--
184+-- This is the streaming equivalent of monad like sequenced application of
185+-- folds where next fold is dependent on the previous fold.
186+--
187+-- /Pre-release/
188+--
189+{-# INLINE foldIterateM #-}
190+foldIterateM ::
191+ Monad m => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b
192+foldIterateM f i m = fromStreamD $ D.foldIterateM f i (toStreamD m)
193+
194+-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be
195+-- much more efficient due to stream fusion.
196+--
197+-- /Internal/
198+{-# INLINE refoldIterateM #-}
199+refoldIterateM :: Monad m =>
200+ Refold m b a b -> m b -> Stream m a -> Stream m b
201+refoldIterateM c i m = fromStreamD $ D.refoldIterateM c i (toStreamD m)
202+
203+------------------------------------------------------------------------------
204+-- Parsing
205+------------------------------------------------------------------------------
206+
207+-- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the
208+-- output stream.
209+--
210+-- This is the streaming equivalent of the 'Streamly.Internal.Data.Parser.many'
211+-- parse combinator.
212+--
213+-- >>> s = Stream.unfold Unfold.fromList [1..10]
214+-- >>> parser = Parser.takeBetween 0 2 Fold.sum
215+-- >>> Stream.fold Fold.toList $ Stream.parseMany parser s
216+-- [3,7,11,15,19]
217+--
218+-- foldMany f = Stream.parseMany (Parser.fromFold f)
219+--
220+-- Known Issues: When the parser fails there is no way to get the remaining
221+-- stream.
222+--
223+-- /Pre-release/
224+--
225+{-# INLINE parseMany #-}
226+parseMany
227+ :: MonadThrow m
228+ => Parser m a b
229+ -> Stream m a
230+ -> Stream m b
231+parseMany p m =
232+ fromStreamD $ D.parseMany (ParserD.fromParserK p) (toStreamD m)
233+
234+-- | Same as parseMany but for StreamD streams.
235+--
236+-- /Internal/
237+--
238+{-# INLINE parseManyD #-}
239+parseManyD
240+ :: MonadThrow m
241+ => ParserD.Parser m a b
242+ -> Stream m a
243+ -> Stream m b
244+parseManyD p m =
245+ fromStreamD $ D.parseMany p (toStreamD m)
246+
247+-- | Apply a stream of parsers to an input stream and emit the results in the
248+-- output stream.
249+--
250+-- /Unimplemented/
251+--
252+{-# INLINE parseSequence #-}
253+parseSequence
254+ :: -- Monad m =>
255+ Stream m (Parser m a b)
256+ -> Stream m a
257+ -> Stream m b
258+parseSequence _f _m = undefined
259+
260+-- XXX Change the parser arguments' order
261+
262+-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
263+-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
264+-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
265+-- output of @test@ is discarded and the output of @collect@ is emitted in the
266+-- output stream. The parser fails if @collect@ fails.
267+--
268+-- /Unimplemented/
269+--
270+{-# INLINE parseManyTill #-}
271+parseManyTill ::
272+ -- MonadThrow m =>
273+ Parser m a b
274+ -> Parser m a x
275+ -> t m a
276+ -> t m b
277+parseManyTill = undefined
278+
279+-- | Iterate a parser generating function on a stream. The initial value @b@ is
280+-- used to generate the first parser, the parser is applied on the stream and
281+-- the result is used to generate the next parser and so on.
282+--
283+-- >>> import Data.Monoid (Sum(..))
284+-- >>> s = Stream.unfold Unfold.fromList [1..10]
285+-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) 0 $ fmap Sum s
286+-- [3,10,21,36,55,55]
287+--
288+-- This is the streaming equivalent of monad like sequenced application of
289+-- parsers where next parser is dependent on the previous parser.
290+--
291+-- /Pre-release/
292+--
293+{-# INLINE parseIterate #-}
294+parseIterate
295+ :: MonadThrow m
296+ => (b -> Parser m a b)
297+ -> b
298+ -> Stream m a
299+ -> Stream m b
300+parseIterate f i m = fromStreamD $
301+ D.parseIterate (ParserD.fromParserK . f) i (toStreamD m)
302+
303+------------------------------------------------------------------------------
304+-- Chunking
305+------------------------------------------------------------------------------
306+
307+-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
308+-- @n@ elements each.
309+--
310+-- Same as the following but may be more efficient:
311+--
312+-- >>> arraysOf n = Stream.foldMany (Array.writeN n)
313+--
314+-- /Pre-release/
315+{-# INLINE arraysOf #-}
316+arraysOf :: (MonadIO m, Storable a)
317+ => Int -> Stream m a -> Stream m (Array a)
318+arraysOf n = fromStreamD . Array.arraysOf n . toStreamD
core/src/Streamly/Internal/Data/Stream/Serial.hs+4−2
@@ -343,6 +343,8 @@ list = fromStreamD . D.fromList
343343unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> SerialT m a
344344unfoldrM step seed = SerialT $ D.toStreamK (D.unfoldrM step seed)
345345
346−{-# INLINE drain #-}
346+{-# INLINE_EARLY drain #-}
347347drain :: Monad m => SerialT m a -> m ()
348−drain (SerialT m) = P.drain m
348+drain (SerialT m) = D.drain $ D.fromStreamK m
349+{-# RULES "drain fallback to CPS" [1]
350+ forall a. D.drain (D.fromStreamK a) = K.drain a #-}
core/src/Streamly/Internal/Data/Stream/Transform.hs+831−28
@@ -5,37 +5,249 @@
55-- Maintainer : streamly@composewell.com
66-- Stability : experimental
77-- Portability : GHC
8−--
8+
99module Streamly.Internal.Data.Stream.Transform
1010 (
11− filter
12− , foldFilter
13− , map
11+ -- * Piping
12+ -- | Pass through a 'Pipe'.
13+ transform
14+
15+ -- * Folding
16+ , foldrS
17+ , foldrT
18+
19+ -- * Mapping
20+ -- | Stateless one-to-one maps.
21+ , sequence
1422 , mapM
23+
24+ -- * Mapping Side Effects (Observation)
25+ -- | See also the intersperse*_ combinators.
26+ , trace
27+ , trace_
28+ , tap
29+
30+ -- * Scanning
31+ , scan
32+ , scanMany
33+ , postscan
34+ , smapM
35+
36+ -- * Filtering
37+ -- | Produce a subset of the stream using criteria based on the values of
38+ -- the elements. We can use a concatMap and scan for filtering but these
39+ -- combinators are more efficient and convenient.
40+
41+ , with
42+ , deleteBy
43+ , filter
44+ , filterM
45+ , foldFilter
46+ , uniq
47+ , uniqBy
48+ , nubBy
49+ , prune
50+ , repeated
51+
52+ -- * Trimming
53+ -- | Produce a subset of the stream trimmed at ends.
54+
55+ , take
56+ , takeWhile
57+ , takeWhileM
58+ , drop
59+ , dropWhile
60+ , dropWhileM
61+
62+ -- * Inserting Elements
63+ -- | Produce a superset of the stream. This is the opposite of
64+ -- filtering/sampling. We can always use concatMap and scan for inserting
65+ -- but these combinators are more efficient and convenient.
66+
67+ -- Element agnostic (Opposite of sampling)
68+ , intersperse
69+ , intersperseM -- XXX naming
70+ , intersperseBySpan
71+
72+ , intersperseSuffix
73+ , intersperseSuffixBySpan
74+
75+ -- , interspersePrefix
76+ -- , interspersePrefixBySpan
77+
78+ -- * Inserting Side Effects/Time
79+ , intersperseM_ -- XXX naming
80+ , delay
81+ , intersperseSuffix_
82+ , delayPost
83+ , interspersePrefix_
84+ , delayPre
85+
86+ -- * Element Aware Insertion
87+ -- | Opposite of filtering
88+ , insertBy
89+ -- , intersperseByBefore
90+ -- , intersperseByAfter
91+
92+ -- * Reordering
93+ , reverse
94+ , reverse'
95+ , reassembleBy
96+
97+ -- * Position Indexing
98+ , indexed
99+ , indexedR
100+
101+ -- * Searching
102+ , findIndices -- XXX indicesBy
103+ , elemIndices -- XXX indicesOf
104+
105+ -- * Rolling map
106+ -- | Map using the previous element.
107+ , rollingMapM
108+ , rollingMap
109+ , rollingMap2
110+
111+ -- * Maybe Streams
112+ -- Move these to Streamly.Data.Maybe.Stream?
113+ , catMaybes -- XXX justs (like lefts/rights)
114+ , mapMaybe
115+ , mapMaybeM
116+
117+ -- * Either Streams
118+ -- Move these to Streamly.Data.Either.Stream?
119+ , lefts
120+ , rights
121+ , both
15122 )
16123where
17124
125+#include "inline.hs"
126+
127+import Control.Concurrent (threadDelay)
128+import Control.Monad (void)
129+import Control.Monad.IO.Class (MonadIO (liftIO))
130+import Control.Monad.Trans.Class (MonadTrans(..))
131+import Data.Either (fromLeft, isLeft, isRight, fromRight)
132+import Data.Maybe (isJust, fromJust)
133+
18134import Streamly.Internal.Data.Fold.Type (Fold)
19−import Streamly.Internal.Data.Stream.Type (Stream)
135+import Streamly.Internal.Data.Pipe (Pipe)
136+
137+import qualified Streamly.Internal.Data.Fold as FL
138+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
139+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
140+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
20141
21−import qualified Streamly.Internal.Data.Stream.Type as Stream
22−import qualified Streamly.Internal.Data.Stream.StreamD.Transform as StreamD
142+import Streamly.Internal.Data.Stream.Bottom
143+import Streamly.Internal.Data.Stream.Type
23144
24−import Prelude hiding (map, mapM, filter)
145+import Prelude hiding
146+ ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence
147+ , reverse, foldr1 , repeat, scanl, scanl1, zipWith)
25148
26149--
27150-- $setup
28151-- >>> :m
152+-- >>> import Control.Concurrent (threadDelay)
153+-- >>> import Control.Monad.Trans (lift)
154+-- >>> import Control.Monad.Trans.Identity (runIdentityT)
155+-- >>> import Data.Function ((&))
156+-- >>> import Data.Maybe (fromJust, isJust)
157+-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
158+-- >>> import Streamly.Internal.Data.Stream as Stream
159+-- >>> import qualified Streamly.Data.Fold as Fold
29160-- >>> import qualified Streamly.Internal.Data.Fold as Fold
30−-- >>> import qualified Streamly.Data.Unfold as Unfold
31−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
161+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
162+-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
163+--
164+-- >>> hSetBuffering stdout LineBuffering
165+
166+-- XXX because of the use of D.cons for appending, folds and scans have
167+-- quadratic complexity when iterated over a stream. We should use StreamK for
168+-- linear performance on iteration.
169+
170+------------------------------------------------------------------------------
171+-- Piping
172+------------------------------------------------------------------------------
173+
174+-- | Use a 'Pipe' to transform a stream.
175+--
176+-- /Pre-release/
177+--
178+{-# INLINE transform #-}
179+transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b
180+transform pipe xs = fromStreamD $ D.transform pipe (toStreamD xs)
181+
182+------------------------------------------------------------------------------
183+-- Transformation Folds
184+------------------------------------------------------------------------------
185+
186+-- | Right fold to a streaming monad.
187+--
188+-- > foldrS Stream.cons Stream.nil === id
189+--
190+-- 'foldrS' can be used to perform stateless stream to stream transformations
191+-- like map and filter in general. It can be coupled with a scan to perform
192+-- stateful transformations. However, note that the custom map and filter
193+-- routines can be much more efficient than this due to better stream fusion.
194+--
195+-- >>> input = Stream.unfold Unfold.fromList [1..5]
196+-- >>> Stream.fold Fold.toList $ Stream.foldrS Stream.cons Stream.nil input
197+-- [1,2,3,4,5]
198+--
199+-- Find if any element in the stream is 'True':
200+--
201+-- >>> step x xs = if odd x then return True else xs
202+-- >>> input = Stream.unfold Unfold.fromList (2:4:5:undefined) :: Stream IO Int
203+-- >>> Stream.fold Fold.toList $ Stream.foldrS step (return False) input
204+-- [True]
205+--
206+-- Map (+2) on odd elements and filter out the even elements:
207+--
208+-- >>> step x xs = if odd x then (x + 2) `Stream.cons` xs else xs
209+-- >>> input = Stream.unfold Unfold.fromList [1..5] :: Stream IO Int
210+-- >>> Stream.fold Fold.toList $ Stream.foldrS step Stream.nil input
211+-- [3,5,7]
212+--
213+-- /Pre-release/
214+{-# INLINE foldrS #-}
215+foldrS ::
216+ (a -> Stream m b -> Stream m b)
217+ -> Stream m b
218+ -> Stream m a
219+ -> Stream m b
220+foldrS f z xs =
221+ fromStreamK
222+ $ K.foldrS
223+ (\y ys -> toStreamK $ f y (fromStreamK ys))
224+ (toStreamK z)
225+ (toStreamK xs)
226+
227+-- | Right fold to a transformer monad. This is the most general right fold
228+-- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'
229+-- implementation can be more efficient:
230+--
231+-- >>> foldrS = foldrT
232+--
233+-- >>> step f x xs = lift $ f x (runIdentityT xs)
234+-- >>> foldrM f z s = runIdentityT $ foldrT (step f) (lift z) s
235+--
236+-- 'foldrT' can be used to translate streamly streams to other transformer
237+-- monads e.g. to a different streaming type.
238+--
239+-- /Pre-release/
240+{-# INLINE foldrT #-}
241+foldrT :: (Monad m, Monad (s m), MonadTrans s)
242+ => (a -> s m b -> s m b) -> s m b -> Stream m a -> s m b
243+foldrT f z s = D.foldrT f z (toStreamD s)
32244
33245------------------------------------------------------------------------------
34246-- Transformation by Mapping
35247------------------------------------------------------------------------------
36248
37249-- |
38−-- >> mapM f = Stream.sequence . Stream.map f
250+-- >>> mapM f = Stream.sequence . fmap f
39251--
40252-- Apply a monadic function to each element of the stream and replace it with
41253-- the output of the resulting action.
@@ -48,40 +260,631 @@ import Prelude hiding (map, mapM, filter)
48260--
49261{-# INLINE mapM #-}
50262mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
51−mapM f = Stream.fromStreamD . StreamD.mapM f . Stream.toStreamD
263+mapM f m = fromStreamK $ D.toStreamK $ D.mapM f $ toStreamD m
52264
53265-- |
54−-- >>> map = fmap
266+-- sequence = Stream.mapM id
267+--
268+-- Replace the elements of a stream of monadic actions with the outputs of
269+-- those actions.
270+--
271+-- >>> s = Stream.unfold Unfold.fromList [putStr "a", putStr "b", putStrLn "c"]
272+-- >>> Stream.fold Fold.drain $ Stream.sequence s
273+-- abc
274+--
275+{-# INLINE sequence #-}
276+sequence :: Monad m => Stream m (m a) -> Stream m a
277+sequence = mapM id
278+
279+------------------------------------------------------------------------------
280+-- Mapping side effects
281+------------------------------------------------------------------------------
282+
283+-- | Tap the data flowing through a stream into a 'Fold'. For example, you may
284+-- add a tap to log the contents flowing through the stream. The fold is used
285+-- only for effects, its result is discarded.
286+--
287+-- @
288+-- Fold m a b
289+-- |
290+-- -----stream m a ---------------stream m a-----
291+--
292+-- @
293+--
294+-- >>> s = Stream.unfold Unfold.enumerateFromTo (1, 2)
295+-- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainBy print) s
296+-- 1
297+-- 2
298+--
299+-- Compare with 'trace'.
300+--
301+{-# INLINE tap #-}
302+tap :: Monad m => FL.Fold m a b -> Stream m a -> Stream m a
303+tap f xs = fromStreamD $ D.tap f (toStreamD xs)
304+
305+-- | Apply a monadic function to each element flowing through the stream and
306+-- discard the results.
55307--
56−-- Same as 'fmap'.
308+-- >>> s = Stream.unfold Unfold.enumerateFromTo (1, 2)
309+-- >>> Stream.fold Fold.drain $ Stream.trace print s
310+-- 1
311+-- 2
57312--
58−-- >>> s = Stream.unfold Unfold.fromList [1,2,3]
59−-- >>> Stream.fold Fold.toList $ Stream.map (+1) s
60−-- [2,3,4]
313+-- Compare with 'tap'.
314+--
315+{-# INLINE trace #-}
316+trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a
317+trace f = mapM (\x -> void (f x) >> return x)
318+
319+-- | Perform a side effect before yielding each element of the stream and
320+-- discard the results.
321+--
322+-- >>> s = Stream.unfold Unfold.enumerateFromTo (1, 2)
323+-- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s
324+-- "got here"
325+-- "got here"
326+--
327+-- Same as 'interspersePrefix_' but always serial.
328+--
329+-- See also: 'trace'
330+--
331+-- /Pre-release/
332+{-# INLINE trace_ #-}
333+trace_ :: Monad m => m b -> Stream m a -> Stream m a
334+trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD
335+
336+------------------------------------------------------------------------------
337+-- Scanning with a Fold
338+------------------------------------------------------------------------------
339+
340+-- XXX It may be useful to have a version of scan where we can keep the
341+-- accumulator independent of the value emitted. So that we do not necessarily
342+-- have to keep a value in the accumulator which we are not using. We can pass
343+-- an extraction function that will take the accumulator and the current value
344+-- of the element and emit the next value in the stream. That will also make it
345+-- possible to modify the accumulator after using it. In fact, the step function
346+-- can return new accumulator and the value to be emitted. The signature would
347+-- be more like mapAccumL.
348+
349+-- | Strict left scan. Scan a stream using the given monadic fold.
350+--
351+-- >>> s = Stream.unfold Unfold.fromList [1..10]
352+-- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s
353+-- [0,1,3,6]
354+--
355+-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)
356+--
357+-- Like 'map', 'scanl'' too is a one to one transformation,
358+-- however it adds an extra element.
359+--
360+-- >>> s = Stream.unfold Unfold.fromList [1,2,3,4]
361+-- >>> Stream.fold Fold.toList $ scanl' (+) 0 s
362+-- [0,1,3,6,10]
363+--
364+-- >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s
365+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
366+--
367+-- The output of 'scanl'' is the initial value of the accumulator followed by
368+-- all the intermediate steps and the final result of 'foldl''.
369+--
370+-- By streaming the accumulated state after each fold step, we can share the
371+-- state across multiple stages of stream composition. Each stage can modify or
372+-- extend the state, do some processing with it and emit it for the next stage,
373+-- thus modularizing the stream processing. This can be useful in
374+-- stateful or event-driven programming.
375+--
376+-- Consider the following monolithic example, computing the sum and the product
377+-- of the elements in a stream in one go using a @foldl'@:
378+--
379+-- >>> foldl' step z = Stream.fold (Fold.foldl' step z)
380+-- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s
381+-- (10,24)
382+--
383+-- Using @scanl'@ we can make it modular by computing the sum in the first
384+-- stage and passing it down to the next stage for computing the product:
385+--
386+-- >>> :{
387+-- foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)
388+-- $ scanl' (\(s, _) x -> (s + x, x)) (0,1)
389+-- $ Stream.unfold Unfold.fromList [1,2,3,4]
390+-- :}
391+-- (10,24)
392+--
393+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
394+-- lazy expressions inside the accumulator, it is recommended that a strict
395+-- data structure is used for accumulator.
396+--
397+-- See also: 'usingStateT'
398+--
399+{-# INLINE scan #-}
400+scan :: Monad m => Fold m a b -> Stream m a -> Stream m b
401+scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
402+
403+-- | Like 'scan' but restarts scanning afresh when the scanning fold
404+-- terminates.
61405--
62406-- /Pre-release/
407+{-# INLINE scanMany #-}
408+scanMany :: Monad m => Fold m a b -> Stream m a -> Stream m b
409+scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m
410+
411+------------------------------------------------------------------------------
412+-- Filtering
413+------------------------------------------------------------------------------
414+
415+-- | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a
416+-- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a
417+-- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with
418+-- index or time.
63419--
64−{-# INLINE map #-}
65−map :: Monad m => (a -> b) -> Stream m a -> Stream m b
66−map f = mapM (return . f)
420+-- >>> filterWithIndex = Stream.with Stream.indexed Stream.filter
421+--
422+-- /Pre-release/
423+{-# INLINE with #-}
424+with :: Monad m =>
425+ (Stream m a -> Stream m (s, a))
426+ -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))
427+ -> (((s, a) -> b) -> Stream m a -> Stream m a)
428+with f comb g = fmap snd . comb g . f
67429
68430-- | Include only those elements that pass a predicate.
69431--
70432{-# INLINE filter #-}
71433filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
72−filter p = Stream.fromStreamD . StreamD.filter p . Stream.toStreamD
434+filter p m = fromStreamD $ D.filter p $ toStreamD m
435+
436+-- | Same as 'filter' but with a monadic predicate.
437+--
438+{-# INLINE filterM #-}
439+filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
440+filterM p m = fromStreamD $ D.filterM p $ toStreamD m
73441
74442-- | Use a filtering fold on a stream.
75443--
76−-- >>> :{
77−-- Stream.fold Fold.sum
78−-- $ Stream.foldFilter (Fold.maybe (\x -> if x > 5 then Just x else Nothing))
79−-- $ Stream.unfold Unfold.fromList [1..10]
80−-- :}
81−--40
444+-- >>> input = Stream.unfold Unfold.fromList [1..10]
445+-- >>> Stream.fold Fold.sum $ Stream.foldFilter (Fold.satisfy (> 5)) input
446+-- 40
82447--
83448-- /Pre-release/
84449--
85450{-# INLINE foldFilter #-}
86451foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
87−foldFilter p = Stream.fromStreamD . StreamD.foldFilter p . Stream.toStreamD
452+foldFilter p = fromStreamD . D.foldFilter p . toStreamD
453+
454+-- | Drop repeated elements that are adjacent to each other using the supplied
455+-- comparison function.
456+--
457+-- >>> uniq = Stream.uniqBy (==)
458+--
459+-- To strip duplicate path separators:
460+--
461+-- >>> input = Stream.unfold Unfold.fromList "//a//b"
462+-- >>> f x y = x == '/' && y == '/'
463+-- >>> Stream.fold Fold.toList $ Stream.uniqBy f input
464+-- "/a/b"
465+--
466+-- Space: @O(1)@
467+--
468+-- See also: 'nubBy'.
469+--
470+-- /Pre-release/
471+--
472+{-# INLINE uniqBy #-}
473+uniqBy :: Monad m =>
474+ (a -> a -> Bool) -> Stream m a -> Stream m a
475+uniqBy eq = catMaybes . rollingMap f
476+
477+ where
478+
479+ f pre curr =
480+ case pre of
481+ Nothing -> Just curr
482+ Just x -> if x `eq` curr then Nothing else Just curr
483+
484+-- | Drop repeated elements that are adjacent to each other.
485+--
486+{-# INLINE uniq #-}
487+uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
488+uniq = fromStreamD . D.uniq . toStreamD
489+
490+-- | Strip all leading and trailing occurrences of an element passing a
491+-- predicate and make all other consecutive occurrences uniq.
492+--
493+-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)
494+--
495+-- @
496+-- > Stream.prune isSpace (Stream.fromList " hello world! ")
497+-- "hello world!"
498+--
499+-- @
500+--
501+-- Space: @O(1)@
502+--
503+-- /Unimplemented/
504+{-# INLINE prune #-}
505+prune ::
506+ -- (Monad m, Eq a) =>
507+ (a -> Bool) -> Stream m a -> Stream m a
508+prune = error "Not implemented yet!"
509+
510+-- Possible implementation:
511+-- @repeated =
512+-- Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@
513+--
514+-- 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a
515+-- single element.
516+
517+-- | Emit only repeated elements, once.
518+--
519+-- /Unimplemented/
520+repeated :: -- (Monad m, Eq a) =>
521+ Stream m a -> Stream m a
522+repeated = undefined
523+
524+-- We can have more efficient implementations for nubOrd and nubInt by using
525+-- Set and IntSet to find/remove duplication. For Hashable we can use a
526+-- hashmap. Use rewrite rules to specialize to more efficient impls.
527+
528+-- | Drop repeated elements anywhere in the stream.
529+--
530+-- /Caution: not scalable for infinite streams/
531+--
532+-- /Unimplemented/
533+--
534+{-# INLINE nubBy #-}
535+nubBy :: -- Monad m =>
536+ (a -> a -> Bool) -> Stream m a -> Stream m a
537+nubBy = undefined -- fromStreamD . D.nubBy . toStreamD
538+
539+-- | Deletes the first occurrence of the element in the stream that satisfies
540+-- the given equality predicate.
541+--
542+-- >>> input = Stream.unfold Unfold.fromList [1,3,3,5]
543+-- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input
544+-- [1,3,5]
545+--
546+{-# INLINE deleteBy #-}
547+deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
548+deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
549+
550+------------------------------------------------------------------------------
551+-- Trimming
552+------------------------------------------------------------------------------
553+
554+-- | Same as 'takeWhile' but with a monadic predicate.
555+--
556+{-# INLINE takeWhileM #-}
557+takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
558+takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
559+
560+-- | Drop elements in the stream as long as the predicate succeeds and then
561+-- take the rest of the stream.
562+--
563+{-# INLINE dropWhile #-}
564+dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
565+dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m
566+
567+-- | Same as 'dropWhile' but with a monadic predicate.
568+--
569+{-# INLINE dropWhileM #-}
570+dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
571+dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
572+
573+------------------------------------------------------------------------------
574+-- Inserting Elements
575+------------------------------------------------------------------------------
576+
577+-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
578+-- @stream@ that is less than @elem@ when compared using @cmp@.
579+--
580+-- >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)
581+--
582+-- >>> input = Stream.unfold Unfold.fromList [1,3,5]
583+-- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input
584+-- [1,2,3,5]
585+--
586+{-# INLINE insertBy #-}
587+insertBy ::Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
588+insertBy cmp x m = fromStreamD $ D.insertBy cmp x (toStreamD m)
589+
590+-- | Insert a pure value between successive elements of a stream.
591+--
592+-- >>> input = Stream.unfold Unfold.fromList "hello"
593+-- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input
594+-- "h,e,l,l,o"
595+--
596+{-# INLINE intersperse #-}
597+intersperse :: Monad m => a -> Stream m a -> Stream m a
598+intersperse a = fromStreamD . D.intersperse a . toStreamD
599+
600+-- | Insert a side effect before consuming an element of a stream except the
601+-- first one.
602+--
603+-- >>> input = Stream.unfold Unfold.fromList "hello"
604+-- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input
605+-- h.e.l.l.o
606+--
607+-- /Pre-release/
608+{-# INLINE intersperseM_ #-}
609+intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a
610+intersperseM_ m = fromStreamD . D.intersperseM_ m . toStreamD
611+
612+-- | Intersperse a monadic action into the input stream after every @n@
613+-- elements.
614+--
615+-- >> input = Stream.unfold Unfold.fromList "hello"
616+-- >> Stream.fold Fold.toList $ Stream.intersperseBySpan 2 (return ',') input
617+-- "he,ll,o"
618+--
619+-- /Unimplemented/
620+{-# INLINE intersperseBySpan #-}
621+intersperseBySpan :: -- Monad m =>
622+ Int -> m a -> t m a -> t m a
623+intersperseBySpan _n _f _xs = undefined
624+
625+-- | Insert an effect and its output after consuming an element of a stream.
626+--
627+-- >>> input = Stream.unfold Unfold.fromList "hello"
628+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ intersperseSuffix (putChar '.' >> return ',') input
629+-- h.,e.,l.,l.,o.,"h,e,l,l,o,"
630+--
631+-- /Pre-release/
632+{-# INLINE intersperseSuffix #-}
633+intersperseSuffix :: Monad m => m a -> Stream m a -> Stream m a
634+intersperseSuffix m = fromStreamD . D.intersperseSuffix m . toStreamD
635+
636+-- | Insert a side effect after consuming an element of a stream.
637+--
638+-- >>> input = Stream.unfold Unfold.fromList "hello"
639+-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffix_ (threadDelay 1000000) input
640+-- "hello"
641+--
642+-- /Pre-release/
643+--
644+{-# INLINE intersperseSuffix_ #-}
645+intersperseSuffix_ :: Monad m => m b -> Stream m a -> Stream m a
646+intersperseSuffix_ m = fromStreamD . D.intersperseSuffix_ m . toStreamD
647+
648+-- XXX Use an offset argument, like tapOffsetEvery
649+
650+-- | Like 'intersperseSuffix' but intersperses an effectful action into the
651+-- input stream after every @n@ elements and after the last element.
652+--
653+-- >>> input = Stream.unfold Unfold.fromList "hello"
654+-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffixBySpan 2 (return ',') input
655+-- "he,ll,o,"
656+--
657+-- /Pre-release/
658+--
659+{-# INLINE intersperseSuffixBySpan #-}
660+intersperseSuffixBySpan :: Monad m
661+ => Int -> m a -> Stream m a -> Stream m a
662+intersperseSuffixBySpan n eff =
663+ fromStreamD . D.intersperseSuffixBySpan n eff . toStreamD
664+
665+-- | Insert a side effect before consuming an element of a stream.
666+--
667+-- >>> input = Stream.unfold Unfold.fromList "hello"
668+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.interspersePrefix_ (putChar '.' >> return ',') input
669+-- .h.e.l.l.o"hello"
670+--
671+-- Same as 'trace_'.
672+--
673+-- /Pre-release/
674+--
675+{-# INLINE interspersePrefix_ #-}
676+interspersePrefix_ :: Monad m => m b -> Stream m a -> Stream m a
677+interspersePrefix_ m = mapM (\x -> void m >> return x)
678+
679+------------------------------------------------------------------------------
680+-- Inserting Time
681+------------------------------------------------------------------------------
682+
683+-- | Introduce a delay of specified seconds between elements of the stream.
684+--
685+-- >>> input = Stream.unfold Unfold.enumerateFromTo (1, 3)
686+-- >>> Stream.fold (Fold.drainBy print) $ Stream.delay 1 input
687+-- 1
688+-- 2
689+-- 3
690+--
691+{-# INLINE delay #-}
692+delay :: MonadIO m => Double -> Stream m a -> Stream m a
693+delay n = intersperseM_ $ liftIO $ threadDelay $ round $ n * 1000000
694+
695+-- | Introduce a delay of specified seconds after consuming an element of a
696+-- stream.
697+--
698+-- >>> input = Stream.unfold Unfold.enumerateFromTo (1, 3)
699+-- >>> Stream.fold (Fold.drainBy print) $ Stream.delayPost 1 input
700+-- 1
701+-- 2
702+-- 3
703+--
704+-- /Pre-release/
705+--
706+{-# INLINE delayPost #-}
707+delayPost :: MonadIO m => Double -> Stream m a -> Stream m a
708+delayPost n = intersperseSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000
709+
710+-- | Introduce a delay of specified seconds before consuming an element of a
711+-- stream.
712+--
713+-- >>> input = Stream.unfold Unfold.enumerateFromTo (1, 3)
714+-- >>> Stream.fold (Fold.drainBy print) $ Stream.delayPre 1 input
715+-- 1
716+-- 2
717+-- 3
718+--
719+-- /Pre-release/
720+--
721+{-# INLINE delayPre #-}
722+delayPre :: MonadIO m => Double -> Stream m a -> Stream m a
723+delayPre n = trace_ $ liftIO $ threadDelay $ round $ n * 1000000
724+
725+------------------------------------------------------------------------------
726+-- Reorder in sequence
727+------------------------------------------------------------------------------
728+
729+-- | Buffer until the next element in sequence arrives. The function argument
730+-- determines the difference in sequence numbers. This could be useful in
731+-- implementing sequenced streams, for example, TCP reassembly.
732+--
733+-- /Unimplemented/
734+--
735+{-# INLINE reassembleBy #-}
736+reassembleBy
737+ :: -- Monad m =>
738+ Fold m a b
739+ -> (a -> a -> Int)
740+ -> t m a
741+ -> t m b
742+reassembleBy = undefined
743+
744+------------------------------------------------------------------------------
745+-- Position Indexing
746+------------------------------------------------------------------------------
747+
748+-- |
749+-- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
750+-- >>> indexed = Stream.postscan f
751+-- >>> indexed = Stream.zipWith (,) (Stream.unfold Unfold.enumerateFrom 0)
752+--
753+-- Pair each element in a stream with its index, starting from index 0.
754+--
755+-- >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.unfold Unfold.fromList "hello"
756+-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
757+--
758+{-# INLINE indexed #-}
759+indexed :: Monad m => Stream m a -> Stream m (Int, a)
760+indexed = fromStreamD . D.indexed . toStreamD
761+
762+-- |
763+-- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
764+-- >>> indexedR n = Stream.postscan (f n)
765+--
766+-- >>> s n = Stream.unfold Unfold.enumerateFromThen (n, (n - 1))
767+-- >>> indexedR n = Stream.zipWith (,) (s n)
768+--
769+-- Pair each element in a stream with its index, starting from the
770+-- given index @n@ and counting down.
771+--
772+-- >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.unfold Unfold.fromList "hello"
773+-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
774+--
775+{-# INLINE indexedR #-}
776+indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
777+indexedR n = fromStreamD . D.indexedR n . toStreamD
778+
779+------------------------------------------------------------------------------
780+-- Searching
781+------------------------------------------------------------------------------
782+
783+-- | Find all the indices where the value of the element in the stream is equal
784+-- to the given value.
785+--
786+-- >>> elemIndices a = Stream.findIndices (== a)
787+--
788+{-# INLINE elemIndices #-}
789+elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
790+elemIndices a = findIndices (== a)
791+
792+------------------------------------------------------------------------------
793+-- Rolling map
794+------------------------------------------------------------------------------
795+
796+-- XXX this is not a one-to-one map so calling it map may not be right.
797+-- We can perhaps call it zipWithTail or rollWith.
798+
799+-- | Apply a function on every two successive elements of a stream. The first
800+-- argument of the map function is the previous element and the second argument
801+-- is the current element. When the current element is the first element, the
802+-- previous element is 'Nothing'.
803+--
804+-- /Pre-release/
805+--
806+{-# INLINE rollingMap #-}
807+rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
808+rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
809+
810+-- | Like 'rollingMap' but with an effectful map function.
811+--
812+-- /Pre-release/
813+--
814+{-# INLINE rollingMapM #-}
815+rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
816+rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
817+
818+-- | Like 'rollingMap' but requires at least two elements in the stream,
819+-- returns an empty stream otherwise.
820+--
821+-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.
822+--
823+-- /Pre-release/
824+--
825+{-# INLINE rollingMap2 #-}
826+rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b
827+rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m
828+
829+------------------------------------------------------------------------------
830+-- Maybe Streams
831+------------------------------------------------------------------------------
832+
833+-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
834+-- elements, and return a stream of values extracted from 'Just'.
835+--
836+-- Equivalent to:
837+--
838+-- >>> mapMaybe f = fmap fromJust . Stream.filter isJust . fmap f
839+--
840+{-# INLINE mapMaybe #-}
841+mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
842+mapMaybe f m = fromStreamD $ D.mapMaybe f $ toStreamD m
843+
844+-- | Like 'mapMaybe' but maps a monadic function.
845+--
846+-- Equivalent to:
847+--
848+-- >>> mapMaybeM f = fmap fromJust . Stream.filter isJust . Stream.mapM f
849+--
850+{-# INLINE_EARLY mapMaybeM #-}
851+mapMaybeM :: Monad m
852+ => (a -> m (Maybe b)) -> Stream m a -> Stream m b
853+mapMaybeM f = fmap fromJust . filter isJust . mapM f
854+
855+-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
856+--
857+-- /Pre-release/
858+--
859+{-# INLINE catMaybes #-}
860+catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
861+catMaybes = fmap fromJust . filter isJust
862+
863+------------------------------------------------------------------------------
864+-- Either streams
865+------------------------------------------------------------------------------
866+
867+-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
868+--
869+-- /Pre-release/
870+--
871+{-# INLINE lefts #-}
872+lefts :: Monad m => Stream m (Either a b) -> Stream m a
873+lefts = fmap (fromLeft undefined) . filter isLeft
874+
875+-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
876+--
877+-- /Pre-release/
878+--
879+{-# INLINE rights #-}
880+rights :: Monad m => Stream m (Either a b) -> Stream m b
881+rights = fmap (fromRight undefined) . filter isRight
882+
883+-- | Remove the either wrapper and flatten both lefts and as well as rights in
884+-- the output stream.
885+--
886+-- /Pre-release/
887+--
888+{-# INLINE both #-}
889+both :: Monad m => Stream m (Either a a) -> Stream m a
890+both = fmap (either id id)
core/src/Streamly/Internal/Data/Stream/Type.hs+166−11
@@ -10,11 +10,30 @@
1010--
1111module Streamly.Internal.Data.Stream.Type
1212 (
13+ -- * Stream Type
1314 Stream
15+
16+ -- * Type Conversion
1417 , fromStreamK
1518 , toStreamK
1619 , fromStreamD
1720 , toStreamD
21+
22+ -- * Construction
23+ , cons
24+ , consM
25+ , nil
26+ , nilM
27+ , fromPure
28+ , fromEffect
29+
30+ -- * Bind/Concat
31+ , bindWith
32+ , concatMapWith
33+
34+ -- * Double folds
35+ , eqBy
36+ , cmpBy
1837 )
1938where
2039
@@ -49,7 +68,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
4968
5069-- $setup
5170-- >>> import qualified Streamly.Data.Fold as Fold
52−-- >>> import qualified Streamly.Data.Unfold as Unfold
71+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
5372-- >>> import qualified Streamly.Internal.Data.Stream as Stream
5473
5574------------------------------------------------------------------------------
@@ -58,12 +77,12 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
5877
5978-- | Semigroup instance appends two streams:
6079--
61−-- > (<>) = Stream.append
80+-- >>> (<>) = Stream.append
6281--
6382-- Monad bind maps a stream generator function on the stream and flattens the
6483-- resulting stream:
6584--
66−-- > (>>=) = flip . Stream.concatMapWith Stream.append
85+-- >>> (>>=) = flip (Stream.concatMapWith Stream.append)
6786--
6887-- A 'Monad' bind behaves like a @for@ loop:
6988--
@@ -101,13 +120,34 @@ fromStreamK = Stream
101120toStreamK :: Stream m a -> K.Stream m a
102121toStreamK (Stream k) = k
103122
123+{-# INLINE fromStreamD #-}
124+fromStreamD :: Monad m => D.Stream m a -> Stream m a
125+fromStreamD = fromStreamK . D.toStreamK
126+
104127{-# INLINE toStreamD #-}
105128toStreamD :: Applicative m => Stream m a -> D.Stream m a
106129toStreamD = D.fromStreamK . toStreamK
107130
108−{-# INLINE fromStreamD #-}
109−fromStreamD :: Monad m => D.Stream m a -> Stream m a
110−fromStreamD = fromStreamK . D.toStreamK
131+------------------------------------------------------------------------------
132+-- Comparison
133+------------------------------------------------------------------------------
134+
135+-- | Compare two streams for equality
136+--
137+-- @since 0.9.0
138+{-# INLINE eqBy #-}
139+eqBy :: Monad m =>
140+ (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
141+eqBy f m1 m2 = D.eqBy f (toStreamD m1) (toStreamD m2)
142+
143+-- | Compare two streams
144+--
145+-- @since 0.9.0
146+{-# INLINE cmpBy #-}
147+cmpBy
148+ :: Monad m
149+ => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
150+cmpBy f m1 m2 = D.cmpBy f (toStreamD m1) (toStreamD m2)
111151
112152------------------------------------------------------------------------------
113153-- Monad
@@ -127,7 +167,7 @@ instance Monad m => Monad (Stream m) where
127167 -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
128168 --
129169 {-# INLINE (>>=) #-}
130− (>>=) (Stream m) f = Stream $ K.bindWith K.serial m (toStreamK . f)
170+ (>>=) m f = fromStreamK $ K.bindWith K.serial (toStreamK m) (toStreamK . f)
131171
132172 {-# INLINE (>>) #-}
133173 (>>) = (*>)
@@ -139,23 +179,23 @@ instance Monad m => Monad (Stream m) where
139179{-# INLINE apSerial #-}
140180apSerial :: Monad m => Stream m (a -> b) -> Stream m a -> Stream m b
141181apSerial (Stream m1) (Stream m2) =
142− Stream $ D.toStreamK $ D.fromStreamK m1 <*> D.fromStreamK m2
182+ fromStreamK $ D.toStreamK $ D.fromStreamK m1 <*> D.fromStreamK m2
143183
144184{-# INLINE apSequence #-}
145185apSequence :: Monad m => Stream m a -> Stream m b -> Stream m b
146186apSequence (Stream m1) (Stream m2) =
147− Stream $ D.toStreamK $ D.fromStreamK m1 *> D.fromStreamK m2
187+ fromStreamK $ D.toStreamK $ D.fromStreamK m1 *> D.fromStreamK m2
148188
149189{-# INLINE apDiscardSnd #-}
150190apDiscardSnd :: Monad m => Stream m a -> Stream m b -> Stream m a
151191apDiscardSnd (Stream m1) (Stream m2) =
152− Stream $ D.toStreamK $ D.fromStreamK m1 <* D.fromStreamK m2
192+ fromStreamK $ D.toStreamK $ D.fromStreamK m1 <* D.fromStreamK m2
153193
154194-- Note: we need to define all the typeclass operations because we want to
155195-- INLINE them.
156196instance Monad m => Applicative (Stream m) where
157197 {-# INLINE pure #-}
158− pure = Stream . K.fromPure
198+ pure = fromStreamK . K.fromPure
159199
160200 {-# INLINE (<*>) #-}
161201 (<*>) = apSerial
@@ -177,3 +217,118 @@ LIST_INSTANCES(Stream)
177217NFDATA1_INSTANCE(Stream)
178218FOLDABLE_INSTANCE(Stream)
179219TRAVERSABLE_INSTANCE(Stream)
220+
221+-------------------------------------------------------------------------------
222+-- Construction
223+-------------------------------------------------------------------------------
224+
225+infixr 5 `cons`
226+
227+-- | Construct a stream by adding a pure value at the head of an existing
228+-- stream. Same as the following but more efficient:
229+--
230+-- For example:
231+--
232+-- >>> s = 1 `Stream.cons` 2 `Stream.cons` 3 `Stream.cons` Stream.nil
233+-- >>> Stream.fold Fold.toList s
234+-- [1,2,3]
235+--
236+-- >>> cons x xs = return x `Stream.consM` xs
237+--
238+-- /Pre-release/
239+--
240+{-# INLINE_NORMAL cons #-}
241+cons :: a -> Stream m a -> Stream m a
242+cons x = fromStreamK . K.cons x . toStreamK
243+
244+infixr 5 `consM`
245+
246+-- | Constructs a stream by adding a monadic action at the head of an
247+-- existing stream. For example:
248+--
249+-- >>> s = putStrLn "hello" `consM` putStrLn "world" `consM` Stream.nil
250+-- >>> Stream.fold Fold.drain s
251+-- hello
252+-- world
253+--
254+-- >>> consM x xs = Stream.fromEffect x `Stream.append` xs
255+--
256+-- /Pre-release/
257+--
258+{-# INLINE consM #-}
259+{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
260+consM :: Monad m => m a -> Stream m a -> Stream m a
261+consM m = fromStreamK . K.consM m . toStreamK
262+
263+-- | A pure empty stream with no result and no side-effect.
264+--
265+-- >>> Stream.fold Fold.toList Stream.nil
266+-- []
267+--
268+{-# INLINE_NORMAL nil #-}
269+nil :: Stream m a
270+nil = fromStreamK K.nil
271+
272+-- | An empty stream producing the supplied side effect.
273+--
274+-- >>> Stream.fold Fold.toList (Stream.nilM (print "nil"))
275+-- "nil"
276+-- []
277+--
278+-- /Pre-release/
279+{-# INLINE_NORMAL nilM #-}
280+nilM :: Monad m => m b -> Stream m a
281+nilM = fromStreamK . K.nilM
282+
283+-- | Create a singleton stream from a pure value.
284+--
285+-- >>> fromPure a = a `cons` Stream.nil
286+-- >>> fromPure = pure
287+-- >>> fromPure = fromEffect . pure
288+--
289+{-# INLINE_NORMAL fromPure #-}
290+fromPure :: a -> Stream m a
291+fromPure = fromStreamK . K.fromPure
292+
293+-- | Create a singleton stream from a monadic action.
294+--
295+-- >>> fromEffect m = m `consM` Stream.nil
296+--
297+-- >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")
298+-- hello
299+--
300+{-# INLINE_NORMAL fromEffect #-}
301+fromEffect :: Monad m => m a -> Stream m a
302+fromEffect = fromStreamK . K.fromEffect
303+
304+-------------------------------------------------------------------------------
305+-- Bind/Concat
306+-------------------------------------------------------------------------------
307+
308+{-# INLINE bindWith #-}
309+bindWith
310+ :: (Stream m b -> Stream m b -> Stream m b)
311+ -> Stream m a
312+ -> (a -> Stream m b)
313+ -> Stream m b
314+bindWith par m1 f =
315+ fromStreamK
316+ $ K.bindWith
317+ (\s1 s2 -> toStreamK $ par (fromStreamK s1) (fromStreamK s2))
318+ (toStreamK m1)
319+ (toStreamK . f)
320+
321+-- | @concatMapWith mixer generator stream@ is a two dimensional looping
322+-- combinator. The @generator@ function is used to generate streams from the
323+-- elements in the input @stream@ and the @mixer@ function is used to merge
324+-- those streams.
325+--
326+-- Note we can merge streams concurrently by using a concurrent merge function.
327+--
328+{-# INLINE concatMapWith #-}
329+concatMapWith
330+ :: (Stream m b -> Stream m b -> Stream m b)
331+ -> (a -> Stream m b)
332+ -> Stream m a
333+ -> Stream m b
334+concatMapWith par f xs = bindWith par xs f
core/src/Streamly/Internal/Data/Stream/Zip.hs−7
@@ -53,13 +53,6 @@ import Prelude hiding (map, repeat, zipWith)
5353
5454-- $setup
5555-- >>> import qualified Streamly.Prelude as Stream
56−-- >>> import Control.Concurrent (threadDelay)
57−-- >>> :{
58−-- delay n = do
59−-- threadDelay (n * 1000000) -- sleep for n seconds
60−-- putStrLn (show n ++ " sec") -- print "n sec"
61−-- return n -- IO Int
62−-- :}
6356
6457{-# INLINE zipWithMK #-}
6558zipWithMK :: Monad m =>
core/src/Streamly/Internal/Data/Unfold/Type.hs+4
@@ -308,6 +308,7 @@ instance Functor m => Functor (Unfold m a) where
308308-- Applicative
309309------------------------------------------------------------------------------
310310
311+-- XXX Shouldn't this be Unfold m (m a) a ?
311312-- | The unfold discards its input and generates a function stream using the
312313-- supplied monadic action.
313314--
@@ -323,6 +324,9 @@ fromEffect m = Unfold step inject
323324 step False = (`Yield` True) <$> m
324325 step True = pure Stop
325326
327+-- XXX Shouldn't this be Unfold m a a ? Which is identity. Should this function
328+-- even exist for Unfolds. Should we have applicative/Monad for unfolds?
329+--
326330-- | Discards the unfold input and always returns the argument of 'fromPure'.
327331--
328332-- > fromPure = fromEffect . pure
core/streamly-core.cabal+5
@@ -303,6 +303,11 @@ library
303303 , Streamly.Internal.Data.Stream.Eliminate
304304 , Streamly.Internal.Data.Stream.Generate
305305 , Streamly.Internal.Data.Stream.Transform
306+ , Streamly.Internal.Data.Stream.Bottom
307+ , Streamly.Internal.Data.Stream.Exception
308+ , Streamly.Internal.Data.Stream.Expand
309+ , Streamly.Internal.Data.Stream.Lift
310+ , Streamly.Internal.Data.Stream.Reduce
306311 , Streamly.Internal.Data.Stream
307312 , Streamly.Internal.Data.Stream.Serial
308313 , Streamly.Internal.Data.Stream.WSerial
streamly.cabal+11−1
@@ -194,8 +194,13 @@ extra-source-files:
194194 core/src/Streamly/Internal/Data/Pipe.hs
195195 core/src/Streamly/Internal/Data/Stream/Serial.hs
196196 core/src/Streamly/Internal/Data/Stream.hs
197+ core/src/Streamly/Internal/Data/Stream/Bottom.hs
197198 core/src/Streamly/Internal/Data/Stream/Eliminate.hs
199+ core/src/Streamly/Internal/Data/Stream/Exception.hs
200+ core/src/Streamly/Internal/Data/Stream/Expand.hs
198201 core/src/Streamly/Internal/Data/Stream/Generate.hs
202+ core/src/Streamly/Internal/Data/Stream/Lift.hs
203+ core/src/Streamly/Internal/Data/Stream/Reduce.hs
199204 core/src/Streamly/Internal/Data/Stream/Transform.hs
200205 core/src/Streamly/Internal/Data/Stream/Type.hs
201206 core/src/Streamly/Internal/Data/Stream/WSerial.hs
@@ -613,9 +618,14 @@ library
613618 , Streamly.Internal.Data.Parser
614619 , Streamly.Internal.Data.Pipe
615620 , Streamly.Internal.Data.Stream.Type
616− , Streamly.Internal.Data.Stream.Eliminate
621+ , Streamly.Internal.Data.Stream.Bottom
617622 , Streamly.Internal.Data.Stream.Generate
623+ , Streamly.Internal.Data.Stream.Eliminate
624+ , Streamly.Internal.Data.Stream.Expand
625+ , Streamly.Internal.Data.Stream.Reduce
618626 , Streamly.Internal.Data.Stream.Transform
627+ , Streamly.Internal.Data.Stream.Exception
628+ , Streamly.Internal.Data.Stream.Lift
619629 , Streamly.Internal.Data.Stream
620630 , Streamly.Internal.Data.Stream.Serial
621631 , Streamly.Internal.Data.Stream.Zip