on master help sign in
Add Data.Stream module for serial streams

This module will replace SerialT type with Stream type. The new serial stream type "Stream m a" has the same underlying representation as "SerialT m a" and the two types can be coerced to each other.

commit4f929f3ed3b6bd4012291be1ca3bcbf2b26e9669 authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateTue 24 May 2022, 17:52 (4 years ago) parent24f3447
12 files changed+3275−118
core/src/Streamly/Internal/Data/Stream.hs+13−5 core/src/Streamly/Internal/Data/Stream/Bottom.hs+402 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+277−10 core/src/Streamly/Internal/Data/Stream/Exception.hs+269 core/src/Streamly/Internal/Data/Stream/Expand.hs+771 core/src/Streamly/Internal/Data/Stream/Generate.hs+4−65 core/src/Streamly/Internal/Data/Stream/Lift.hs+147 core/src/Streamly/Internal/Data/Stream/Reduce.hs+162 core/src/Streamly/Internal/Data/Stream/Transform.hs+922−34 core/src/Streamly/Internal/Data/Stream/Type.hs+288−1 core/streamly-core.cabal+6 streamly.cabal+14−3
core/src/Streamly/Internal/Data/Stream.hs+13−5
@@ -7,14 +7,22 @@
77-- Portability : GHC
88--
99module Streamly.Internal.Data.Stream
10− ( module Streamly.Internal.Data.Stream.Type
11− , module Streamly.Internal.Data.Stream.Eliminate
12− , module Streamly.Internal.Data.Stream.Generate
13− , module Streamly.Internal.Data.Stream.Transform
14− )
10+( module Streamly.Internal.Data.Stream.Type
11+, module Streamly.Internal.Data.Stream.Eliminate
12+, module Streamly.Internal.Data.Stream.Exception
13+, module Streamly.Internal.Data.Stream.Expand
14+, module Streamly.Internal.Data.Stream.Generate
15+, module Streamly.Internal.Data.Stream.Lift
16+, module Streamly.Internal.Data.Stream.Reduce
17+, module Streamly.Internal.Data.Stream.Transform
18+)
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+402
@@ -0,0 +1,402 @@
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+ -- * Elimination
19+ , foldContinue
20+ , foldBreak
21+
22+ -- * Transformation
23+ , map
24+ , scanlMAfter'
25+ , postscanlMAfter'
26+ , postscanlM'
27+ , smapM
28+ -- $smapM_Notes
29+ , take
30+ , takeWhile
31+ , takeEndBy
32+ , drop
33+ , findIndices
34+ , intersperseM
35+ , reverse
36+ , reverse'
37+
38+ -- * Nesting
39+ , concatM
40+ , concatMapM
41+ , concatMap
42+
43+ -- * Zipping
44+ , zipWithM
45+ , zipWith
46+ )
47+where
48+
49+#include "inline.hs"
50+
51+import Control.Monad.IO.Class (MonadIO(..))
52+import Foreign.Storable (Storable)
53+import Streamly.Internal.Data.Fold.Type (Fold (..))
54+import Streamly.Internal.System.IO (defaultChunkSize)
55+
56+import qualified Streamly.Internal.Data.Array.Foreign.Type as A
57+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
58+import qualified Streamly.Internal.Data.Stream.StreamK as K
59+import qualified Streamly.Internal.Data.Stream.StreamD as D
60+
61+import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
62+
63+import Streamly.Internal.Data.Stream.Type
64+
65+--
66+-- $setup
67+-- >>> :m
68+-- >>> import Control.Concurrent (threadDelay)
69+-- >>> import Control.Monad (join)
70+-- >>> import Control.Monad.Trans.Class (lift)
71+-- >>> import Data.Function (fix, (&))
72+-- >>> import Data.Semigroup (cycle1)
73+-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
74+-- >>> import System.IO.Unsafe (unsafePerformIO)
75+-- >>> import Streamly.Internal.Data.Stream as Stream
76+-- >>> import qualified Streamly.Data.Array.Foreign as Array
77+-- >>> import qualified Streamly.Data.Fold as Fold
78+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
79+-- >>> import qualified Streamly.Data.Unfold as Unfold
80+
81+------------------------------------------------------------------------------
82+-- Elimination - Running a Fold
83+------------------------------------------------------------------------------
84+
85+-- | We can create higher order folds using 'foldContinue'. We can fold a
86+-- number of streams to a given fold efficiently with full stream fusion. For
87+-- example, to fold a list of streams on the same sum fold:
88+--
89+-- >>> concatFold = Prelude.foldl Stream.foldContinue Fold.sum
90+--
91+-- >>> fold f = Fold.finish . Stream.foldContinue f
92+--
93+-- /Internal/
94+{-# INLINE foldContinue #-}
95+foldContinue :: Monad m => Fold m a b -> Stream m a -> Fold m a b
96+foldContinue f s = D.foldContinue f $ toStreamD s
97+
98+-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
99+-- expression strictly at each step. The behavior is similar to 'foldl''. A
100+-- 'Fold' can terminate early without consuming the full stream. See the
101+-- documentation of individual 'Fold's for termination behavior.
102+--
103+-- | Like 'fold' but also returns the remaining stream.
104+--
105+-- /Inhibits stream fusion/
106+--
107+{-# INLINE foldBreak #-}
108+foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
109+{-
110+-- XXX This shows quadratic performance when used recursively perhaps because
111+-- of StreamK to StreamD conversions not getting eliminated sue to recursion.
112+foldBreak fl (Stream strm) = fmap f $ D.foldBreak fl $ D.fromStreamK strm
113+
114+ where
115+
116+ f (b, str) = (b, Stream (D.toStreamK str))
117+-}
118+foldBreak fl strm = fmap f $ K.foldBreak fl (toStreamK strm)
119+
120+ where
121+
122+ f (b, str) = (b, fromStreamK str)
123+
124+------------------------------------------------------------------------------
125+-- Transformation
126+------------------------------------------------------------------------------
127+
128+-- |
129+-- @
130+-- map = fmap
131+-- @
132+--
133+-- Same as 'fmap'.
134+--
135+-- @
136+-- > K.toList $ K.map (+1) $ K.fromList [1,2,3]
137+-- [2,3,4]
138+-- @
139+--
140+-- @since 0.4.0
141+{-# INLINE map #-}
142+map :: (Monad m) => (a -> b) -> Stream m a -> Stream m b
143+map f = fromStreamD . D.map f . toStreamD
144+
145+-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
146+-- that it provides an additional @done@ function to be applied on the
147+-- accumulator when the stream stops. The result of @done@ is also emitted in
148+-- the stream.
149+--
150+-- This function can be used to allocate a resource in the beginning of the
151+-- scan and release it when the stream ends or to flush the internal state of
152+-- the scan at the end.
153+--
154+-- /Pre-release/
155+--
156+{-# INLINE scanlMAfter' #-}
157+scanlMAfter' :: (Monad m)
158+ => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b
159+scanlMAfter' step initial done stream =
160+ fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream
161+
162+{-# INLINE postscanlMAfter' #-}
163+postscanlMAfter' :: (Monad m)
164+ => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b
165+postscanlMAfter' step initial done stream =
166+ fromStreamD $ D.postscanlMAfter' step initial done $ toStreamD stream
167+
168+-- XXX this needs to be concurrent
169+-- | Like 'postscanl'' but with a monadic step function and a monadic seed.
170+--
171+-- >>> postscanlM' f z xs = Stream.drop 1 $ Stream.scanlM' f z xs
172+--
173+-- /Since: 0.7.0/
174+--
175+-- /Since: 0.8.0 (signature change)/
176+{-# INLINE postscanlM' #-}
177+postscanlM' :: (Monad m) => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
178+postscanlM' step z m = fromStreamD $ D.postscanlM' step z $ toStreamD m
179+
180+-- $smapM_Notes
181+--
182+-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
183+-- a read-only environment. However, that would just be 'mapM'.
184+--
185+-- The initial action could be @m (s, Maybe b)@, and we can also add a final
186+-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
187+-- functionality and also to flush the state in the end like scanlMAfter'.
188+-- We can also use it along with a fusible version of bracket to get
189+-- scanlMAfter' like functionality. See issue #677.
190+--
191+-- This can be further generalized to a type similar to Fold/Parser, giving it
192+-- filtering and parsing capability as well (this is in fact equivalent to
193+-- parseMany):
194+--
195+-- smapM :: (s -> a -> m (Step s b)) -> m s -> Stream m a -> Stream m b
196+--
197+
198+-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
199+-- Hopefully, this is a better alternative to @scan@. Separation of state from
200+-- the output makes it easier to think in terms of a shared state, and also
201+-- makes it easier to keep the state fully strict and the output lazy.
202+--
203+-- See also: 'scanlM''
204+--
205+-- /Pre-release/
206+--
207+{-# INLINE smapM #-}
208+smapM :: (Monad m) =>
209+ (s -> a -> m (s, b))
210+ -> m s
211+ -> Stream m a
212+ -> Stream m b
213+smapM step initial stream =
214+ -- XXX implement this directly instead of using scanlM'
215+ -- Once we have postscanlM' with monadic initial we can use this code
216+ -- let r = postscanlM'
217+ -- (\(s, _) a -> step s a)
218+ -- (fmap (,undefined) initial)
219+ -- stream
220+ let r = postscanlM'
221+ (\(s, _) a -> step s a)
222+ (fmap (,undefined) initial)
223+ stream
224+ in map snd r
225+
226+------------------------------------------------------------------------------
227+-- Transformation - Trimming
228+------------------------------------------------------------------------------
229+
230+-- | Take first 'n' elements from the stream and discard the rest.
231+--
232+-- @since 0.1.0
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+-- @since 0.1.0
240+{-# INLINE takeWhile #-}
241+takeWhile :: (Monad m) => (a -> Bool) -> Stream m a -> Stream m a
242+takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m
243+
244+{-# INLINE takeEndBy #-}
245+takeEndBy :: (Monad m) => (a -> Bool) -> Stream m a -> Stream m a
246+takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
247+
248+-- | Discard first 'n' elements from the stream and take the rest.
249+--
250+-- @since 0.1.0
251+{-# INLINE drop #-}
252+drop :: (Monad m) => Int -> Stream m a -> Stream m a
253+drop n m = fromStreamD $ D.drop n $ toStreamD m
254+
255+------------------------------------------------------------------------------
256+-- Searching
257+------------------------------------------------------------------------------
258+
259+-- | Find all the indices where the element in the stream satisfies the given
260+-- predicate.
261+--
262+-- > findIndices = fold Fold.findIndices
263+--
264+-- @since 0.5.0
265+{-# INLINE findIndices #-}
266+findIndices :: (Monad m) => (a -> Bool) -> Stream m a -> Stream m Int
267+findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
268+
269+------------------------------------------------------------------------------
270+-- Transformation by Inserting
271+------------------------------------------------------------------------------
272+
273+-- intersperseM = intersperseBySpan 1
274+
275+-- | Insert an effect and its output before consuming an element of a stream
276+-- except the first one.
277+--
278+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.unfold Unfold.fromList "hello"
279+-- h.,e.,l.,l.,o"h,e,l,l,o"
280+--
281+-- Be careful about the order of effects. In the above example we used trace
282+-- after the intersperse, if we use it before the intersperse the output would
283+-- be he.l.l.o."h,e,l,l,o".
284+--
285+-- >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar $ Stream.unfold Unfold.fromList "hello"
286+-- he.l.l.o."h,e,l,l,o"
287+--
288+-- @since 0.5.0
289+{-# INLINE intersperseM #-}
290+intersperseM :: (Monad m) => m a -> Stream m a -> Stream m a
291+intersperseM m = fromStreamD . D.intersperseM m . toStreamD
292+
293+------------------------------------------------------------------------------
294+-- Transformation by Reordering
295+------------------------------------------------------------------------------
296+
297+-- XXX Use a compact region list to temporarily store the list, in both reverse
298+-- as well as in reverse'.
299+--
300+-- /Note:/ 'reverse'' is much faster than this, use that when performance
301+-- matters.
302+--
303+-- | Returns the elements of the stream in reverse order. The stream must be
304+-- finite. Note that this necessarily buffers the entire stream in memory.
305+--
306+-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
307+--
308+-- /Since 0.7.0 (Monad m constraint)/
309+--
310+-- /Since: 0.1.1/
311+{-# INLINE reverse #-}
312+reverse :: (Monad m) => Stream m a -> Stream m a
313+reverse s = fromStreamD $ D.reverse $ toStreamD s
314+
315+-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
316+--
317+-- /Pre-release/
318+{-# INLINE reverse' #-}
319+reverse' :: (MonadIO m, Storable a) => Stream m a -> Stream m a
320+-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
321+reverse' =
322+ fromStreamD
323+ . A.flattenArraysRev -- unfoldMany A.readRev
324+ . D.fromStreamK
325+ . K.reverse
326+ . D.toStreamK
327+ . A.arraysOf defaultChunkSize
328+ . toStreamD
329+
330+------------------------------------------------------------------------------
331+-- Combine streams and flatten
332+------------------------------------------------------------------------------
333+
334+-- | Map a stream producing monadic function on each element of the stream
335+-- and then flatten the results into a single stream. Since the stream
336+-- generation function is monadic, unlike 'concatMap', it can produce an
337+-- effect at the beginning of each iteration of the inner loop.
338+--
339+-- @since 0.6.0
340+{-# INLINE concatMapM #-}
341+concatMapM :: (Monad m) => (a -> m (Stream m b)) -> Stream m a -> Stream m b
342+concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
343+
344+-- | Map a stream producing function on each element of the stream and then
345+-- flatten the results into a single stream.
346+--
347+-- >>> concatMap f = Stream.concatMapM (return . f)
348+-- >>> concatMap f = Stream.concatMapWith Stream.append f
349+-- >>> concatMap f = Stream.concat . Stream.map f
350+-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
351+--
352+-- @since 0.6.0
353+{-# INLINE concatMap #-}
354+concatMap ::(Monad m) => (a -> Stream m b) -> Stream m a -> Stream m b
355+concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
356+
357+-- | Given a stream value in the underlying monad, lift and join the underlying
358+-- monad with the stream monad.
359+--
360+-- >>> concatM = Stream.concat . Stream.fromEffect
361+-- >>> concatM = Stream.concat . lift -- requires (MonadTrans t)
362+-- >>> concatM = join . lift -- requires (MonadTrans t, Monad (Stream m))
363+--
364+-- See also: 'concat', 'sequence'
365+--
366+-- /Internal/
367+--
368+{-# INLINE concatM #-}
369+concatM :: (Monad m) => m (Stream m a) -> Stream m a
370+concatM generator = concatMapM (\() -> generator) (fromPure ())
371+
372+------------------------------------------------------------------------------
373+-- Zipping
374+------------------------------------------------------------------------------
375+
376+-- | Like 'zipWith' but using a monadic zipping function.
377+--
378+-- @since 0.4.0
379+{-# INLINE zipWithM #-}
380+zipWithM :: (Monad m) => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
381+zipWithM f m1 m2 =
382+ fromStreamD
383+ $ D.zipWithM f (toStreamD m1) (toStreamD m2)
384+
385+-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
386+-- elements @a@ and @b@ are then zipped using the supplied zip function and the
387+-- result @c@ is yielded to the consumer.
388+--
389+-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
390+-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
391+--
392+-- @
393+-- > K.toList $ K.zipWith (+) (K.fromList [1,2,3]) (K.fromList [4,5,6])
394+-- [5,7,9]
395+-- @
396+--
397+-- @since 0.1.0
398+{-# INLINE zipWith #-}
399+zipWith :: (Monad m) => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
400+zipWith f m1 m2 =
401+ fromStreamD
402+ $ D.zipWith f (toStreamD m1) (toStreamD m2)
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+277−10
@@ -8,38 +8,114 @@
88--
99module Streamly.Internal.Data.Stream.Eliminate
1010 (
11− fold
11+ fold
12+ , foldBreak
13+ , foldContinue
14+
15+ -- * Running a 'Parser'
16+ -- "Streamly.Internal.Data.Parser".
17+ , parse
18+ , parseK
19+ , parseD
20+ , parseBreak
21+ , parseBreakD
22+
23+ -- * Stream Deconstruction
24+ -- | foldr and foldl do not provide the remaining stream. 'uncons' is more
25+ -- general, as it can be used to implement those as well. It allows to use
26+ -- the stream one element at a time, and we have the remaining stream all
27+ -- the time.
28+ , uncons
29+
30+ -- * Right Folds
31+ , foldrM
32+ , foldr
33+
34+ -- * Specific Fold Functions
35+ -- | Folds as functions of the shape @t m a -> m b@.
36+ --
37+ -- These functions are good to run individually but they do not compose
38+ -- well. Prefer writing folds as the 'Fold' data type. Use folds from
39+ -- "Streamly.Internal.Data.Fold" instead of using the functions in this
40+ -- section.
41+ --
42+ -- This section can possibly be removed in future. Are these better in
43+ -- some case compared to 'Fold'? When the input stream is in CPS style
44+ -- (StreamK) we may want to rewrite the function call to CPS implementation
45+ -- of the fold through these definitions. Will that be more efficient for
46+ -- StreamK?
47+
48+ -- -- ** Lazy Folds
49+ -- ** To Containers
50+ , toList
51+
52+ -- * Multi-Stream folds
53+ -- Full equivalence
54+ , eqBy
55+ , cmpBy
56+
57+ -- finding subsequences
58+ , isPrefixOf
59+ , isSuffixOf
60+ , isSubsequenceOf
61+
62+ -- trimming sequences
63+ , stripPrefix
64+ , stripSuffix
1265 )
1366where
1467
68+#if __GLASGOW_HASKELL__ < 808
69+import Data.Semigroup (Semigroup(..))
70+#endif
71+
72+import Control.Monad.Catch (MonadThrow)
73+import Streamly.Internal.Data.Parser (Parser (..))
1574import Streamly.Internal.Data.Fold.Type (Fold)
16−import Streamly.Internal.Data.Stream.Type (Stream)
1775
18−import qualified Streamly.Internal.Data.Stream.Type as Stream
76+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
77+import qualified Streamly.Internal.Data.Parser.ParserK.Type as PRK
78+import qualified Streamly.Internal.Data.Stream.StreamD as D
79+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
80+import qualified Streamly.Internal.Data.Stream.StreamK as K
1981import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD
2082
83+import Streamly.Internal.Data.Stream.Bottom
84+import Streamly.Internal.Data.Stream.Type
85+
86+import Prelude hiding
87+ ( map, mapM, repeat, filter, drop, take, takeWhile, foldr , foldl, mapM_
88+ , product, elem, notElem, maximum, minimum, head, last, tail, length
89+ , null , reverse, init, and, or, lookup, foldr1, (!!) , splitAt, break
90+ , mconcat, sequence, all, any, sum)
91+
92+#include "inline.hs"
93+
2194-- $setup
22−-- >>> import qualified Streamly.Data.Fold as Fold
23−-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
95+-- >>> :m
96+-- >>> import Streamly.Internal.Data.Stream (Stream)
2497-- >>> import qualified Streamly.Internal.Data.Stream as Stream
98+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
99+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
100+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
25101
26102-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
27103-- expression strictly at each step. The behavior is similar to 'foldl''. A
28104-- 'Fold' can terminate early without consuming the full stream. See the
29105-- documentation of individual 'Fold's for termination behavior.
30106--
31−-- >>> Stream.fold Fold.sum (Stream.unfold Unfold.enumerateFromTo (1,100))
107+-- >>> Stream.fold Fold.sum ((Stream.unfold Unfold.enumerateFromTo (1::Int, 100)) :: Stream IO Int)
32108-- 5050
33109--
34110-- Folds never fail, therefore, they produce a default value even when no input
35111-- is provided. It means we can always fold an empty stream and get a valid
36112-- result. For example:
37113--
38−-- >> Stream.fold Fold.sum Stream.nil
39−-- >0
114+-- >>> Stream.fold Fold.sum Stream.nil
115+-- 0
40116--
41117-- 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
118+-- Therefore, @Stream.foldWith f@ is not the same as @Stream.head . Stream.foldMany
43119-- f@.
44120--
45121-- @fold f = Stream.parse (Parser.fromFold f)@
@@ -47,4 +123,195 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD
47123-- /Pre-release/
48124{-# INLINE fold #-}
49125fold :: Monad m => Fold m a b -> Stream m a -> m b
50−fold fld = StreamD.fold fld . Stream.toStreamD
126+fold fld = StreamD.fold fld . toStreamD
127+
128+------------------------------------------------------------------------------
129+-- Deconstruction
130+------------------------------------------------------------------------------
131+
132+-- | Decompose a stream into its head and tail. If the stream is empty, returns
133+-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is
134+-- the head of the stream and @ma@ its tail.
135+--
136+-- This can be used to do pretty much anything in an imperative manner, as it
137+-- just breaks down the stream into individual elements and we can loop over
138+-- them as we deem fit. For example, this can be used to convert a streamly
139+-- stream into other stream types.
140+--
141+-- All the folds in this module can be expressed in terms of 'uncons', however,
142+-- this is generally less efficient than specific folds because it takes apart
143+-- the stream one element at a time, therefore, does not take adavantage of
144+-- stream fusion.
145+--
146+-- 'foldBreak' is a more general way of consuming a stream piecemeal.
147+--
148+-- >>> :{
149+-- uncons xs = do
150+-- r <- Stream.foldBreak Fold.one xs
151+-- return $ case r of
152+-- (Nothing, _) -> Nothing
153+-- (Just h, t) -> Just (h, t)
154+-- :}
155+--
156+-- @since 0.1.0
157+{-# INLINE uncons #-}
158+uncons :: (Monad m) => Stream m a -> m (Maybe (a, Stream m a))
159+uncons m = fmap (fmap (fmap fromStreamK)) $ K.uncons (toStreamK m)
160+
161+------------------------------------------------------------------------------
162+-- Running a Parser
163+------------------------------------------------------------------------------
164+
165+-- | Parse a stream using the supplied ParserD 'PRD.Parser'.
166+--
167+-- /Internal/
168+--
169+{-# INLINE_NORMAL parseD #-}
170+parseD :: MonadThrow m => PRD.Parser m a b -> Stream m a -> m b
171+parseD p = D.parse p . toStreamD
172+
173+-- | Parse a stream using the supplied ParserK 'PRK.Parser'.
174+--
175+-- /Internal/
176+{-# INLINE parseK #-}
177+parseK :: MonadThrow m => PRK.Parser m a b -> Stream m a -> m b
178+parseK = parse
179+
180+-- | Parse a stream using the supplied 'Parser'.
181+--
182+-- Unlike folds, parsers may not always result in a valid output, they may
183+-- result in an error. For example:
184+--
185+-- >>> Stream.parse (Parser.takeEQ 1 Fold.drain) Stream.nil
186+-- *** Exception: ParseError "takeEQ: Expecting exactly 1 elements, input terminated on 0"
187+--
188+-- Note:
189+--
190+-- @
191+-- fold f = Stream.parse (Parser.fromFold f)
192+-- @
193+--
194+-- @parse p@ is not the same as @head . parseMany p@ on an empty stream.
195+--
196+-- /Pre-release/
197+--
198+{-# INLINE [3] parse #-}
199+parse :: MonadThrow m => Parser m a b -> Stream m a -> m b
200+parse = parseD . PRD.fromParserK
201+
202+{-# INLINE_NORMAL parseBreakD #-}
203+parseBreakD :: MonadThrow m => PRD.Parser m a b -> Stream m a -> m (b, Stream m a)
204+parseBreakD parser strm = do
205+ (b, strmD) <- D.parseBreak parser (toStreamD strm)
206+ return $! (b, fromStreamD strmD)
207+
208+-- | Parse a stream using the supplied 'Parser'.
209+--
210+-- /Internal/
211+--
212+{-# INLINE parseBreak #-}
213+parseBreak :: MonadThrow m => Parser m a b -> Stream m a -> m (b, Stream m a)
214+parseBreak p strm = fmap f $ K.parseBreak (PRD.fromParserK p) (toStreamK strm)
215+
216+ where
217+
218+ f (b, str) = (b, fromStreamK str)
219+
220+------------------------------------------------------------------------------
221+-- Multi-stream folds
222+------------------------------------------------------------------------------
223+
224+-- | Returns 'True' if the first stream is the same as or a prefix of the
225+-- second. A stream is a prefix of itself.
226+--
227+-- >>> Stream.isPrefixOf (Stream.unfold Unfold.fromList "hello") (Stream.unfold Unfold.fromList "hello" :: Stream IO Char)
228+-- True
229+--
230+-- @since 0.6.0
231+{-# INLINE isPrefixOf #-}
232+isPrefixOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool
233+isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
234+
235+-- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf
236+-- may use the suffix stream many times. To run in optimal memory we do not
237+-- want to buffer the suffix stream in memory therefore we need an ability to
238+-- clone (or consume iStream multiple times) the suffix stream without any side
239+-- effects so thaStream multiple potential suffix matches can proceed in parallel
240+-- without buffering the suffix stream. For example, we may create the suffix
241+-- stream from a file handle, however, if we evaluate the stream multiple
242+-- times, once for each match, we will need a different file handle each time
243+-- which may exhaust the file descriptors. Instead, we want to share the same
244+-- underlying file descriptor, use pread on it to generate the stream and clone
245+-- the stream for each match. Therefore the suffix stream should be built in
246+-- such a way that it can be consumed multiple times without any problems.
247+
248+-- XXX Can be implemented with better space/time complexity.
249+-- Space: @O(n)@ worst case where @n@ is the length of the suffix.
250+
251+-- | Returns 'True' if the first stream is a suffix of the second. A stream is
252+-- considered a suffix of itself.
253+--
254+-- >>> Stream.isSuffixOf (Stream.unfold Unfold.fromList "hello") (Stream.unfold Unfold.fromList "hello" :: Stream IO Char)
255+-- True
256+--
257+-- Space: @O(n)@, buffers entire input stream and the suffix.
258+--
259+-- /Pre-release/
260+--
261+-- /Suboptimal/ - Help wanted.
262+--
263+{-# INLINE isSuffixOf #-}
264+isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
265+isSuffixOf suffix stream = reverse suffix `isPrefixOf` reverse stream
266+
267+-- | Returns 'True' if all the elements of the first stream occur, in order, in
268+-- the second stream. The elements do not have to occur consecutively. A stream
269+-- is a subsequence of itself.
270+--
271+-- >>> Stream.isSubsequenceOf (Stream.unfold Unfold.fromList "hlo") (Stream.unfold Unfold.fromList "hello" :: Stream IO Char)
272+-- True
273+--
274+-- @since 0.6.0
275+{-# INLINE isSubsequenceOf #-}
276+isSubsequenceOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool
277+isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)
278+
279+-- Note: If we want to return a Maybe value to know whether the
280+-- suffix/infix was present or not along with the stripped stream then
281+-- we need to buffer the whole input stream.
282+--
283+-- | @stripPrefix prefix stream@ strips @prefix@ from @stream@ if it is a
284+-- prefix of stream. Returns 'Nothing' if the stream does not start with the
285+-- given prefix, stripped stream otherwise. Returns @Just nil@ when the prefix
286+-- is the same as the stream.
287+--
288+-- See also "Streamly.Internal.Data.Stream.Nesting.dropPrefix".
289+--
290+-- Space: @O(1)@
291+--
292+-- @since 0.6.0
293+{-# INLINE stripPrefix #-}
294+stripPrefix
295+ :: (Eq a, Monad m)
296+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
297+stripPrefix m1 m2 = fmap fromStreamD <$>
298+ D.stripPrefix (toStreamD m1) (toStreamD m2)
299+
300+-- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does
301+-- not end with the given suffix. Returns @Just nil@ when the suffix is the
302+-- same as the stream.
303+--
304+-- IStream may be more efficient to convert the stream to an Array and use
305+-- stripSuffix on that especially if the elements have a Storable or Prim
306+-- instance.
307+--
308+-- See also "Streamly.Internal.Data.Stream.Nesting.dropSuffix".
309+--
310+-- Space: @O(n)@, buffers the entire input stream as well as the suffix
311+--
312+-- /Pre-release/
313+{-# INLINE stripSuffix #-}
314+stripSuffix
315+ :: (Monad m, Eq a)
316+ => Stream m a -> Stream m a -> m (Maybe (Stream m a))
317+stripSuffix m1 m2 = fmap reverse <$> stripPrefix (reverse m1) (reverse m2)
core/src/Streamly/Internal/Data/Stream/Exception.hsnew file+269
@@ -0,0 +1,269 @@
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.Prelude as Stream
37+-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (nilM)
38+
39+------------------------------------------------------------------------------
40+-- Exceptions
41+------------------------------------------------------------------------------
42+
43+-- | Run the action @m b@ before the stream yields its first element.
44+--
45+-- Same as the following but more efficient due to fusion:
46+--
47+-- >>> before action xs = Stream.nilM action <> xs
48+-- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)
49+--
50+-- @since 0.7.0
51+{-# INLINE before #-}
52+before :: (Monad m) => m b -> Stream m a -> Stream m a
53+before action xs = fromStreamD $ D.before action $ toStreamD xs
54+
55+-- | Like 'after', with following differences:
56+--
57+-- * action @m b@ won't run if the stream is garbage collected
58+-- after partial evaluation.
59+-- * Monad @m@ does not require any other constraints.
60+-- * has slightly better performance than 'after'.
61+--
62+-- Same as the following, but with stream fusion:
63+--
64+-- > after_ action xs = xs <> 'nilM' action
65+--
66+-- /Pre-release/
67+--
68+{-# INLINE after_ #-}
69+after_ :: (Monad m) => m b -> Stream m a -> Stream m a
70+after_ action xs = fromStreamD $ D.after_ action $ toStreamD xs
71+
72+-- | Run the action @m b@ whenever the stream @Stream m a@ stops normally, or if it
73+-- is garbage collected after a partial lazy evaluation.
74+--
75+-- The semantics of the action @m b@ are similar to the semantics of cleanup
76+-- action in 'bracket'.
77+--
78+-- /See also 'after_'/
79+--
80+-- @since 0.7.0
81+--
82+{-# INLINE after #-}
83+after :: (MonadAsync m)
84+ => m b -> Stream m a -> Stream m a
85+after action xs = fromStreamD $ D.after action $ toStreamD xs
86+
87+-- | Run the action @m b@ if the stream aborts due to an exception. The
88+-- exception is not caught, simply rethrown.
89+--
90+-- /Inhibits stream fusion/
91+--
92+-- @since 0.7.0
93+{-# INLINE onException #-}
94+onException :: (MonadCatch m) => m b -> Stream m a -> Stream m a
95+onException action xs = fromStreamD $ D.onException action $ toStreamD xs
96+
97+-- | Like 'finally' with following differences:
98+--
99+-- * action @m b@ won't run if the stream is garbage collected
100+-- after partial evaluation.
101+-- * does not require a 'MonadAsync' constraint.
102+-- * has slightly better performance than 'finally'.
103+--
104+-- /Inhibits stream fusion/
105+--
106+-- /Pre-release/
107+--
108+{-# INLINE finally_ #-}
109+finally_ :: (MonadCatch m) => m b -> Stream m a -> Stream m a
110+finally_ action xs = fromStreamD $ D.finally_ action $ toStreamD xs
111+
112+-- | Run the action @m b@ whenever the stream @Stream m a@ stops normally, aborts
113+-- due to an exception or if it is garbage collected after a partial lazy
114+-- evaluation.
115+--
116+-- The semantics of running the action @m b@ are similar to the cleanup action
117+-- semantics described in 'bracket'.
118+--
119+-- @
120+-- finally release = bracket (return ()) (const release)
121+-- @
122+--
123+-- /See also 'finally_'/
124+--
125+-- /Inhibits stream fusion/
126+--
127+-- @since 0.7.0
128+--
129+{-# INLINE finally #-}
130+finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
131+finally action xs = fromStreamD $ D.finally action $ toStreamD xs
132+
133+-- | Like 'bracket' but with following differences:
134+--
135+-- * alloc action @m b@ runs with async exceptions enabled
136+-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
137+-- after partial evaluation.
138+-- * does not require a 'MonadAsync' constraint.
139+-- * has slightly better performance than 'bracket'.
140+--
141+-- /Inhibits stream fusion/
142+--
143+-- /Pre-release/
144+--
145+{-# INLINE bracket_ #-}
146+bracket_ :: (MonadCatch m)
147+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
148+bracket_ bef aft bet = fromStreamD $
149+ D.bracket_ bef aft (toStreamD . bet)
150+
151+-- | Run the alloc action @m b@ with async exceptions disabled but keeping
152+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
153+-- output @b@ as input to @b -> Stream m a@ to generate an output stream.
154+--
155+-- @b@ is usually a resource under the state of monad @m@, e.g. a file
156+-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
157+-- runs whenever the stream ends normally, due to a sync or async exception or
158+-- if it gets garbage collected after a partial lazy evaluation.
159+--
160+-- 'bracket' only guarantees that the cleanup action runs, and it runs with
161+-- async exceptions enabled. The action must ensure that it can successfully
162+-- cleanup the resource in the face of sync or async exceptions.
163+--
164+-- When the stream ends normally or on a sync exception, cleanup action runs
165+-- immediately in the current thread context, whereas in other cases it runs in
166+-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
167+--
168+-- /See also: 'bracket_'/
169+--
170+-- /Inhibits stream fusion/
171+--
172+-- @since 0.7.0
173+--
174+{-# INLINE bracket #-}
175+bracket :: (MonadAsync m, MonadCatch m)
176+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
177+bracket bef aft = bracket' bef aft aft aft
178+
179+-- For a use case of this see the "streamly-process" package. It needs to kill
180+-- the process in case of exception or garbage collection, but waits for the
181+-- process to terminate in normal cases.
182+--
183+-- | Like 'bracket' but can use separate cleanup actions depending on the mode
184+-- of termination. @bracket' before onStop onGC onException action@ runs
185+-- @action@ using the result of @before@. If the stream stops, @onStop@ action
186+-- is executed, if the stream is abandoned @onGC@ is executed, if the stream
187+-- encounters an exception @onException@ is executed.
188+--
189+-- /Pre-release/
190+{-# INLINE bracket' #-}
191+bracket' :: (MonadAsync m, MonadCatch m)
192+ => m b -> (b -> m c) -> (b -> m d) -> (b -> m e) -> (b -> Stream m a) -> Stream m a
193+bracket' bef aft gc exc bet = fromStreamD $
194+ D.bracket' bef aft exc gc (toStreamD . bet)
195+
196+-- | Like 'handle' but the exception handler is also provided with the stream
197+-- that generated the exception as input. The exception handler can thus
198+-- re-evaluate the stream to retry the action that failed. The exception
199+-- handler can again call 'ghandle' on it to retry the action multiple times.
200+--
201+-- This is highly experimental. In a stream of actions we can map the stream
202+-- with a retry combinator to retry each action on failure.
203+--
204+-- /Inhibits stream fusion/
205+--
206+-- /Pre-release/
207+--
208+{-# INLINE ghandle #-}
209+ghandle :: (MonadCatch m, Exception e)
210+ => (e -> Stream m a -> Stream m a) -> Stream m a -> Stream m a
211+ghandle handler =
212+ fromStreamD
213+ . D.ghandle (\e xs -> toStreamD $ handler e (fromStreamD xs))
214+ . toStreamD
215+
216+-- | When evaluating a stream if an exception occurs, stream evaluation aborts
217+-- and the specified exception handler is run with the exception as argument.
218+--
219+-- /Inhibits stream fusion/
220+--
221+-- @since 0.7.0
222+{-# INLINE handle #-}
223+handle :: (MonadCatch m, Exception e)
224+ => (e -> Stream m a) -> Stream m a -> Stream m a
225+handle handler xs =
226+ fromStreamD $ D.handle (toStreamD . handler) $ toStreamD xs
227+
228+
229+-- | @retry@ takes 3 arguments
230+--
231+-- 1. A map @m@ whose keys are exceptions and values are the number of times to
232+-- retry the action given that the exception occurs.
233+--
234+-- 2. A handler @han@ that decides how to handle an exception when the exception
235+-- cannot be retried.
236+--
237+-- 3. The stream itself that we want to run this mechanism on.
238+--
239+-- When evaluating a stream if an exception occurs,
240+--
241+-- 1. The stream evaluation aborts
242+--
243+-- 2. The exception is looked up in @m@
244+--
245+-- a. If the exception exists and the mapped value is > 0 then,
246+--
247+-- i. The value is decreased by 1.
248+--
249+-- ii. The stream is resumed from where the exception was called, retrying
250+-- the action.
251+--
252+-- b. If the exception exists and the mapped value is == 0 then the stream
253+-- evaluation stops.
254+--
255+-- c. If the exception does not exist then we handle the exception using
256+-- @han@.
257+--
258+-- /Internal/
259+--
260+{-# INLINE retry #-}
261+retry :: (MonadCatch m, Exception e, Ord e)
262+ => Map e Int
263+ -- ^ map from exception to retry count
264+ -> (e -> Stream m a)
265+ -- ^ default handler for those exceptions that are not in the map
266+ -> Stream m a
267+ -> Stream m a
268+retry emap handler inp =
269+ fromStreamD $ D.retry emap (toStreamD . handler) $ toStreamD inp
core/src/Streamly/Internal/Data/Stream/Expand.hsnew file+771
@@ -0,0 +1,771 @@
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 (Pair Wise)
15+ -- | Like the functions in the section above these functions also combine
16+ -- two streams into a single stream but when used @n@ times linearly they
17+ -- exhibit O(n^2) complexity. They are best combined in a binary tree
18+ -- fashion using 'concatPairsWith' giving a @n * log n@ complexity. Avoid
19+ -- using these with 'concatMapWith' when combining a large or infinite
20+ -- number of streams.
21+
22+ -- ** Append
23+ append
24+
25+ -- ** wSerial
26+ -- | 'wSerial' is a CPS based stream interleaving functions. Use
27+ -- 'concatPairsWith wSerial' to interleave @n@ streams uniformly. It can be
28+ -- used with 'concatMapWith' as well, however, the interleaving behavior of
29+ -- @n@ streams would be asymmetric giving exponentially more weightage to
30+ -- streams that come earlier in the composition.
31+ --
32+ , wSerial
33+ , WSerial.wSerialFst
34+ , WSerial.wSerialMin
35+
36+ -- ** Interleave
37+ -- | 'interleave' is like 'wSerial' but using a direct style
38+ -- implementation instead of CPS. It is faster than 'wSerial' due to stream
39+ -- fusion but has worse efficiency when used with 'concatMapWith' for large
40+ -- number of streams.
41+ , interleave
42+ , interleaveMin
43+ , interleaveSuffix
44+ , interleaveInfix
45+
46+ -- ** Round Robin
47+ , roundrobin
48+
49+ -- ** Zip
50+ , zipWith
51+ , zipWithM
52+
53+ -- ** Merge
54+ , merge
55+ , mergeBy
56+ , mergeByM
57+ , mergeByMFused
58+ , mergeMinBy
59+ , mergeFstBy
60+
61+ -- * Combine Streams and Unfolds
62+ -- |
63+ -- Expand a stream by repeatedly using an unfold and merging the resulting
64+ -- streams. Functions generally ending in the shape:
65+ --
66+ -- @Unfold m a b -> Stream m a -> Stream m b@
67+
68+ -- ** Append Many (Unfold)
69+ -- | Unfold and flatten streams.
70+ , unfoldMany
71+ , unfoldManyInterleave
72+ , unfoldManyRoundRobin
73+
74+ -- ** Interpose
75+ -- | Insert effects between streams. Like unfoldMany but intersperses an
76+ -- effect between the streams. A special case of gintercalate.
77+ , interpose
78+ , interposeSuffix
79+ -- , interposeBy
80+
81+ -- ** Intercalate
82+ -- | Insert Streams between Streams.
83+ -- Like unfoldMany but intersperses streams from another source between
84+ -- the streams from the first source.
85+ , intercalate
86+ , intercalateSuffix
87+ , gintercalate
88+ , gintercalateSuffix
89+
90+ -- * Append Many (concatMap)
91+ -- | Map and serially append streams. 'concatMapM' is a generalization of
92+ -- the binary append operation to append many streams.
93+ , concatMapM
94+ , concatMap
95+ , concatM
96+ , concat
97+
98+ -- * ConcatMapWith
99+ -- | Map and flatten a stream like 'concatMap' but using a custom binary
100+ -- stream merging combinator instead of just appending the streams. The
101+ -- merging occurs sequentially, it works efficiently for 'serial', 'async',
102+ -- 'ahead' like merge operations where we consume one stream before the
103+ -- next or in case of 'wAsync' or 'parallel' where we consume all streams
104+ -- simultaneously anyway.
105+ --
106+ -- However, in cases where the merging consumes streams in a round robin
107+ -- fashion, a pair wise merging using 'concatPairsWith' would be more
108+ -- efficient. These cases include operations like 'mergeBy' or 'zipWith'.
109+
110+ , concatSmapMWith
111+
112+ -- * ConcatPairsWith
113+ -- | See the notes about suitable merge functions in the 'concatMapWith'
114+ -- section.
115+ , concatPairsWith
116+
117+ -- * IterateMap
118+ -- | Map and flatten Trees of Streams
119+ , iterateMapWith
120+ , iterateSmapMWith
121+ , iterateMapLeftsWith
122+ , iterateUnfold
123+ )
124+where
125+
126+#include "inline.hs"
127+
128+import Streamly.Internal.Data.Stream.Bottom
129+ ( concatM, concatMapM, concatMap, smapM, fromPure, fromEffect
130+ , zipWith, zipWithM)
131+import Streamly.Internal.Data.Stream.Type
132+ ( Stream, append, fromStreamD, fromStreamK, toStreamD, toStreamK
133+ , concatMapWith, nil)
134+import Streamly.Internal.Data.Unfold.Type (Unfold)
135+
136+import qualified Streamly.Internal.Data.Stream.StreamD as D
137+import qualified Streamly.Internal.Data.Stream.StreamK as K (mergeBy, mergeByM)
138+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
139+import qualified Streamly.Internal.Data.Stream.WSerial as WSerial
140+
141+import Prelude hiding (concat, concatMap, zipWith)
142+
143+-- $setup
144+-- >>> :m
145+-- >>> import Control.Concurrent (threadDelay)
146+-- >>> import Data.IORef
147+-- >>> import Prelude hiding (zipWith, concatMap, concat)
148+-- >>> import qualified Streamly.Prelude as Stream
149+-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
150+-- >>> import qualified Streamly.Data.Fold as Fold
151+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
152+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
153+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
154+-- >>> import qualified Streamly.Data.Array.Foreign as Array
155+-- >>> :{
156+-- delay n = do
157+-- threadDelay (n * 1000000) -- sleep for n seconds
158+-- putStrLn (show n ++ " sec") -- print "n sec"
159+-- return n -- IO Int
160+-- :}
161+--
162+
163+-------------------------------------------------------------------------------
164+-- Interleaving
165+-------------------------------------------------------------------------------
166+
167+infixr 6 `wSerial`
168+
169+-- XXX doc duplicated from Stream.Serial module.
170+--
171+-- | Interleaves two streams, yielding one element from each stream
172+-- alternately. When one stream stops the rest of the other stream is used in
173+-- the output stream.
174+--
175+-- >>> import Streamly.Prelude (wSerial)
176+-- >>> stream1 = Stream.fromList [1,2]
177+-- >>> stream2 = Stream.fromList [3,4]
178+-- >>> Stream.toList $ Stream.fromWSerial $ stream1 `wSerial` stream2
179+-- [1,3,2,4]
180+--
181+-- Note, for singleton streams 'wSerial' and 'serial' are identical.
182+--
183+-- Note that this operation cannot be used to fold a container of infinite
184+-- streams but it can be used for very large streams as the state that it needs
185+-- to maintain is proportional to the logarithm of the number of streams.
186+--
187+-- @since 0.8.0
188+--
189+-- /Since: 0.2.0 ("Streamly")/
190+
191+-- Scheduling Notes:
192+--
193+-- Note that evaluation of @a \`wSerial` b \`wSerial` c@ does not interleave
194+-- @a@, @b@ and @c@ with equal priority. This expression is equivalent to @a
195+-- \`wSerial` (b \`wSerial` c)@, therefore, it fairly interleaves @a@ with the
196+-- result of @b \`wSerial` c@. For example, @Stream.fromList [1,2] \`wSerial`
197+-- Stream.fromList [3,4] \`wSerial` Stream.fromList [5,6]@ would result in
198+-- [1,3,2,5,4,6]. In other words, the leftmost stream gets the same scheduling
199+-- priority as the rest of the streams taken together. The same is true for
200+-- each subexpression on the right.
201+--
202+{-# INLINE wSerial #-}
203+wSerial :: Stream m a -> Stream m a -> Stream m a
204+wSerial m1 m2 = fromStreamK $ WSerial.wSerialK (toStreamK m1) (toStreamK m2)
205+
206+-- XXX Same as 'wSerial'. We should perhaps rename wSerial to interleave.
207+-- XXX Document the interleaving behavior of side effects in all the
208+-- interleaving combinators.
209+-- XXX Write time-domain equivalents of these. In the time domain we can
210+-- interleave two streams such that the value of second stream is always taken
211+-- from its last value even if no new value is being yielded, like
212+-- zipWithLatest. It would be something like interleaveWithLatest.
213+--
214+-- | Interleaves the outputs of two streams, yielding elements from each stream
215+-- alternately, starting from the first stream. If any of the streams finishes
216+-- early the other stream continues alone until it too finishes.
217+--
218+-- >>> :set -XOverloadedStrings
219+-- >>> import Data.Functor.Identity (Identity)
220+-- >>> Stream.interleave "ab" ",,,," :: Stream.SerialT Identity Char
221+-- fromList "a,b,,,"
222+--
223+-- >>> Stream.interleave "abcd" ",," :: Stream.SerialT Identity Char
224+-- fromList "a,b,cd"
225+--
226+-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
227+--
228+-- Do not use at scale in concatMapWith.
229+--
230+-- /Pre-release/
231+{-# INLINE interleave #-}
232+interleave ::(Monad m) => Stream m b -> Stream m b -> Stream m b
233+interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
234+
235+-- | Interleaves the outputs of two streams, yielding elements from each stream
236+-- alternately, starting from the first stream. As soon as the first stream
237+-- finishes, the output stops, discarding the remaining part of the second
238+-- stream. In this case, the last element in the resulting stream would be from
239+-- the second stream. If the second stream finishes early then the first stream
240+-- still continues to yield elements until it finishes.
241+--
242+-- >>> :set -XOverloadedStrings
243+-- >>> import Data.Functor.Identity (Identity)
244+-- >>> Stream.interleaveSuffix "abc" ",,,," :: Stream.SerialT Identity Char
245+-- fromList "a,b,c,"
246+-- >>> Stream.interleaveSuffix "abc" "," :: Stream.SerialT Identity Char
247+-- fromList "a,bc"
248+--
249+-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
250+--
251+-- Do not use at scale in concatMapWith.
252+--
253+-- /Pre-release/
254+{-# INLINE interleaveSuffix #-}
255+interleaveSuffix ::(Monad m) => Stream m b -> Stream m b -> Stream m b
256+interleaveSuffix m1 m2 =
257+ fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
258+
259+-- | Interleaves the outputs of two streams, yielding elements from each stream
260+-- alternately, starting from the first stream and ending at the first stream.
261+-- If the second stream is longer than the first, elements from the second
262+-- stream are infixed with elements from the first stream. If the first stream
263+-- is longer then it continues yielding elements even after the second stream
264+-- has finished.
265+--
266+-- >>> :set -XOverloadedStrings
267+-- >>> import Data.Functor.Identity (Identity)
268+-- >>> Stream.interleaveInfix "abc" ",,,," :: Stream.SerialT Identity Char
269+-- fromList "a,b,c"
270+-- >>> Stream.interleaveInfix "abc" "," :: Stream.SerialT Identity Char
271+-- fromList "a,bc"
272+--
273+-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
274+--
275+-- Do not use at scale in concatMapWith.
276+--
277+-- /Pre-release/
278+{-# INLINE interleaveInfix #-}
279+interleaveInfix ::(Monad m) => Stream m b -> Stream m b -> Stream m b
280+interleaveInfix m1 m2 =
281+ fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
282+
283+-- | Interleaves the outputs of two streams, yielding elements from each stream
284+-- alternately, starting from the first stream. The output stops as soon as any
285+-- of the two streams finishes, discarding the remaining part of the other
286+-- stream. The last element of the resulting stream would be from the longer
287+-- stream.
288+--
289+-- >>> :set -XOverloadedStrings
290+-- >>> import Data.Functor.Identity (Identity)
291+-- >>> Stream.interleaveMin "ab" ",,,," :: Stream.SerialT Identity Char
292+-- fromList "a,b,"
293+-- >>> Stream.interleaveMin "abcd" ",," :: Stream.SerialT Identity Char
294+-- fromList "a,b,c"
295+--
296+-- 'interleaveMin' is dual to 'interleave'.
297+--
298+-- Do not use at scale in concatMapWith.
299+--
300+-- /Pre-release/
301+{-# INLINE interleaveMin #-}
302+interleaveMin ::(Monad m) => Stream m b -> Stream m b -> Stream m b
303+interleaveMin m1 m2 = fromStreamD $ D.interleaveMin (toStreamD m1) (toStreamD m2)
304+
305+-------------------------------------------------------------------------------
306+-- Scheduling
307+-------------------------------------------------------------------------------
308+
309+-- | Schedule the execution of two streams in a fair round-robin manner,
310+-- executing each stream once, alternately. Execution of a stream may not
311+-- necessarily result in an output, a stream may chose to @Skip@ producing an
312+-- element until later giving the other stream a chance to run. Therefore, this
313+-- combinator fairly interleaves the execution of two streams rather than
314+-- fairly interleaving the output of the two streams. This can be useful in
315+-- co-operative multitasking without using explicit threads. This can be used
316+-- as an alternative to `async`.
317+--
318+-- Do not use at scale in concatMapWith.
319+--
320+-- /Pre-release/
321+{-# INLINE roundrobin #-}
322+roundrobin ::(Monad m) => Stream m b -> Stream m b -> Stream m b
323+roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)
324+
325+------------------------------------------------------------------------------
326+-- Merging (sorted streams)
327+------------------------------------------------------------------------------
328+
329+-- | Merge two streams using a comparison function. The head elements of both
330+-- the streams are compared and the smaller of the two elements is emitted, if
331+-- both elements are equal then the element from the first stream is used
332+-- first.
333+--
334+-- If the streams are sorted in ascending order, the resulting stream would
335+-- also remain sorted in ascending order.
336+--
337+-- @
338+-- >>> Stream.toList $ Stream.mergeBy compare (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
339+-- [1,2,3,4,5,6,8]
340+--
341+-- @
342+--
343+-- See also: 'mergeByMFused'
344+--
345+-- @since 0.6.0
346+{-# INLINE mergeBy #-}
347+mergeBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
348+mergeBy f m1 m2 = fromStreamK $ K.mergeBy f (toStreamK m1) (toStreamK m2)
349+
350+-- | Like 'mergeBy' but with a monadic comparison function.
351+--
352+-- Merge two streams randomly:
353+--
354+-- @
355+-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT
356+-- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])
357+-- [2,1,2,2,2,1,1,1]
358+-- @
359+--
360+-- Merge two streams in a proportion of 2:1:
361+--
362+-- @
363+-- >>> :{
364+-- do
365+-- let proportionately m n = do
366+-- ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]
367+-- return $ \_ _ -> do
368+-- r <- readIORef ref
369+-- writeIORef ref $ Prelude.tail r
370+-- return $ Prelude.head r
371+-- f <- proportionately 2 1
372+-- xs <- Stream.toList $ Stream.mergeByM f (Stream.fromList [1,1,1,1,1,1]) (Stream.fromList [2,2,2])
373+-- print xs
374+-- :}
375+-- [1,1,2,1,1,2,1,1,2]
376+--
377+-- @
378+--
379+-- See also: 'mergeByMFused'
380+--
381+-- @since 0.6.0
382+{-# INLINE mergeByM #-}
383+mergeByM
384+ :: (Monad m)
385+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
386+mergeByM f m1 m2 = fromStreamK $ K.mergeByM f (toStreamK m1) (toStreamK m2)
387+
388+-- XXX Fused versions should probably go to a separate module using the same
389+-- names for the combinators.
390+--
391+-- | Like 'mergeByM' but much faster, works best when merging statically known
392+-- number of streams. When merging more than two streams try to merge pairs and
393+-- pair pf pairs in a tree like structure.'mergeByM' works better with variable
394+-- number of streams being merged using 'concatPairsWith'.
395+--
396+-- /Internal/
397+{-# INLINE mergeByMFused #-}
398+mergeByMFused
399+ :: (Monad m)
400+ => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
401+mergeByMFused f m1 m2 =
402+ fromStreamD $ D.mergeByM f (toStreamD m1) (toStreamD m2)
403+
404+-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
405+--
406+-- /Unimplemented/
407+{-# INLINABLE mergeMinBy #-}
408+mergeMinBy :: -- (Monad m) =>
409+ (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
410+mergeMinBy _f _m1 _m2 = undefined
411+ -- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)
412+
413+-- | Like 'mergeByM' but stops merging as soon as the first stream stops.
414+--
415+-- /Unimplemented/
416+{-# INLINABLE mergeFstBy #-}
417+mergeFstBy :: -- (Monad m) =>
418+ (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
419+mergeFstBy _f _m1 _m2 = undefined
420+ -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
421+
422+-- XXX we may want to use the name "merge" differently
423+-- | Same as @'mergeBy' 'compare'@.
424+--
425+-- >>> Stream.toList $ Stream.merge (Stream.fromList [1,3,5]) (Stream.fromList [2,4,6,8])
426+-- [1,2,3,4,5,6,8]
427+--
428+-- /Internal/
429+--
430+{-# INLINABLE merge #-}
431+merge ::
432+ (Ord a) => Stream m a -> Stream m a -> Stream m a
433+merge = mergeBy compare
434+
435+------------------------------------------------------------------------------
436+-- Combine N Streams - unfoldMany
437+------------------------------------------------------------------------------
438+
439+-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike
440+-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and
441+-- therefore provide many times better performance.
442+--
443+-- @since 0.8.0
444+{-# INLINE unfoldMany #-}
445+unfoldMany ::(Monad m) => Unfold m a b -> Stream m a -> Stream m b
446+unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)
447+
448+-- | Like 'unfoldMany' but interleaves the streams in the same way as
449+-- 'interleave' behaves instead of appending them.
450+--
451+-- /Pre-release/
452+{-# INLINE unfoldManyInterleave #-}
453+unfoldManyInterleave ::(Monad m)
454+ => Unfold m a b -> Stream m a -> Stream m b
455+unfoldManyInterleave u m =
456+ fromStreamD $ D.unfoldManyInterleave u (toStreamD m)
457+
458+-- | Like 'unfoldMany' but executes the streams in the same way as
459+-- 'roundrobin'.
460+--
461+-- /Pre-release/
462+{-# INLINE unfoldManyRoundRobin #-}
463+unfoldManyRoundRobin ::(Monad m)
464+ => Unfold m a b -> Stream m a -> Stream m b
465+unfoldManyRoundRobin u m =
466+ fromStreamD $ D.unfoldManyRoundRobin u (toStreamD m)
467+
468+------------------------------------------------------------------------------
469+-- Combine N Streams - interpose
470+------------------------------------------------------------------------------
471+
472+-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
473+--
474+-- | Unfold the elements of a stream, intersperse the given element between the
475+-- unfolded streams and then concat them into a single stream.
476+--
477+-- > unwords = S.interpose ' '
478+--
479+-- /Pre-release/
480+{-# INLINE interpose #-}
481+interpose :: (Monad m)
482+ => c -> Unfold m b c -> Stream m b -> Stream m c
483+interpose x unf str =
484+ fromStreamD $ D.interpose (return x) unf (toStreamD str)
485+
486+-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
487+--
488+-- | Unfold the elements of a stream, append the given element after each
489+-- unfolded stream and then concat them into a single stream.
490+--
491+-- > unlines = S.interposeSuffix '\n'
492+--
493+-- /Pre-release/
494+{-# INLINE interposeSuffix #-}
495+interposeSuffix :: (Monad m)
496+ => c -> Unfold m b c -> Stream m b -> Stream m c
497+interposeSuffix x unf str =
498+ fromStreamD $ D.interposeSuffix (return x) unf (toStreamD str)
499+
500+------------------------------------------------------------------------------
501+-- Combine N Streams - intercalate
502+------------------------------------------------------------------------------
503+
504+-- XXX we can swap the order of arguments to gintercalate so that the
505+-- definition of unfoldMany becomes simpler? The first stream should be
506+-- infixed inside the second one. However, if we change the order in
507+-- "interleave" as well similarly, then that will make it a bit unintuitive.
508+--
509+-- > unfoldMany unf str =
510+-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
511+--
512+-- | 'interleaveInfix' followed by unfold and concat.
513+--
514+-- /Pre-release/
515+{-# INLINE gintercalate #-}
516+gintercalate
517+ :: (Monad m)
518+ => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
519+gintercalate unf1 str1 unf2 str2 =
520+ fromStreamD $ D.gintercalate
521+ unf1 (toStreamD str1)
522+ unf2 (toStreamD str2)
523+
524+-- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)
525+--
526+-- | 'intersperse' followed by unfold and concat.
527+--
528+-- > intercalate unf a str = unfoldMany unf $ intersperse a str
529+-- > intersperse = intercalate (Unfold.function id)
530+-- > unwords = intercalate Unfold.fromList " "
531+--
532+-- >>> Stream.toList $ Stream.intercalate Unfold.fromList " " $ Stream.fromList ["abc", "def", "ghi"]
533+-- "abc def ghi"
534+--
535+-- @since 0.8.0
536+{-# INLINE intercalate #-}
537+intercalate :: (Monad m)
538+ => Unfold m b c -> b -> Stream m b -> Stream m c
539+intercalate unf seed str = fromStreamD $
540+ D.unfoldMany unf $ D.intersperse seed (toStreamD str)
541+
542+-- | 'interleaveSuffix' followed by unfold and concat.
543+--
544+-- /Pre-release/
545+{-# INLINE gintercalateSuffix #-}
546+gintercalateSuffix
547+ :: (Monad m)
548+ => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
549+gintercalateSuffix unf1 str1 unf2 str2 =
550+ fromStreamD $ D.gintercalateSuffix
551+ unf1 (toStreamD str1)
552+ unf2 (toStreamD str2)
553+
554+-- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)
555+--
556+-- | 'intersperseSuffix' followed by unfold and concat.
557+--
558+-- > intercalateSuffix unf a str = unfoldMany unf $ intersperseSuffix a str
559+-- > intersperseSuffix = intercalateSuffix (Unfold.function id)
560+-- > unlines = intercalateSuffix Unfold.fromList "\n"
561+--
562+-- >>> Stream.toList $ Stream.intercalateSuffix Unfold.fromList "\n" $ Stream.fromList ["abc", "def", "ghi"]
563+-- "abc\ndef\nghi\n"
564+--
565+-- @since 0.8.0
566+{-# INLINE intercalateSuffix #-}
567+intercalateSuffix :: (Monad m)
568+ => Unfold m b c -> b -> Stream m b -> Stream m c
569+intercalateSuffix unf seed str = fromStreamD $ D.unfoldMany unf
570+ $ D.intersperseSuffix (return seed) (toStreamD str)
571+
572+------------------------------------------------------------------------------
573+-- Combine N Streams - concatMap
574+------------------------------------------------------------------------------
575+
576+-- | Flatten a stream of streams to a single stream.
577+--
578+-- @
579+-- concat = concatMap id
580+-- @
581+--
582+-- /Pre-release/
583+{-# INLINE concat #-}
584+concat :: (Monad m) => Stream m (Stream m a) -> Stream m a
585+concat = concatMap id
586+
587+------------------------------------------------------------------------------
588+-- Combine N Streams - concatMap
589+------------------------------------------------------------------------------
590+
591+-- | Like 'concatMapWith' but carries a state which can be used to share
592+-- information across multiple steps of concat.
593+--
594+-- @
595+-- concatSmapMWith combine f initial = concatMapWith combine id . smapM f initial
596+-- @
597+--
598+-- /Pre-release/
599+--
600+{-# INLINE concatSmapMWith #-}
601+concatSmapMWith
602+ :: (Monad m)
603+ => (Stream m b -> Stream m b -> Stream m b)
604+ -> (s -> a -> m (s, Stream m b))
605+ -> m s
606+ -> Stream m a
607+ -> Stream m b
608+concatSmapMWith combine f initial =
609+ concatMapWith combine id . smapM f initial
610+
611+-- XXX Implement a StreamD version for fusion.
612+--
613+-- | Combine streams in pairs using a binary stream combinator, then combine
614+-- the resulting streams in pairs recursively until we get to a single combined
615+-- stream.
616+--
617+-- For example, you can sort a stream using merge sort like this:
618+--
619+-- >>> Stream.toList $ Stream.concatPairsWith (Stream.mergeBy compare) Stream.fromPure $ Stream.fromList [5,1,7,9,2]
620+-- [1,2,5,7,9]
621+--
622+-- /Caution: the stream of streams must be finite/
623+--
624+-- /Pre-release/
625+--
626+{-# INLINE concatPairsWith #-}
627+concatPairsWith ::
628+ (Stream m b -> Stream m b -> Stream m b)
629+ -> (a -> Stream m b)
630+ -> Stream m a
631+ -> Stream m b
632+concatPairsWith par f m =
633+ fromStreamK
634+ $ K.concatPairsWith
635+ (\s1 s2 -> toStreamK $ fromStreamK s1 `par` fromStreamK s2)
636+ (toStreamK . f)
637+ (toStreamK m)
638+
639+------------------------------------------------------------------------------
640+-- IterateMap - Map and flatten Trees of Streams
641+------------------------------------------------------------------------------
642+
643+-- | Like 'iterateM' but iterates after mapping a stream generator on the
644+-- output.
645+--
646+-- Yield an input element in the output stream, map a stream generator on it
647+-- and then do the same on the resulting stream. This can be used for a depth
648+-- first traversal of a tree like structure.
649+--
650+-- Note that 'iterateM' is a special case of 'iterateMapWith':
651+--
652+-- @
653+-- iterateM f = iterateMapWith serial (fromEffect . f) . fromEffect
654+-- @
655+--
656+-- It can be used to traverse a tree structure. For example, to list a
657+-- directory tree:
658+--
659+-- @
660+-- Stream.iterateMapWith Stream.serial
661+-- (either Dir.toEither (const nil))
662+-- (fromPure (Left "tmp"))
663+-- @
664+--
665+-- /Pre-release/
666+--
667+{-# INLINE iterateMapWith #-}
668+iterateMapWith ::
669+ (Stream m a -> Stream m a -> Stream m a)
670+ -> (a -> Stream m a)
671+ -> Stream m a
672+ -> Stream m a
673+iterateMapWith combine f = concatMapWith combine go
674+ where
675+ go x = fromPure x `combine` concatMapWith combine go (f x)
676+
677+-- | Same as @iterateMapWith Stream.serial@ but more efficient due to stream
678+-- fusion.
679+--
680+-- /Unimplemented/
681+{-# INLINE iterateUnfold #-}
682+iterateUnfold :: -- (MonadAsync m) =>
683+ Unfold m a a -> Stream m a -> Stream m a
684+iterateUnfold = undefined
685+
686+------------------------------------------------------------------------------
687+-- Flattening Graphs
688+------------------------------------------------------------------------------
689+
690+-- To traverse graphs we need a state to be carried around in the traversal.
691+-- For example, we can use a hashmap to store the visited status of nodes.
692+
693+-- | Like 'iterateMap' but carries a state in the stream generation function.
694+-- This can be used to traverse graph like structures, we can remember the
695+-- visited nodes in the state to avoid cycles.
696+--
697+-- Note that a combination of 'iterateMap' and 'usingState' can also be used to
698+-- traverse graphs. However, this function provides a more localized state
699+-- instead of using a global state.
700+--
701+-- See also: 'mfix'
702+--
703+-- /Pre-release/
704+--
705+{-# INLINE iterateSmapMWith #-}
706+iterateSmapMWith
707+ :: (Monad m)
708+ => (Stream m a -> Stream m a -> Stream m a)
709+ -> (b -> a -> m (b, Stream m a))
710+ -> m b
711+ -> Stream m a
712+ -> Stream m a
713+iterateSmapMWith combine f initial stream =
714+ concatMap
715+ (\b -> concatMapWith combine (go b) stream)
716+ (fromEffect initial)
717+
718+ where
719+
720+ go b a = fromPure a `combine` feedback b a
721+
722+ feedback b a =
723+ concatMap
724+ (\(b1, s) -> concatMapWith combine (go b1) s)
725+ (fromEffect $ f b a)
726+
727+------------------------------------------------------------------------------
728+-- Either streams
729+------------------------------------------------------------------------------
730+
731+-- Keep concating either streams as long as rights are generated, stop as soon
732+-- as a left is generated and concat the left stream.
733+--
734+-- See also: 'handle'
735+--
736+-- /Unimplemented/
737+--
738+{-
739+concatMapEitherWith
740+ :: -- (MonadAsync m) =>
741+ (forall x. t m x -> t m x -> t m x)
742+ -> (a -> t m (Either (Stream m b) b))
743+ -> Stream m a
744+ -> Stream m b
745+concatMapEitherWith = undefined
746+-}
747+
748+-- | In an 'Either' stream iterate on 'Left's. This is a special case of
749+-- 'iterateMapWith':
750+--
751+-- @
752+-- iterateMapLeftsWith combine f = iterateMapWith combine (either f (const nil))
753+-- @
754+--
755+-- To traverse a directory tree:
756+--
757+-- @
758+-- iterateMapLeftsWith serial Dir.toEither (fromPure (Left "tmp"))
759+-- @
760+--
761+-- /Pre-release/
762+--
763+{-# INLINE iterateMapLeftsWith #-}
764+iterateMapLeftsWith
765+ :: (b ~ Either a c)
766+ => (Stream m b -> Stream m b -> Stream m b)
767+ -> (a -> Stream m b)
768+ -> Stream m b
769+ -> Stream m b
770+iterateMapLeftsWith combine f =
771+ iterateMapWith combine (either f (const nil))
core/src/Streamly/Internal/Data/Stream/Generate.hs+4−65
@@ -8,10 +8,7 @@
88--
99module Streamly.Internal.Data.Stream.Generate
1010 (
11− cons
12− , consM
13− , unfold
14− , append
11+ unfold
1512 )
1613where
1714
@@ -21,12 +18,12 @@ import Data.Semigroup (Semigroup(..))
2118#if __GLASGOW_HASKELL__ < 808
2219import Data.Semigroup (Semigroup(..))
2320#endif
21+
2422import Streamly.Internal.Data.Stream.Type (Stream)
2523import Streamly.Internal.Data.Unfold (Unfold)
2624
25+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
2726import 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
3027
3128-- $setup
3229-- >>> :m
@@ -34,42 +31,6 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as StreamK
3431-- >>> import qualified Streamly.Data.Unfold as Unfold
3532-- >>> import qualified Streamly.Internal.Data.Stream as Stream
3633
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
7334
7435------------------------------------------------------------------------------
7536-- From Unfold
@@ -86,26 +47,4 @@ consM m = Stream.fromStreamK . StreamK.consM m . Stream.toStreamK
8647-- /Pre-release/
8748{-# INLINE unfold #-}
8849unfold :: Monad m => Unfold m a b -> a -> Stream m b
89−unfold unf = Stream.fromStreamD . StreamD.unfold unf
90−
91−------------------------------------------------------------------------------
92−-- Combining
93−------------------------------------------------------------------------------
94−
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.
99−--
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]
104−--
105−-- This operation can be used to fold an infinite lazy container of streams.
106−--
107−-- /Pre-release/
108−--
109−{-# INLINE append #-}
110−append :: Stream m a -> Stream m a -> Stream m a
111−append = (<>)
50+unfold unf = Stream.fromStreamD . D.unfold unf
core/src/Streamly/Internal/Data/Stream/Lift.hsnew file+147
@@ -0,0 +1,147 @@
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+------------------------------------------------------------------------------
38+-- Generalize the underlying monad
39+------------------------------------------------------------------------------
40+
41+-- | Transform the inner monad of a stream using a natural transformation.
42+--
43+-- / Internal/
44+--
45+{-# INLINE hoist #-}
46+hoist :: (Monad m, Monad n)
47+ => (forall x. m x -> n x) -> Stream m a -> Stream n a
48+hoist f xs = fromStreamK $ K.hoist f (toStreamK xs)
49+
50+-- | Generalize the inner monad of the stream from 'Identity' to any monad.
51+--
52+-- / Internal/
53+--
54+{-# INLINE generally #-}
55+generally :: (Monad m) => Stream Identity a -> Stream m a
56+generally xs = fromStreamK $ K.hoist (return . runIdentity) (toStreamK xs)
57+
58+------------------------------------------------------------------------------
59+-- Add and remove a monad transformer
60+------------------------------------------------------------------------------
61+
62+-- | Lift the inner monad @m@ of a stream @Stream m a@ to @tr m@ using the monad
63+-- transformer @tr@.
64+--
65+-- @since 0.8.0
66+--
67+{-# INLINE liftInner #-}
68+liftInner :: (Monad m, MonadTrans tr, Monad (tr m))
69+ => Stream m a -> Stream (tr m) a
70+liftInner xs = fromStreamD $ D.liftInner (toStreamD xs)
71+
72+------------------------------------------------------------------------------
73+-- Sharing read only state in a stream
74+------------------------------------------------------------------------------
75+
76+-- | Evaluate the inner monad of a stream as 'ReaderT'.
77+--
78+-- @since 0.8.0
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+-- This is supported only for 'Stream' as concurrent state updation may not be
106+-- safe.
107+--
108+-- > evalStateT s = Stream.map snd . Stream.runStateT s
109+--
110+-- / Internal/
111+--
112+{-# INLINE evalStateT #-}
113+evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a
114+-- evalStateT s = fmap snd . runStateT s
115+evalStateT s xs = fromStreamD $ D.evalStateT s (toStreamD xs)
116+
117+-- | Run a stateful (StateT) stream transformation using a given state.
118+--
119+-- This is supported only for 'Stream' as concurrent state updation may not be
120+-- safe.
121+--
122+-- > usingStateT s f = evalStateT s . f . liftInner
123+--
124+-- See also: 'scanl''
125+--
126+-- / Internal/
127+--
128+{-# INLINE usingStateT #-}
129+usingStateT
130+ :: Monad m
131+ => m s
132+ -> (Stream (StateT s m) a -> Stream (StateT s m) a)
133+ -> Stream m a
134+ -> Stream m a
135+usingStateT s f = evalStateT s . f . liftInner
136+
137+-- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting
138+-- state and value pair after each step.
139+--
140+-- This is supported only for 'Stream' as concurrent state updation may not be
141+-- safe.
142+--
143+-- @since 0.8.0
144+--
145+{-# INLINE runStateT #-}
146+runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)
147+runStateT s xs = fromStreamD $ D.runStateT s (toStreamD xs)
core/src/Streamly/Internal/Data/Stream/Reduce.hsnew file+162
@@ -0,0 +1,162 @@
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+
14+ -- * Reduce By Folds
15+ -- |
16+ -- Reduce a stream by folding . Functions
17+ -- generally ending in these shapes:
18+ --
19+ -- @
20+ -- f (Fold m a b) -> Stream m a -> Stream m b
21+ -- f (Parser m a b) -> Stream m a -> Stream m b
22+ -- @
23+
24+ -- ** Generic Folding
25+ -- | Apply folds on a stream.
26+ foldMany
27+ , foldManyPost
28+ , refoldMany
29+ , foldSequence
30+ , foldIterateM
31+ , refoldIterateM
32+ )
33+where
34+
35+import Streamly.Internal.Data.Fold.Type (Fold (..))
36+import Streamly.Internal.Data.Refold.Type (Refold (..))
37+import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
38+
39+import qualified Streamly.Internal.Data.Stream.StreamD as D
40+
41+import Prelude hiding (concatMap, map)
42+
43+-- $setup
44+-- >>> :m
45+-- >>> import Prelude hiding (zipWith, concatMap, concat)
46+-- >>> import Streamly.Internal.Data.Stream as Stream
47+-- >>> import qualified Streamly.Data.Fold as Fold
48+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
49+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
50+-- >>> import qualified Streamly.Internal.Data.Parser as Parser
51+-- >>> import qualified Streamly.Data.Array.Foreign as Array
52+
53+------------------------------------------------------------------------------
54+-- Folding
55+------------------------------------------------------------------------------
56+
57+-- Splitting operations that take a predicate and a Fold can be
58+-- expressed using parseMany. Operations like chunksOf, intervalsOf, split*,
59+-- can be expressed using parseMany when used with an appropriate Parser.
60+--
61+-- XXX We need takeGE/takeBetween to implement "some" using "many".
62+
63+-- | Like 'foldMany' but appends empty fold output if the fold and stream
64+-- termination aligns:
65+--
66+-- >>> f = Fold.take 2 Fold.sum
67+-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList []
68+-- [0]
69+-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList [1..9]
70+-- [3,7,11,15,9]
71+-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList [1..10]
72+-- [3,7,11,15,19,0]
73+--
74+-- /Pre-release/
75+--
76+{-# INLINE foldManyPost #-}
77+foldManyPost
78+ :: (Monad m)
79+ => Fold m a b
80+ -> Stream m a
81+ -> Stream m b
82+foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
83+
84+-- | Apply a 'Fold' repeatedly on a stream and emit the fold outputs in the
85+-- output stream.
86+--
87+-- To sum every two contiguous elements in a stream:
88+--
89+-- >>> f = Fold.take 2 Fold.sum
90+-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.unfold Unfold.fromList [1..10]
91+-- [3,7,11,15,19]
92+--
93+-- On an empty stream the output is empty:
94+--
95+-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.unfold Unfold.fromList []
96+-- []
97+--
98+-- Note @Stream.foldMany (Fold.take 0)@ would result in an infinite loop in a
99+-- non-empty stream.
100+--
101+-- @since 0.8.0
102+--
103+{-# INLINE foldMany #-}
104+foldMany
105+ :: (Monad m)
106+ => Fold m a b
107+ -> Stream m a
108+ -> Stream m b
109+foldMany f m = fromStreamD $ D.foldMany f (toStreamD m)
110+
111+-- | Like 'foldMany' but using the 'Refold' type instead of 'Fold'.
112+--
113+-- /Pre-release/
114+{-# INLINE refoldMany #-}
115+refoldMany :: (Monad m) =>
116+ Refold m c a b -> m c -> Stream m a -> Stream m b
117+refoldMany f action = fromStreamD . D.refoldMany f action . toStreamD
118+
119+-- | Apply a stream of folds to an input stream and emit the results in the
120+-- output stream.
121+--
122+-- /Unimplemented/
123+--
124+{-# INLINE foldSequence #-}
125+foldSequence
126+ :: -- (Monad m) =>
127+ Stream m (Fold m a b)
128+ -> Stream m a
129+ -> Stream m b
130+foldSequence _f _m = undefined
131+
132+-- | Iterate a fold generator on a stream. The initial value @b@ is used to
133+-- generate the first fold, the fold is applied on the stream and the result of
134+-- the fold is used to generate the next fold and so on.
135+--
136+-- @
137+-- >>> import Data.Monoid (Sum(..))
138+-- >>> f x = return (Fold.take 2 (Fold.sconcat x))
139+-- >>> s = Stream.map Sum $ Stream.unfold Unfold.fromList [1..10]
140+-- >>> Stream.fold Fold.toList $ Stream.map getSum $ Stream.foldIterateM f (pure 0) s
141+-- [3,10,21,36,55,55]
142+--
143+-- @
144+--
145+-- This is the streaming equivalent of monad like sequenced application of
146+-- folds where next fold is dependent on the previous fold.
147+--
148+-- /Pre-release/
149+--
150+{-# INLINE foldIterateM #-}
151+foldIterateM ::
152+ (Monad m) => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b
153+foldIterateM f i m = fromStreamD $ D.foldIterateM f i (toStreamD m)
154+
155+-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be
156+-- much more efficient due to stream fusion.
157+--
158+-- /Internal/
159+{-# INLINE refoldIterateM #-}
160+refoldIterateM :: (Monad m) =>
161+ Refold m b a b -> m b -> Stream m a -> Stream m b
162+refoldIterateM c i m = fromStreamD $ D.refoldIterateM c i (toStreamD m)
core/src/Streamly/Internal/Data/Stream/Transform.hs+922−34
@@ -8,80 +8,968 @@
88--
99module Streamly.Internal.Data.Stream.Transform
1010 (
11− filter
12− , foldFilter
11+ -- * Piping
12+ -- | Pass through a 'Pipe'.
13+ transform
14+
15+ -- * Folding
16+ , foldrS
17+ , foldrSShared
18+ -- , foldrT
19+
20+ -- * Mapping
21+ -- | Stateless one-to-one maps.
1322 , map
23+ , sequence
1424 , mapM
25+ , smapM
26+
27+ -- * Mapping Side Effects (Observation)
28+ -- | See also the intersperse*_ combinators.
29+ , trace
30+ , trace_
31+ , tap
32+ --, tapOffsetEvery
33+ --, tapAsync
34+ --, tapAsyncK
35+ --, distributeAsync_
36+ --, pollCounts
37+
38+ -- * Scanning By 'Fold'
39+ , scan
40+ , scanMany
41+ , postscan
42+
43+ -- * Scanning
44+ -- | Left scans. Stateful, mostly one-to-one maps.
45+ , scanl'
46+ , scanlM'
47+ , scanlMAfter'
48+ , postscanlMAfter'
49+ , postscanl'
50+ , postscanlM'
51+ , prescanl'
52+ , prescanlM'
53+ , scanl1'
54+ , scanl1M'
55+
56+ -- XXX Once we have pipes the contravariant transformations can be
57+ -- represented by attaching pipes before a transformation.
58+ --
59+ -- , lscanl'
60+ -- , lscanlM'
61+ -- , lscanl1'
62+ -- , lscanl1M'
63+ --
64+ -- , lpostscanl'
65+ -- , lpostscanlM'
66+ -- , lprescanl'
67+ -- , lprescanlM'
68+
69+ -- * Filtering
70+ -- | Produce a subset of the stream using criteria based on the values of
71+ -- the elements. We can use a concatMap and scan for filtering but these
72+ -- combinators are more efficient and convenient.
73+
74+ --, with
75+ , deleteBy
76+ , filter
77+ , filterM
78+ , foldFilter
79+ , uniq
80+ , uniqBy
81+ --, nubBy
82+ --, nubWindowBy
83+ --, prune
84+ --, repeated
85+
86+ -- * Trimming
87+ -- | Produce a subset of the stream trimmed at ends.
88+
89+ , take
90+ , takeInterval
91+ --, takeLast
92+ --, takeLastInterval
93+ , takeWhile
94+ , takeWhileM
95+ --, takeWhileLast
96+ --, takeWhileAround
97+ , drop
98+ , dropInterval
99+ --, dropLast
100+ --, dropLastInterval
101+ , dropWhile
102+ , dropWhileM
103+ --, dropWhileLast
104+ --, dropWhileAround
105+
106+ -- * Inserting Elements
107+ -- | Produce a superset of the stream. This is the opposite of
108+ -- filtering/sampling. We can always use concatMap and scan for inserting
109+ -- but these combinators are more efficient and convenient.
110+
111+ -- Element agnostic (Opposite of sampling)
112+ , intersperse
113+ , intersperseM -- XXX naming
114+ --, intersperseBySpan
115+
116+ , intersperseSuffix
117+ , intersperseSuffixBySpan
118+ --, interjectSuffix
119+
120+ -- , interspersePrefix
121+ -- , interspersePrefixBySpan
122+
123+ -- * Inserting Side Effects/Time
124+ , intersperseM_ -- XXX naming
125+ , delay
126+ , intersperseSuffix_
127+ , delayPost
128+ , interspersePrefix_
129+ , delayPre
130+
131+ -- * Element Aware Insertion
132+ -- | Opposite of filtering
133+ , insertBy
134+ -- , intersperseByBefore
135+ -- , intersperseByAfter
136+
137+ -- * Reordering
138+ , reverse
139+ , reverse'
140+ -- , reassembleBy
141+
142+ -- * Position Indexing
143+ , indexed
144+ , indexedR
145+
146+ -- * Searching
147+ , findIndices -- XXX indicesBy
148+ , elemIndices -- XXX indicesOf
149+
150+ -- * Rolling map
151+ -- | Map using the previous element.
152+ , rollingMapM
153+ , rollingMap
154+ , rollingMap2
155+
156+ -- * Maybe Streams
157+ -- Move these to Streamly.Data.Maybe.Stream?
158+ , catMaybes -- XXX justs (like lefts/rights)
159+ , mapMaybe
160+ , mapMaybeM
161+
162+ -- * Either Streams
163+ -- Move these to Streamly.Data.Either.Stream?
164+ , lefts
165+ , rights
166+ , both
15167 )
16168where
17169
170+import Control.Concurrent (threadDelay)
171+import Control.Monad (void)
172+import Control.Monad.IO.Class (MonadIO (liftIO))
173+import Data.Either (fromLeft, isLeft, isRight, fromRight)
174+import Data.Maybe (isJust, fromJust)
175+
18176import Streamly.Internal.Data.Fold.Type (Fold)
19−import Streamly.Internal.Data.Stream.Type (Stream)
177+import Streamly.Internal.Data.Pipe (Pipe)
178+import Streamly.Internal.Data.Time.Units (TimeUnit64)
179+
180+import qualified Streamly.Internal.Data.Fold as FL
181+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
182+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
183+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
20184
21−import qualified Streamly.Internal.Data.Stream.Type as Stream
22−import qualified Streamly.Internal.Data.Stream.StreamD.Transform as StreamD
185+import Streamly.Internal.Data.Stream.Bottom
186+import Streamly.Internal.Data.Stream.Type
23187
24−import Prelude hiding (map, mapM, filter)
188+import Prelude hiding
189+ ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence
190+ , reverse, foldr1 , repeat, scanl, scanl1, zipWith)
191+
192+#include "Instances.hs"
193+#include "inline.hs"
25194
26195--
27196-- $setup
28197-- >>> :m
198+-- >>> import Control.Concurrent (threadDelay)
199+-- >>> import Data.Function ((&))
200+-- >>> import Prelude hiding ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
201+-- >>> import Streamly.Internal.Data.Stream as Stream
202+-- >>> import qualified Streamly.Data.Fold as Fold
29203-- >>> 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
204+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
205+-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
206+--
207+-- >>> hSetBuffering stdout LineBuffering
32208
33209------------------------------------------------------------------------------
34210-- Transformation by Mapping
35211------------------------------------------------------------------------------
36212
37213-- |
38−-- >> mapM f = Stream.sequence . Stream.map f
214+-- @
215+-- mapM f = sequence . map f
216+-- @
39217--
40218-- Apply a monadic function to each element of the stream and replace it with
41219-- the output of the resulting action.
42220--
43−-- >>> s = Stream.unfold Unfold.fromList ["a", "b", "c"]
44−-- >>> Stream.fold Fold.drain $ Stream.mapM putStr s
221+-- @
222+-- > drain $ Stream.mapM putStr $ Stream.unfold Unfold.fromList ["a", "b", "c"]
45223-- abc
46224--
47−-- /Pre-release/
225+-- > :{
226+-- drain $ Stream.replicateM 10 (return 1)
227+-- & (fromSerial . Stream.mapM (\x -> threadDelay 1000000 >> print x))
228+-- :}
229+-- 1
230+-- ...
231+-- 1
48232--
49−{-# INLINE mapM #-}
50−mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
51−mapM f = Stream.fromStreamD . StreamD.mapM f . Stream.toStreamD
52−
53−-- |
54−-- >>> map = fmap
233+-- > drain $ Stream.replicateM 10 (return 1)
234+-- & (fromAsync . Stream.mapM (\x -> threadDelay 1000000 >> print x))
235+-- @
55236--
56−-- Same as 'fmap'.
57−--
58−-- >>> s = Stream.unfold Unfold.fromList [1,2,3]
59−-- >>> Stream.fold Fold.toList $ Stream.map (+1) s
60−-- [2,3,4]
237+-- /Concurrent (do not use with 'fromParallel' on infinite streams)/
61238--
62239-- /Pre-release/
63240--
64−{-# INLINE map #-}
65−map :: Monad m => (a -> b) -> Stream m a -> Stream m b
66−map f = mapM (return . f)
241+{-# INLINE mapM #-}
242+mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
243+mapM f m = fromStreamK $ D.toStreamK $ D.mapM f $ toStreamD m
67244
68245-- | Include only those elements that pass a predicate.
69246--
70247{-# INLINE filter #-}
71248filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
72−filter p = Stream.fromStreamD . StreamD.filter p . Stream.toStreamD
249+filter p m = fromStreamD $ D.filter p $ toStreamD m
250+
251+-- | Same as 'filter' but with a monadic predicate.
252+--
253+-- @since 0.4.0
254+{-# INLINE filterM #-}
255+filterM :: (Monad m) => (a -> m Bool) -> Stream m a -> Stream m a
256+filterM p m = fromStreamD $ D.filterM p $ toStreamD m
73257
74258-- | Use a filtering fold on a stream.
75259--
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
260+-- > Stream.sum $ Stream.foldFilter (Fold.satisfy (> 5)) $ Stream.unfold Unfold.fromList [1..10]
261+-- 40
82262--
83263-- /Pre-release/
84264--
85265{-# INLINE foldFilter #-}
86266foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
87−foldFilter p = Stream.fromStreamD . StreamD.foldFilter p . Stream.toStreamD
267+foldFilter p = fromStreamD . D.foldFilter p . toStreamD
268+
269+------------------------------------------------------------------------------
270+-- Piping
271+------------------------------------------------------------------------------
272+
273+-- | Use a 'Pipe' to transform a stream.
274+--
275+-- /Pre-release/
276+--
277+{-# INLINE transform #-}
278+transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b
279+transform pipe xs = fromStreamD $ D.transform pipe (toStreamD xs)
280+
281+------------------------------------------------------------------------------
282+-- Transformation Folds
283+------------------------------------------------------------------------------
284+
285+-- | Right fold to a streaming monad.
286+--
287+-- > foldrS Stream.cons Stream.nil === id
288+--
289+-- 'foldrS' can be used to perform stateless stream to stream transformations
290+-- like map and filter in general. It can be coupled with a scan to perform
291+-- stateful transformations. However, note that the custom map and filter
292+-- routines can be much more efficient than this due to better stream fusion.
293+--
294+-- >>> Stream.fold Fold.toList $ Stream.foldrS Stream.cons Stream.nil $ Stream.unfold Unfold.fromList [1..5]
295+-- [1,2,3,4,5]
296+--
297+-- Find if any element in the stream is 'True':
298+--
299+-- >>> Stream.fold Fold.toList $ Stream.foldrS (\x xs -> if odd x then return True else xs) (return False) $ (Stream.unfold Unfold.fromList (2:4:5:undefined) :: Stream IO Int)
300+-- [True]
301+--
302+-- Map (+2) on odd elements and filter out the even elements:
303+--
304+-- >>> Stream.fold Fold.toList $ Stream.foldrS (\x xs -> if odd x then (x + 2) `Stream.cons` xs else xs) Stream.nil $ (Stream.unfold Unfold.fromList [1..5] :: Stream IO Int)
305+-- [3,5,7]
306+--
307+-- 'foldrM' can also be represented in terms of 'foldrS', however, the former
308+-- iStream much more efficient:
309+--
310+-- > foldrM f z s = runIdentityT $ foldrS (\x xs -> lift $ f x (runIdentityT xs)) (lift z) s
311+--
312+-- /Pre-release/
313+{-# INLINE foldrS #-}
314+foldrS ::
315+ (a -> Stream m b -> Stream m b)
316+ -> Stream m b
317+ -> Stream m a
318+ -> Stream m b
319+foldrS f z xs =
320+ fromStreamK
321+ $ K.foldrS
322+ (\y ys -> toStreamK $ f y (fromStreamK ys))
323+ (toStreamK z)
324+ (toStreamK xs)
325+
326+{-# INLINE foldrSShared #-}
327+foldrSShared ::
328+ (a -> Stream m b -> Stream m b)
329+ -> Stream m b
330+ -> Stream m a
331+ -> Stream m b
332+foldrSShared f z xs =
333+ fromStreamK
334+ $ K.foldrSShared
335+ (\y ys -> toStreamK $ f y (fromStreamK ys))
336+ (toStreamK z)
337+ (toStreamK xs)
338+
339+{-# INLINE sequence #-}
340+sequence :: (Monad m) => Stream m (m a) -> Stream m a
341+sequence = mapM id
342+
343+{-# INLINE tap #-}
344+tap :: Monad m => FL.Fold m a b -> Stream m a -> Stream m a
345+tap f xs = fromStreamD $ D.tap f (toStreamD xs)
346+
347+-- | Apply a monadic function to each element flowing through the stream and
348+-- discard the results.
349+--
350+-- @
351+-- >>> Stream.fold Fold.drain $ Stream.trace print (Stream.unfold Unfold.fromList[1, 2])
352+-- 1
353+-- 2
354+--
355+-- @
356+--
357+-- Compare with 'tap'.
358+--
359+-- @since 0.7.0
360+{-# INLINE trace #-}
361+trace :: (Monad m) => (a -> m b) -> Stream m a -> Stream m a
362+trace f = mapM (\x -> void (f x) >> return x)
363+
364+-- | Perform a side effect before yielding each element of the stream and
365+-- discard the results.
366+--
367+-- @
368+-- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") (Stream.unfold Unfold.fromList[1, 2])
369+-- "got here"
370+-- "got here"
371+--
372+-- @
373+--
374+-- Same as 'interspersePrefix_' but always serial.
375+--
376+-- See also: 'trace'
377+--
378+-- /Pre-release/
379+{-# INLINE trace_ #-}
380+trace_ :: (Monad m) => m b -> Stream m a -> Stream m a
381+trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD
382+
383+------------------------------------------------------------------------------
384+-- Scanning with a Fold
385+------------------------------------------------------------------------------
386+
387+-- | Scan a stream using the given monadic fold.
388+--
389+-- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum (Stream.unfold Unfold.fromList [1..10])
390+-- [0,1,3,6]
391+--
392+-- @since 0.7.0
393+{-# INLINE scan #-}
394+scan :: (Monad m) => Fold m a b -> Stream m a -> Stream m b
395+scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
396+
397+-- | Like 'scan' but restarts scanning afresh when the scanning fold
398+-- terminates.
399+--
400+-- /Pre-release/
401+{-# INLINE scanMany #-}
402+scanMany :: (Monad m) => Fold m a b -> Stream m a -> Stream m b
403+scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m
404+
405+-- | Postscan a stream using the given monadic fold.
406+--
407+-- The following example extracts the input stream up to a point where the
408+-- running average of elements is no more than 10:
409+--
410+-- >>> import Data.Maybe (fromJust)
411+-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
412+-- >>> :{
413+-- Stream.fold Fold.toList
414+-- $ Stream.map (fromJust . fst)
415+-- $ Stream.takeWhile (\(_,x) -> x <= 10)
416+-- $ Stream.postscan (Fold.tee Fold.last avg) (Stream.unfold Unfold.fromList $ enumFromTo 1.0 100.0)
417+-- :}
418+-- [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]
419+--
420+-- @since 0.7.0
421+{-# INLINE postscan #-}
422+postscan :: (Monad m) => Fold m a b -> Stream m a -> Stream m b
423+postscan fld = fromStreamD . D.postscanOnce fld . toStreamD
424+
425+-- XXX this needs to be concurrent
426+-- XXX because of the use of D.cons for appending, scanlM' has quadratic
427+-- complexity when iterated over a stream. We should use StreamK style scanlM'
428+-- for linear performance on iteration.
429+--
430+-- | Like 'scanl'' but with a monadic step function and a monadic seed.
431+--
432+-- /Since: 0.4.0/
433+--
434+-- /Since: 0.8.0 (signature change)/
435+{-# INLINE scanlM' #-}
436+scanlM' :: (Monad m) => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
437+scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
438+
439+-- XXX because of the use of D.cons for appending, scanl' has quadratic
440+-- complexity when iterated over a stream. We should use StreamK style scanlM'
441+-- for linear performance on iteration.
442+
443+-- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,
444+-- however it adds an extra element.
445+--
446+-- @
447+-- >>> Stream.fold Fold.toList $ Stream.scanl' (+) 0 $ Stream.unfold Unfold.fromList [1,2,3,4]
448+-- [0,1,3,6,10]
449+--
450+-- @
451+--
452+-- @
453+-- >>> Stream.fold Fold.toList $ Stream.scanl' (flip (:)) [] $ Stream.unfold Unfold.fromList [1,2,3,4]
454+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
455+--
456+-- @
457+--
458+-- The output of 'scanl'' is the initial value of the accumulator followed by
459+-- all the intermediate steps and the final result of 'foldl''.
460+--
461+-- By streaming the accumulated state after each fold step, we can share the
462+-- state across multiple stages of stream composition. Each stage can modify or
463+-- extend the state, do some processing with it and emit it for the next stage,
464+-- thus modularizing the stream processing. This can be useful in
465+-- stateful or event-driven programming.
466+--
467+-- Consider the following monolithic example, computing the sum and the product
468+-- of the elements in a stream in one go using a @foldl'@:
469+--
470+-- @
471+-- >>> Stream.foldl' (\(s, p) x -> (s + x, p * x)) (0,1) $ Stream.unfold Unfold.fromList [1,2,3,4]
472+-- (10,24)
473+--
474+-- @
475+--
476+-- Using @scanl'@ we can make iStream modular by computing the sum in the first
477+-- stage and passing it down to the next stage for computing the product:
478+--
479+-- @
480+-- >>> :{
481+-- Stream.foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)
482+-- $ Stream.scanl' (\(s, _) x -> (s + x, x)) (0,1)
483+-- $ Stream.unfold Unfold.fromList [1,2,3,4]
484+-- :}
485+-- (10,24)
486+--
487+-- @
488+--
489+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
490+-- lazy expressions inside the accumulator, it is recommended that a strict
491+-- data structure is used for accumulator.
492+--
493+-- >>> scanl' step z = scan (Fold.foldl' step z)
494+-- >>> scanl' f z xs = scanlM' (\a b -> return (f a b)) (return z) xs
495+-- >>> scanl' f z xs = z `Stream.cons` postscanl' f z xs
496+--
497+-- See also: 'usingStateT'
498+--
499+-- @since 0.2.0
500+{-# INLINE scanl' #-}
501+scanl' :: (Monad m) => (b -> a -> b) -> b -> Stream m a -> Stream m b
502+scanl' step z m = fromStreamD $ D.scanl' step z $ toStreamD m
503+
504+-- | Like 'scanl'' but does not stream the initial value of the accumulator.
505+--
506+-- >>> postscanl' step z = postscan (Fold.foldl' step z)
507+-- >>> postscanl' f z = postscanlM' (\a b -> return (f a b)) (return z)
508+-- >>> postscanl' f z xs = Stream.drop 1 $ Stream.scanl' f z xs
509+--
510+-- @since 0.7.0
511+{-# INLINE postscanl' #-}
512+postscanl' :: (Monad m) => (b -> a -> b) -> b -> Stream m a -> Stream m b
513+postscanl' step z m = fromStreamD $ D.postscanl' step z $ toStreamD m
514+
515+-- XXX prescanl does not sound very useful, enable only if there is a
516+-- compelling use case.
517+--
518+-- | Like scanl' but does not stream the final value of the accumulator.
519+--
520+-- /Pre-release/
521+{-# INLINE prescanl' #-}
522+prescanl' :: (Monad m) => (b -> a -> b) -> b -> Stream m a -> Stream m b
523+prescanl' step z m = fromStreamD $ D.prescanl' step z $ toStreamD m
524+
525+-- XXX this needs to be concurrent
526+-- | Like prescanl' but with a monadic step function and a monadic seed.
527+--
528+-- /Pre-release/
529+{-# INLINE prescanlM' #-}
530+prescanlM' :: (Monad m) => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
531+prescanlM' step z m = fromStreamD $ D.prescanlM' step z $ toStreamD m
532+
533+-- XXX this needs to be concurrent
534+-- | Like 'scanl1'' but with a monadic step function.
535+--
536+-- @since 0.6.0
537+{-# INLINE scanl1M' #-}
538+scanl1M' :: (Monad m) => (a -> a -> m a) -> Stream m a -> Stream m a
539+scanl1M' step m = fromStreamD $ D.scanl1M' step $ toStreamD m
540+
541+-- | Like 'scanl'' but for a non-empty stream. The first element of the stream
542+-- is used as the initial value of the accumulator. Does nothing if the stream
543+-- is empty.
544+--
545+-- @
546+-- >>> Stream.fold Fold.toList $ Stream.scanl1' (+) $ Stream.unfold Unfold.fromList [1,2,3,4]
547+-- [1,3,6,10]
548+--
549+-- @
550+--
551+-- @since 0.6.0
552+{-# INLINE scanl1' #-}
553+scanl1' :: (Monad m) => (a -> a -> a) -> Stream m a -> Stream m a
554+scanl1' step m = fromStreamD $ D.scanl1' step $ toStreamD m
555+
556+-- | Drop repeated elements that are adjacent to each other using the supplied
557+-- comparison function.
558+--
559+-- @uniq = uniqBy (==)
560+--
561+-- To strip duplicate path separators:
562+--
563+-- @
564+-- f x y = x == '/' && x == y
565+-- Stream.fold Fold.toList $ Stream.uniqBy f $ Stream.unfold Unfold.fromList "//a//b"
566+-- "/a/b"
567+-- @
568+--
569+-- Space: @O(1)@
570+--
571+-- See also: 'nubBy'.
572+--
573+-- /Pre-release/
574+--
575+{-# INLINE uniqBy #-}
576+uniqBy :: (Monad m) =>
577+ (a -> a -> Bool) -> Stream m a -> Stream m a
578+uniqBy eq = catMaybes . rollingMap f
579+
580+ where
581+
582+ f pre curr =
583+ case pre of
584+ Nothing -> Just curr
585+ Just x -> if x `eq` curr then Nothing else Just curr
586+
587+-- | Drop repeated elements that are adjacent to each other.
588+--
589+-- @since 0.6.0
590+{-# INLINE uniq #-}
591+uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
592+uniq = fromStreamD . D.uniq . toStreamD
593+
594+-- | Deletes the first occurrence of the element in the stream that satisfies
595+-- the given equality predicate.
596+--
597+-- @
598+-- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 $ Stream.unfold Unfold.fromList [1,3,3,5]
599+-- [1,3,5]
600+--
601+-- @
602+--
603+-- @since 0.6.0
604+{-# INLINE deleteBy #-}
605+deleteBy :: (Monad m) => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
606+deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
607+
608+------------------------------------------------------------------------------
609+-- Trimming
610+------------------------------------------------------------------------------
611+
612+-- | Same as 'takeWhile' but with a monadic predicate.
613+--
614+-- @since 0.4.0
615+{-# INLINE takeWhileM #-}
616+takeWhileM :: (Monad m) => (a -> m Bool) -> Stream m a -> Stream m a
617+takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
618+
619+-- | @takeInterval duration@ yields stream elements upto specified time
620+-- @duration@. The duration starts when the stream is evaluated for the first
621+-- time, before the first element is yielded. The time duration is checked
622+-- before generating each element, if the duration has expired the stream
623+-- stops.
624+--
625+-- The total time taken in executing the stream is guaranteed to be /at least/
626+-- @duration@, however, because the duration is checked before generating an
627+-- element, the upper bound is indeterminate and depends on the time taken in
628+-- generating and processing the last element.
629+--
630+-- No element is yielded if the duration is zero. At least one element is
631+-- yielded if the duration is non-zero.
632+--
633+-- /Pre-release/
634+--
635+{-# INLINE takeInterval #-}
636+takeInterval ::(MonadIO m, TimeUnit64 d) => d -> Stream m a -> Stream m a
637+takeInterval d = fromStreamD . D.takeByTime d . toStreamD
638+
639+-- | Drop elements in the stream as long as the predicate succeeds and then
640+-- take the rest of the stream.
641+--
642+-- @since 0.1.0
643+{-# INLINE dropWhile #-}
644+dropWhile :: (Monad m) => (a -> Bool) -> Stream m a -> Stream m a
645+dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m
646+
647+-- | Same as 'dropWhile' but with a monadic predicate.
648+--
649+-- @since 0.4.0
650+{-# INLINE dropWhileM #-}
651+dropWhileM :: (Monad m) => (a -> m Bool) -> Stream m a -> Stream m a
652+dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
653+
654+-- | @dropInterval duration@ drops stream elements until specified @duration@ has
655+-- passed. The duration begins when the stream is evaluated for the first
656+-- time. The time duration is checked /after/ generating a stream element, the
657+-- element is yielded if the duration has expired otherwise it is dropped.
658+--
659+-- The time elapsed before starting to generate the first element is /aStream most/
660+-- @duration@, however, because the duration expiry is checked after the
661+-- element is generated, the lower bound is indeterminate and depends on the
662+-- time taken in generating an element.
663+--
664+-- All elements are yielded if the duration is zero.
665+--
666+-- /Pre-release/
667+--
668+{-# INLINE dropInterval #-}
669+dropInterval ::(MonadIO m, TimeUnit64 d) => d -> Stream m a -> Stream m a
670+dropInterval d = fromStreamD . D.dropByTime d . toStreamD
671+
672+------------------------------------------------------------------------------
673+-- Inserting Elements
674+------------------------------------------------------------------------------
675+
676+-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
677+-- @stream@ that is less than @elem@ when compared using @cmp@.
678+--
679+-- @
680+-- insertBy cmp x = 'mergeBy' cmp ('fromPure' x)
681+-- @
682+--
683+-- @
684+-- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 $ Stream.unfold Unfold.fromList [1,3,5]
685+-- [1,2,3,5]
686+--
687+-- @
688+--
689+-- @since 0.6.0
690+{-# INLINE insertBy #-}
691+insertBy ::(Monad m) => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
692+insertBy cmp x m = fromStreamD $ D.insertBy cmp x (toStreamD m)
693+
694+-- | Insert a pure value between successive elements of a stream.
695+--
696+-- >>> Stream.fold Fold.toList $ Stream.intersperse ',' $ Stream.unfold Unfold.fromList "hello"
697+-- "h,e,l,l,o"
698+--
699+-- @since 0.7.0
700+{-# INLINE intersperse #-}
701+intersperse :: (Monad m) => a -> Stream m a -> Stream m a
702+intersperse a = fromStreamD . D.intersperse a . toStreamD
703+
704+-- | Insert a side effect before consuming an element of a stream except the
705+-- first one.
706+--
707+-- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') $ Stream.unfold Unfold.fromList "hello"
708+-- h.e.l.l.o
709+--
710+-- /Pre-release/
711+{-# INLINE intersperseM_ #-}
712+intersperseM_ :: (Monad m) => m b -> Stream m a -> Stream m a
713+intersperseM_ m = fromStreamD . D.intersperseM_ m . toStreamD
714+
715+-- | Insert an effect and its output after consuming an element of a stream.
716+--
717+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ intersperseSuffix (putChar '.' >> return ',') $ Stream.unfold Unfold.fromList "hello"
718+-- h.,e.,l.,l.,o.,"h,e,l,l,o,"
719+--
720+-- /Pre-release/
721+{-# INLINE intersperseSuffix #-}
722+intersperseSuffix :: (Monad m) => m a -> Stream m a -> Stream m a
723+intersperseSuffix m = fromStreamD . D.intersperseSuffix m . toStreamD
724+
725+-- | Insert a side effect after consuming an element of a stream.
726+--
727+-- @
728+-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffix_ (threadDelay 1000000) $ Stream.unfold Unfold.fromList "hello"
729+-- "hello"
730+--
731+-- @
732+--
733+-- /Pre-release/
734+--
735+{-# INLINE intersperseSuffix_ #-}
736+intersperseSuffix_ :: (Monad m) => m b -> Stream m a -> Stream m a
737+intersperseSuffix_ m = fromStreamD . D.intersperseSuffix_ m . toStreamD
738+
739+-- XXX Use an offset argument, like tapOffsetEvery
740+--
741+-- | Like 'intersperseSuffix' but intersperses an effectful action into the
742+-- input stream after every @n@ elements and after the last element.
743+--
744+-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffixBySpan 2 (return ',') $ Stream.unfold Unfold.fromList "hello"
745+-- "he,ll,o,"
746+--
747+-- /Pre-release/
748+--
749+{-# INLINE intersperseSuffixBySpan #-}
750+intersperseSuffixBySpan :: (Monad m)
751+ => Int -> m a -> Stream m a -> Stream m a
752+intersperseSuffixBySpan n eff =
753+ fromStreamD . D.intersperseSuffixBySpan n eff . toStreamD
754+
755+-- | Insert a side effect before consuming an element of a stream.
756+--
757+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.interspersePrefix_ (putChar '.' >> return ',') $ Stream.unfold Unfold.fromList "hello"
758+-- .h.e.l.l.o"hello"
759+--
760+-- Same as 'trace_' buStream may be concurrent.
761+--
762+-- /Concurrent/
763+--
764+-- /Pre-release/
765+--
766+{-# INLINE interspersePrefix_ #-}
767+interspersePrefix_ :: (Monad m) => m b -> Stream m a -> Stream m a
768+interspersePrefix_ m = mapM (\x -> void m >> return x)
769+
770+------------------------------------------------------------------------------
771+-- Inserting Time
772+------------------------------------------------------------------------------
773+
774+-- Note: delay must be serial.
775+--
776+-- | Introduce a delay of specified seconds before consuming an element of the
777+-- stream except the first one.
778+--
779+-- >>> Stream.mapM_ print $ Stream.timestamped $ Stream.delay 1 $ Stream.unfold Unfold.fromList $ enumFromTo 1 3
780+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
781+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
782+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
783+--
784+-- @since 0.8.0
785+--
786+{-# INLINE delay #-}
787+delay :: (MonadIO m) => Double -> Stream m a -> Stream m a
788+delay n = intersperseM_ $ liftIO $ threadDelay $ round $ n * 1000000
789+
790+-- Note: delay must be serial.
791+--
792+-- | Introduce a delay of specified seconds after consuming an element of a
793+-- stream.
794+--
795+-- >>> Stream.mapM_ print $ Stream.timestamped $ Stream.delayPost 1 $ Stream.unfold Unfold.fromList $ enumFromTo 1 3
796+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
797+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
798+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
799+--
800+-- /Pre-release/
801+--
802+{-# INLINE delayPost #-}
803+delayPost :: (MonadIO m) => Double -> Stream m a -> Stream m a
804+delayPost n = intersperseSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000
805+
806+-- Note: delay must be serial, that's why 'trace_' is used.
807+--
808+-- | Introduce a delay of specified seconds before consuming an element of a
809+-- stream.
810+--
811+-- >>> Stream.mapM_ print $ Stream.timestamped $ Stream.delayPre 1 $ Stream.unfold Unfold.fromList $ enumFromTo 1 3
812+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
813+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
814+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
815+--
816+-- /Pre-release/
817+--
818+{-# INLINE delayPre #-}
819+delayPre :: (MonadIO m) => Double -> Stream m a -> Stream m a
820+delayPre n = trace_ $ liftIO $ threadDelay $ round $ n * 1000000
821+
822+------------------------------------------------------------------------------
823+-- Position Indexing
824+------------------------------------------------------------------------------
825+
826+-- |
827+-- > indexed = Stream.postscanl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
828+-- > indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)
829+--
830+-- Pair each element in a stream with its index, starting from index 0.
831+--
832+-- >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.unfold Unfold.fromList "hello"
833+-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
834+--
835+-- @since 0.6.0
836+{-# INLINE indexed #-}
837+indexed :: (Monad m) => Stream m a -> Stream m (Int, a)
838+indexed = fromStreamD . D.indexed . toStreamD
839+
840+-- |
841+-- > indexedR n = Stream.postscanl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
842+-- > indexedR n = Stream.zipWith (,) (Stream.unfold Unfold.fromList $ enumFromThen n (n - 1))
843+--
844+-- Pair each element in a stream with its index, starting from the
845+-- given index @n@ and counting down.
846+--
847+-- >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.unfold Unfold.fromList "hello"
848+-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
849+--
850+-- @since 0.6.0
851+{-# INLINE indexedR #-}
852+indexedR :: (Monad m) => Int -> Stream m a -> Stream m (Int, a)
853+indexedR n = fromStreamD . D.indexedR n . toStreamD
854+
855+------------------------------------------------------------------------------
856+-- Searching
857+------------------------------------------------------------------------------
858+
859+-- | Find all the indices where the value of the element in the stream is equal
860+-- to the given value.
861+--
862+-- > elemIndices a = findIndices (== a)
863+--
864+-- @since 0.5.0
865+{-# INLINE elemIndices #-}
866+elemIndices :: (Eq a, Monad m) => a -> Stream m a -> Stream m Int
867+elemIndices a = findIndices (== a)
868+
869+------------------------------------------------------------------------------
870+-- Rolling map
871+------------------------------------------------------------------------------
872+
873+-- XXX this is not a one-to-one map so calling iStream map may not be right.
874+-- We can perhaps call it zipWithTail or rollWith.
875+--
876+-- | Apply a function on every two successive elements of a stream. The first
877+-- argument of the map function is the previous element and the second argument
878+-- is the current element. When the current element is the first element, the
879+-- previous element is 'Nothing'.
880+--
881+-- /Pre-release/
882+--
883+{-# INLINE rollingMap #-}
884+rollingMap :: (Monad m) => (Maybe a -> a -> b) -> Stream m a -> Stream m b
885+rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
886+
887+-- | Like 'rollingMap' but with an effectful map function.
888+--
889+-- /Pre-release/
890+--
891+{-# INLINE rollingMapM #-}
892+rollingMapM :: (Monad m) => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
893+rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
894+
895+-- | Like 'rollingMap' but requires at least two elements in the stream,
896+-- returns an empty stream otherwise.
897+--
898+-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.
899+--
900+-- /Pre-release/
901+--
902+{-# INLINE rollingMap2 #-}
903+rollingMap2 :: (Monad m) => (a -> a -> b) -> Stream m a -> Stream m b
904+rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m
905+
906+------------------------------------------------------------------------------
907+-- Maybe Streams
908+------------------------------------------------------------------------------
909+
910+-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
911+-- elements, and return a stream of values extracted from 'Just'.
912+--
913+-- Equivalent to:
914+--
915+-- @
916+-- mapMaybe f = Stream.map 'fromJust' . Stream.filter 'isJust' . Stream.map f
917+-- @
918+--
919+-- @since 0.3.0
920+{-# INLINE mapMaybe #-}
921+mapMaybe :: (Monad m) => (a -> Maybe b) -> Stream m a -> Stream m b
922+mapMaybe f m = fromStreamD $ D.mapMaybe f $ toStreamD m
923+
924+-- | Like 'mapMaybe' buStream maps a monadic function.
925+--
926+-- Equivalent to:
927+--
928+-- @
929+-- mapMaybeM f = Stream.map 'fromJust' . Stream.filter 'isJust' . Stream.mapM f
930+-- @
931+--
932+-- /Concurrent (do not use with 'fromParallel' on infinite streams)/
933+--
934+-- @since 0.3.0
935+{-# INLINE_EARLY mapMaybeM #-}
936+mapMaybeM :: (Monad m)
937+ => (a -> m (Maybe b)) -> Stream m a -> Stream m b
938+mapMaybeM f = fmap fromJust . filter isJust . mapM f
939+
940+-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
941+--
942+-- /Pre-release/
943+--
944+{-# INLINE catMaybes #-}
945+catMaybes :: (Monad m) => Stream m (Maybe a) -> Stream m a
946+catMaybes = fmap fromJust . filter isJust
947+
948+------------------------------------------------------------------------------
949+-- Either streams
950+------------------------------------------------------------------------------
951+
952+-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
953+--
954+-- /Pre-release/
955+--
956+{-# INLINE lefts #-}
957+lefts :: (Monad m) => Stream m (Either a b) -> Stream m a
958+lefts = fmap (fromLeft undefined) . filter isLeft
959+
960+-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
961+--
962+-- /Pre-release/
963+--
964+{-# INLINE rights #-}
965+rights :: (Monad m) => Stream m (Either a b) -> Stream m b
966+rights = fmap (fromRight undefined) . filter isRight
967+
968+-- | Remove the either wrapper and flatten both lefts and as well as rights in
969+-- the output stream.
970+--
971+-- /Pre-release/
972+--
973+{-# INLINE both #-}
974+both :: Monad m => Stream m (Either a a) -> Stream m a
975+both = fmap (either id id)
core/src/Streamly/Internal/Data/Stream/Type.hs+288−1
@@ -15,6 +15,41 @@ module Streamly.Internal.Data.Stream.Type
1515 , toStreamK
1616 , fromStreamD
1717 , toStreamD
18+
19+ -- * Construction
20+ , cons
21+ , consM
22+ , (.:)
23+ , nil
24+ , nilM
25+ , fromPure
26+ , fromEffect
27+ , append
28+
29+ -- * Bind/Concat
30+ , bindWith
31+ , concatMapWith
32+
33+ -- * Fold Utilities
34+ , concatFoldableWith
35+ , concatMapFoldableWith
36+ , concatForFoldableWith
37+
38+ -- * Fold operations
39+ , foldrM
40+ , foldrMx
41+ , foldr
42+
43+ , foldlx'
44+ , foldlMx'
45+ , foldl'
46+
47+ -- * Conversion operations
48+ , fromList
49+ , toList
50+ -- * Zip style operations
51+ , eqBy
52+ , cmpBy
1853 )
1954where
2055
@@ -44,12 +79,14 @@ import qualified Streamly.Internal.Data.Stream.Common as P
4479import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
4580import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
4681
82+import Prelude hiding (repeat)
83+
4784#include "Instances.hs"
4885#include "inline.hs"
4986
5087-- $setup
5188-- >>> import qualified Streamly.Data.Fold as Fold
52−-- >>> import qualified Streamly.Data.Unfold as Unfold
89+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
5390-- >>> import qualified Streamly.Internal.Data.Stream as Stream
5491
5592------------------------------------------------------------------------------
@@ -177,3 +214,253 @@ LIST_INSTANCES(Stream)
177214NFDATA1_INSTANCE(Stream)
178215FOLDABLE_INSTANCE(Stream)
179216TRAVERSABLE_INSTANCE(Stream)
217+
218+-------------------------------------------------------------------------------
219+-- Construction
220+-------------------------------------------------------------------------------
221+
222+infixr 5 `cons`
223+
224+-- | Construct a stream by adding a pure value at the head of an existing
225+-- stream. Same as the following but more efficient:
226+--
227+-- For example:
228+--
229+-- >> s = 1 `Stream.cons` 2 `Stream.cons` 3 `Stream.cons` Stream.nil
230+-- >> Stream.fold Fold.toList s
231+-- >[1,2,3]
232+--
233+-- >>> cons x xs = return x `Stream.consM` xs
234+--
235+-- /Pre-release/
236+--
237+{-# INLINE_NORMAL cons #-}
238+cons :: a -> Stream m a -> Stream m a
239+cons x = fromStreamK . K.cons x . toStreamK
240+
241+-- | Constructs a stream by adding a monadic action at the head of an
242+-- existing stream. For example:
243+--
244+-- @
245+-- > toList $ getLine \`consM` getLine \`consM` nil
246+-- hello
247+-- world
248+-- ["hello","world"]
249+-- @
250+--
251+-- > consM x xs = fromEffect x `append` xs
252+--
253+-- /Pre-release/
254+--
255+{-# INLINE consM #-}
256+{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
257+consM :: Monad m => m a -> Stream m a -> Stream m a
258+consM m = fromStreamK . K.consM m . toStreamK
259+
260+infixr 5 .:
261+
262+-- | Operator equivalent of 'cons'.
263+--
264+-- @
265+-- > toList $ 1 .: 2 .: 3 .: nil
266+-- [1,2,3]
267+-- @
268+--
269+-- @since 0.1.1
270+{-# INLINE (.:) #-}
271+(.:) :: a -> Stream m a -> Stream m a
272+(.:) = cons
273+
274+{-# INLINE_NORMAL nil #-}
275+nil :: Stream m a
276+nil = Stream K.nil
277+
278+{-# INLINE_NORMAL nilM #-}
279+nilM :: (Monad m) => m b -> Stream m a
280+nilM = Stream . K.nilM
281+
282+{-# INLINE_NORMAL fromPure #-}
283+fromPure :: a -> Stream m a
284+fromPure = Stream . K.fromPure
285+
286+{-# INLINE_NORMAL fromEffect #-}
287+fromEffect :: Monad m => m a -> Stream m a
288+fromEffect = Stream . K.fromEffect
289+
290+-------------------------------------------------------------------------------
291+-- Bind/Concat
292+-------------------------------------------------------------------------------
293+
294+{-# INLINE bindWith #-}
295+bindWith
296+ :: (Stream m b -> Stream m b -> Stream m b)
297+ -> Stream m a
298+ -> (a -> Stream m b)
299+ -> Stream m b
300+bindWith par m1 f =
301+ Stream
302+ $ K.bindWith
303+ (\s1 s2 -> toStreamK $ par (Stream s1) (Stream s2))
304+ (toStreamK m1)
305+ (toStreamK . f)
306+
307+-- | @concatMapWith mixer generator stream@ is a two dimensional looping
308+-- combinator. The @generator@ function is used to generate streams from the
309+-- elements in the input @stream@ and the @mixer@ function is used to merge
310+-- those streams.
311+--
312+-- Note we can merge streams concurrently by using a concurrent merge function.
313+--
314+-- /Since: 0.7.0/
315+--
316+-- /Since: 0.8.0 (signature change)/
317+{-# INLINE concatMapWith #-}
318+concatMapWith
319+ :: (Stream m b -> Stream m b -> Stream m b)
320+ -> (a -> Stream m b)
321+ -> Stream m a
322+ -> Stream m b
323+concatMapWith par f xs = bindWith par xs f
324+
325+-- | A variant of 'foldMap' that allows you to map a monadic streaming action
326+-- on a 'Foldable' container and then fold it using the specified stream merge
327+-- operation.
328+--
329+-- @concatMapFoldableWith 'async' return [1..3]@
330+--
331+-- Equivalent to:
332+--
333+-- @
334+-- concatMapFoldableWith f g = Prelude.foldr (f . g) K.nil
335+-- concatMapFoldableWith f g xs = K.concatMapWith f g (K.fromFoldable xs)
336+-- @
337+--
338+-- /Since: 0.8.0 (Renamed foldMapWith to concatMapFoldableWith)/
339+--
340+-- /Since: 0.1.0 ("Streamly")/
341+{-# INLINE concatMapFoldableWith #-}
342+concatMapFoldableWith :: Foldable f
343+ => (Stream m b -> Stream m b -> Stream m b)
344+ -> (a -> Stream m b)
345+ -> f a
346+ -> Stream m b
347+concatMapFoldableWith f g = Prelude.foldr (f . g) nil
348+
349+-- | Like 'concatMapFoldableWith' but with the last two arguments reversed i.e. the
350+-- monadic streaming function is the last argument.
351+--
352+-- Equivalent to:
353+--
354+-- @
355+-- concatForFoldableWith f xs g = Prelude.foldr (f . g) K.nil xs
356+-- concatForFoldableWith f = flip (K.concatMapFoldableWith f)
357+-- @
358+--
359+-- /Since: 0.8.0 (Renamed forEachWith to concatForFoldableWith)/
360+--
361+-- /Since: 0.1.0 ("Streamly")/
362+{-# INLINE concatForFoldableWith #-}
363+concatForFoldableWith :: Foldable f
364+ => (Stream m b -> Stream m b -> Stream m b)
365+ -> f a
366+ -> (a -> Stream m b)
367+ -> Stream m b
368+concatForFoldableWith f = flip (concatMapFoldableWith f)
369+
370+-- | A variant of 'Data.Foldable.fold' that allows you to fold a 'Foldable'
371+-- container of streams using the specified stream sum operation.
372+--
373+-- @concatFoldableWith 'async' $ map return [1..3]@
374+--
375+-- Equivalent to:
376+--
377+-- @
378+-- concatFoldableWith f = Prelude.foldr f K.nil
379+-- concatFoldableWith f = K.concatMapFoldableWith f id
380+-- @
381+--
382+-- /Since: 0.8.0 (Renamed foldWith to concatFoldableWith)/
383+--
384+-- /Since: 0.1.0 ("Streamly")/
385+{-# INLINE concatFoldableWith #-}
386+concatFoldableWith :: Foldable f
387+ => (Stream m a -> Stream m a -> Stream m a)
388+ -> f (Stream m a)
389+ -> Stream m a
390+concatFoldableWith f = concatMapFoldableWith f id
391+
392+------------------------------------------------------------------------------
393+-- Folds
394+------------------------------------------------------------------------------
395+
396+{-# INLINE foldrM #-}
397+foldrM :: (a -> m b -> m b) -> m b -> Stream m a -> m b
398+foldrM step acc m = K.foldrM step acc $ toStreamK m
399+
400+{-# INLINE foldrMx #-}
401+foldrMx :: (Monad m)
402+ => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
403+foldrMx step final project m = D.foldrMx step final project $ toStreamD m
404+
405+-- | Like 'foldlx'', but with a monadic step function.
406+--
407+-- @since 0.7.0
408+{-# INLINE foldlMx' #-}
409+foldlMx' ::
410+ (Monad m)
411+ => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
412+foldlMx' step begin done m = P.foldlMx' step begin done $ toStreamK m
413+
414+-- | Strict left fold with an extraction function. Like the standard strict
415+-- left fold, but applies a user supplied extraction function (the third
416+-- argument) to the folded value at the end. This is designed to work with the
417+-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.
418+--
419+-- @since 0.7.0
420+{-# INLINE foldlx' #-}
421+foldlx' ::
422+ (Monad m) => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
423+foldlx' step begin done m = K.foldlx' step begin done $ toStreamK m
424+
425+------------------------------------------------------------------------------
426+-- Comparison
427+------------------------------------------------------------------------------
428+
429+-- | Compare two streams for equality
430+--
431+-- @since 0.5.3
432+{-# INLINE eqBy #-}
433+eqBy :: (Monad m) =>
434+ (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
435+eqBy f m1 m2 = D.eqBy f (toStreamD m1) (toStreamD m2)
436+
437+-- | Compare two streams
438+--
439+-- @since 0.5.3
440+{-# INLINE cmpBy #-}
441+cmpBy
442+ :: (Monad m)
443+ => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
444+cmpBy f m1 m2 = D.cmpBy f (toStreamD m1) (toStreamD m2)
445+
446+------------------------------------------------------------------------------
447+-- Combining
448+------------------------------------------------------------------------------
449+
450+infixr 6 `append`
451+
452+-- | Appends two streams sequentially, yielding all elements from the first
453+-- stream, and then all elements from the second stream.
454+--
455+-- >>> s1 = Stream.unfold Unfold.fromList [1,2]
456+-- >>> s2 = Stream.unfold Unfold.fromList [3,4]
457+-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2
458+-- [1,2,3,4]
459+--
460+-- This operation can be used to fold an infinite lazy container of streams.
461+--
462+-- /Pre-release/
463+--
464+{-# INLINE append #-}
465+append :: Stream m a -> Stream m a -> Stream m a
466+append = (<>)
core/streamly-core.cabal+6
@@ -308,6 +308,12 @@ library
308308 , Streamly.Internal.Data.Stream.WSerial
309309 , Streamly.Internal.Data.Stream.Zip
310310 , Streamly.Internal.Data.List
311+ , Streamly.Internal.Data.Stream.Bottom
312+ , Streamly.Internal.Data.Stream.Exception
313+ , Streamly.Internal.Data.Stream.Expand
314+ , Streamly.Internal.Data.Stream.Lift
315+ , Streamly.Internal.Data.Stream.Reduce
316+
311317
312318 -- streamly-core-data-arrays
313319 -- XXX Depends on primitive
streamly.cabal+14−3
@@ -198,6 +198,12 @@ extra-source-files:
198198 core/src/Streamly/Internal/Data/Stream/Generate.hs
199199 core/src/Streamly/Internal/Data/Stream/Transform.hs
200200 core/src/Streamly/Internal/Data/Stream/Type.hs
201+ core/src/Streamly/Internal/Data/Stream/Bottom.hs
202+ core/src/Streamly/Internal/Data/Stream/Exception.hs
203+ core/src/Streamly/Internal/Data/Stream/Expand.hs
204+ core/src/Streamly/Internal/Data/Stream/Generate.hs
205+ core/src/Streamly/Internal/Data/Stream/Lift.hs
206+ core/src/Streamly/Internal/Data/Stream/Reduce.hs
201207 core/src/Streamly/Internal/Data/Stream/WSerial.hs
202208 core/src/Streamly/Internal/Data/Stream/Zip.hs
203209 core/src/Streamly/Internal/Data/List.hs
@@ -610,16 +616,21 @@ library
610616 , Streamly.Internal.Data.Fold.Tee
611617 , Streamly.Internal.Data.Fold
612618 , Streamly.Internal.Data.Fold.Window
619+ , Streamly.Internal.Data.List
613620 , Streamly.Internal.Data.Parser
614621 , Streamly.Internal.Data.Pipe
615− , Streamly.Internal.Data.Stream.Type
622+ , Streamly.Internal.Data.Stream
623+ , Streamly.Internal.Data.Stream.Bottom
616624 , Streamly.Internal.Data.Stream.Eliminate
617625 , Streamly.Internal.Data.Stream.Generate
626+ , Streamly.Internal.Data.Stream.Exception
627+ , Streamly.Internal.Data.Stream.Expand
628+ , Streamly.Internal.Data.Stream.Lift
629+ , Streamly.Internal.Data.Stream.Reduce
618630 , Streamly.Internal.Data.Stream.Transform
619− , Streamly.Internal.Data.Stream
620631 , Streamly.Internal.Data.Stream.Serial
632+ , Streamly.Internal.Data.Stream.Type
621633 , Streamly.Internal.Data.Stream.Zip
622− , Streamly.Internal.Data.List
623634
624635 -- streamly-core-data-arrays
625636 -- XXX Depends on primitive