on d29030f help sign in
670 lines 23 kB history raw
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.
12module Streamly.Internal.Data.Stream.Bottom
13 (
14 -- * Generation
15 fromPure
16 , fromEffect
17 , fromList
18 , timesWith
19 , absTimesWith
20 , relTimesWith
22 -- * Folds
23 , fold
24 , foldBreak
25 , foldBreak2
26 , foldEither
27 , foldEither2
28 , foldConcat
30 -- * Builders
31 , foldAdd
32 , foldAddLazy
34 -- * Scans
35 , smapM
36 -- $smapM_Notes
37 , postscan
38 , catMaybes
39 , scanMaybe
41 , take
42 , takeWhile
43 , takeEndBy
44 , drop
45 , findIndices
47 -- * Merge
48 , intersperseM
50 -- * Fold and Unfold
51 , reverse
52 , reverse'
54 -- * Expand
55 , concatEffect
56 , concatEffect2
57 , concatMapM
58 , concatMap
60 -- * Reduce
61 , foldManyPost
63 -- * Zipping
64 , zipWithM
65 , zipWith
66 )
67where
69#include "inline.hs"
71import Control.Monad.IO.Class (MonadIO(..))
72import GHC.Types (SPEC(..))
73import Streamly.Internal.Data.Fold.Type (Fold (..))
74import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
75import Streamly.Internal.Data.Unbox (Unbox)
76import Streamly.Internal.Data.Producer.Type (Producer(..))
77import Streamly.Internal.System.IO (defaultChunkSize)
78import Streamly.Internal.Data.SVar.Type (defState)
80import qualified Streamly.Internal.Data.Array.Type as A
81import qualified Streamly.Internal.Data.Fold as Fold
82import qualified Streamly.Internal.Data.StreamK as K
83import qualified Streamly.Internal.Data.Stream as D
85import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
87import Streamly.Internal.Data.Stream.Type
89--
90-- $setup
91-- >>> :m
92-- >>> import Control.Monad (join, (>=>), (<=<))
93-- >>> import Data.Function (fix, (&))
94-- >>> import Data.Functor.Identity (Identity)
95-- >>> import Data.Maybe (fromJust, isJust)
96-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
97-- >>> import Streamly.Data.Array (Array)
98-- >>> import Streamly.Data.Fold (Fold)
99-- >>> import Streamly.Data.Stream (Stream)
100-- >>> import System.IO.Unsafe (unsafePerformIO)
101-- >>> import qualified Streamly.Data.Array as Array
102-- >>> import qualified Streamly.Data.MutArray as MArray
103-- >>> import qualified Streamly.Data.Fold as Fold
104-- >>> import qualified Streamly.Data.Parser as Parser
105-- >>> import qualified Streamly.Data.Unfold as Unfold
106-- >>> import qualified Streamly.Internal.Data.Fold as Fold (toStream)
107-- >>> import Streamly.Internal.Data.Stream as Stream
109------------------------------------------------------------------------------
110-- Generation - Time related
111------------------------------------------------------------------------------
113-- | @timesWith g@ returns a stream of time value tuples. The first component
114-- of the tuple is an absolute time reference (epoch) denoting the start of the
115-- stream and the second component is a time relative to the reference.
116--
117-- The argument @g@ specifies the granularity of the relative time in seconds.
118-- A lower granularity clock gives higher precision but is more expensive in
119-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
120--
121-- >>> import Control.Concurrent (threadDelay)
122-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)
123-- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01
124-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
125-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
126-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
127--
128-- Note: This API is not safe on 32-bit machines.
129--
130-- /Pre-release/
131--
132{-# INLINE timesWith #-}
133timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)
134timesWith g = fromStreamD $ D.timesWith g
136-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
137-- granularity @g@ specified in seconds. A low granularity clock is more
138-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
139-- as 1 ms.
140--
141-- >>> f = Fold.drainMapM print
142-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01
143-- AbsTime (TimeSpec {sec = ..., nsec = ...})
144-- AbsTime (TimeSpec {sec = ..., nsec = ...})
145-- AbsTime (TimeSpec {sec = ..., nsec = ...})
146--
147-- Note: This API is not safe on 32-bit machines.
148--
149-- /Pre-release/
150--
151{-# INLINE absTimesWith #-}
152absTimesWith :: MonadIO m => Double -> Stream m AbsTime
153absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
155-- | @relTimesWith g@ returns a stream of relative time values starting from 0,
156-- using a clock of granularity @g@ specified in seconds. A low granularity
157-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
158-- ms is treated as 1 ms.
159--
160-- >>> f = Fold.drainMapM print
161-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
162-- RelTime64 (NanoSecond64 ...)
163-- RelTime64 (NanoSecond64 ...)
164-- RelTime64 (NanoSecond64 ...)
165--
166-- Note: This API is not safe on 32-bit machines.
167--
168-- /Pre-release/
169--
170{-# INLINE relTimesWith #-}
171relTimesWith :: MonadIO m => Double -> Stream m RelTime64
172relTimesWith = fmap snd . timesWith
174------------------------------------------------------------------------------
175-- Elimination - Running a Fold
176------------------------------------------------------------------------------
178-- | Append a stream to a fold lazily to build an accumulator incrementally.
179--
180-- Example, to continue folding a list of streams on the same sum fold:
181--
182-- >>> streams = [Stream.fromList [1..5], Stream.fromList [6..10]]
183-- >>> f = Prelude.foldl Stream.foldAddLazy Fold.sum streams
184-- >>> Stream.fold f Stream.nil
185-- 55
186--
187{-# INLINE foldAddLazy #-}
188foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b
189foldAddLazy f s = D.foldAddLazy f $ toStreamD s
191-- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce
193-- |
194-- >>> foldAdd = flip Fold.addStream
195--
196foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)
197foldAdd f = fold (Fold.duplicate f)
199-- >>> fold f = Fold.extractM . Stream.foldAddLazy f
200-- >>> fold f = Stream.fold Fold.one . Stream.foldManyPost f
201-- >>> fold f = Fold.extractM <=< Stream.foldAdd f
203-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
204-- expression strictly at each step. The behavior is similar to 'foldl''. A
205-- 'Fold' can terminate early without consuming the full stream. See the
206-- documentation of individual 'Fold's for termination behavior.
207--
208-- Definitions:
209--
210-- >>> fold f = fmap fst . Stream.foldBreak f
211-- >>> fold f = Stream.parse (Parser.fromFold f)
212--
213-- Example:
214--
215-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
216-- 5050
217--
218{-# INLINE fold #-}
219fold :: Monad m => Fold m a b -> Stream m a -> m b
220fold fl strm = D.fold fl $ D.fromStreamK $ toStreamK strm
222-- Alternative name foldSome, but may be confused vs foldMany.
224-- | Like 'fold' but also returns the remaining stream. The resulting stream
225-- would be 'Stream.nil' if the stream finished before the fold.
226--
227-- /CPS/
228--
229{-# INLINE foldBreak #-}
230foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
231foldBreak fl strm = fmap f $ K.foldBreak fl (toStreamK strm)
233 where
235 f (b, str) = (b, fromStreamK str)
237-- XXX The quadratic slowdown in recursive use is because recursive function
238-- cannot be inlined and StreamD/StreamK conversions pile up and cannot be
239-- eliminated by rewrite rules.
241-- | Like 'foldBreak' but fuses.
242--
243-- /Note:/ Unlike 'foldBreak', recursive application on the resulting stream
244-- would lead to quadratic slowdown. If you need recursion with fusion (within
245-- one iteration of recursion) use StreamD.foldBreak directly.
246--
247-- /Internal/
248{-# INLINE foldBreak2 #-}
249foldBreak2 :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
250foldBreak2 fl strm = fmap f $ D.foldBreak fl $ toStreamD strm
252 where
254 f (b, str) = (b, fromStreamD str)
256-- | Fold resulting in either breaking the stream or continuation of the fold.
257-- Instead of supplying the input stream in one go we can run the fold multiple
258-- times, each time supplying the next segment of the input stream. If the fold
259-- has not yet finished it returns a fold that can be run again otherwise it
260-- returns the fold result and the residual stream.
261--
262-- /Internal/
263{-# INLINE foldEither #-}
264foldEither :: Monad m =>
265 Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
266foldEither fl strm = fmap (fmap f) $ K.foldEither fl $ toStreamK strm
268 where
270 f (b, str) = (b, fromStreamK str)
272-- | Like 'foldEither' but fuses. However, recursive application on resulting
273-- stream would lead to quadratic slowdown.
274--
275-- /Internal/
276{-# INLINE foldEither2 #-}
277foldEither2 :: Monad m =>
278 Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
279foldEither2 fl strm = fmap (fmap f) $ D.foldEither fl $ toStreamD strm
281 where
283 f (b, str) = (b, fromStreamD str)
285-- XXX Array folds can be implemented using this.
286-- foldContainers? Specialized to foldArrays.
288-- | Generate streams from individual elements of a stream and fold the
289-- concatenation of those streams using the supplied fold. Return the result of
290-- the fold and residual stream.
291--
292-- For example, this can be used to efficiently fold an Array Word8 stream
293-- using Word8 folds.
294--
295-- The outer stream forces CPS to allow scalable appends and the inner stream
296-- forces direct style for stream fusion.
297--
298-- /Internal/
299{-# INLINE foldConcat #-}
300foldConcat :: Monad m =>
301 Producer m a b -> Fold m b c -> Stream m a -> m (c, Stream m a)
302foldConcat
303 (Producer pstep pinject pextract)
304 (Fold fstep begin done)
305 stream = do
307 res <- begin
308 case res of
309 Fold.Partial fs -> go fs streamK
310 Fold.Done fb -> return (fb, fromStreamK streamK)
312 where
314 streamK = toStreamK stream
316 go !acc m1 = do
317 let stop = do
318 r <- done acc
319 return (r, fromStreamK K.nil)
320 single a = do
321 st <- pinject a
322 res <- go1 SPEC acc st
323 case res of
324 Left fs -> do
325 r <- done fs
326 return (r, fromStreamK K.nil)
327 Right (b, s) -> do
328 x <- pextract s
329 return (b, fromStreamK (K.fromPure x))
330 yieldk a r = do
331 st <- pinject a
332 res <- go1 SPEC acc st
333 case res of
334 Left fs -> go fs r
335 Right (b, s) -> do
336 x <- pextract s
337 return (b, fromStreamK (x `K.cons` r))
338 in K.foldStream defState yieldk single stop m1
340 {-# INLINE go1 #-}
341 go1 !_ !fs st = do
342 r <- pstep st
343 case r of
344 D.Yield x s -> do
345 res <- fstep fs x
346 case res of
347 Fold.Done b -> return $ Right (b, s)
348 Fold.Partial fs1 -> go1 SPEC fs1 s
349 D.Skip s -> go1 SPEC fs s
350 D.Stop -> return $ Left fs
352------------------------------------------------------------------------------
353-- Transformation
354------------------------------------------------------------------------------
356{-
357-- |
358-- >>> map = fmap
359--
360-- Same as 'fmap'.
361--
362-- >>> Stream.fold Fold.toList $ fmap (+1) $ Stream.fromList [1,2,3]
363-- [2,3,4]
364--
365{-# INLINE map #-}
366map :: Monad m => (a -> b) -> Stream m a -> Stream m b
367map f = fromStreamD . D.map f . toStreamD
368-}
370-- | Postscan a stream using the given monadic fold.
371--
372-- The following example extracts the input stream up to a point where the
373-- running average of elements is no more than 10:
374--
375-- >>> import Data.Maybe (fromJust)
376-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
377-- >>> s = Stream.enumerateFromTo 1.0 100.0
378-- >>> :{
379-- Stream.fold Fold.toList
380-- $ fmap (fromJust . fst)
381-- $ Stream.takeWhile (\(_,x) -> x <= 10)
382-- $ Stream.postscan (Fold.tee Fold.latest avg) s
383-- :}
384-- [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]
385--
386{-# INLINE postscan #-}
387postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b
388postscan fld = fromStreamD . D.postscan fld . toStreamD
390-- $smapM_Notes
391--
392-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
393-- a read-only environment. However, that would just be 'mapM'.
394--
395-- The initial action could be @m (s, Maybe b)@, and we can also add a final
396-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
397-- functionality and also to flush the state in the end like scanlMAfter'.
398-- We can also use it along with a fusible version of bracket to get
399-- scanlMAfter' like functionality. See issue #677.
400--
401-- This can be further generalized to a type similar to Fold/Parser, giving it
402-- filtering and parsing capability as well (this is in fact equivalent to
403-- parseMany):
404--
405-- smapM :: (s -> a -> m (Step s b)) -> m s -> Stream m a -> Stream m b
406--
408-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
409-- Hopefully, this is a better alternative to @scan@. Separation of state from
410-- the output makes it easier to think in terms of a shared state, and also
411-- makes it easier to keep the state fully strict and the output lazy.
412--
413-- See also: 'postscan'
414--
415-- /Pre-release/
416--
417{-# INLINE smapM #-}
418smapM :: Monad m =>
419 (s -> a -> m (s, b))
420 -> m s
421 -> Stream m a
422 -> Stream m b
423smapM step initial stream =
424 -- XXX implement this directly instead of using postscan
425 let f = Fold.foldlM'
426 (\(s, _) a -> step s a)
427 (fmap (,undefined) initial)
428 in fmap snd $ postscan f stream
430-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
431--
432-- >>> catMaybes = Stream.mapMaybe id
433-- >>> catMaybes = fmap fromJust . Stream.filter isJust
434--
435-- /Pre-release/
436--
437{-# INLINE catMaybes #-}
438catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
439-- catMaybes = fmap fromJust . filter isJust
440catMaybes = fromStreamD . D.catMaybes . toStreamD
442-- | Use a filtering fold on a stream.
443--
444-- >>> scanMaybe f = Stream.catMaybes . Stream.postscan f
445--
446{-# INLINE scanMaybe #-}
447scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
448scanMaybe p = catMaybes . postscan p
450------------------------------------------------------------------------------
451-- Transformation - Trimming
452------------------------------------------------------------------------------
454-- | Take first 'n' elements from the stream and discard the rest.
455--
456{-# INLINE take #-}
457take :: Monad m => Int -> Stream m a -> Stream m a
458-- take n = scanMaybe (Fold.taking n)
459take n m = fromStreamD $ D.take n $ toStreamD m
461-- | End the stream as soon as the predicate fails on an element.
462--
463{-# INLINE takeWhile #-}
464takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
465-- takeWhile p = scanMaybe (Fold.takingEndBy_ (not . p))
466takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m
468{-# INLINE takeEndBy #-}
469takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
470-- takeEndBy p = scanMaybe (Fold.takingEndBy p)
471takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
473-- | Discard first 'n' elements from the stream and take the rest.
474--
475{-# INLINE drop #-}
476drop :: Monad m => Int -> Stream m a -> Stream m a
477-- drop n = scanMaybe (Fold.dropping n)
478drop n m = fromStreamD $ D.drop n $ toStreamD m
480------------------------------------------------------------------------------
481-- Searching
482------------------------------------------------------------------------------
484-- | Find all the indices where the element in the stream satisfies the given
485-- predicate.
486--
487-- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
488--
489{-# INLINE findIndices #-}
490findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
491-- findIndices p = scanMaybe (Fold.findIndices p)
492findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
494------------------------------------------------------------------------------
495-- Transformation by Inserting
496------------------------------------------------------------------------------
498-- intersperseM = intersperseMWith 1
500-- | Insert an effect and its output before consuming an element of a stream
501-- except the first one.
502--
503-- >>> input = Stream.fromList "hello"
504-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input
505-- h.,e.,l.,l.,o"h,e,l,l,o"
506--
507-- Be careful about the order of effects. In the above example we used trace
508-- after the intersperse, if we use it before the intersperse the output would
509-- be he.l.l.o."h,e,l,l,o".
510--
511-- >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input
512-- he.l.l.o."h,e,l,l,o"
513--
514{-# INLINE intersperseM #-}
515intersperseM :: Monad m => m a -> Stream m a -> Stream m a
516intersperseM m = fromStreamD . D.intersperseM m . toStreamD
518------------------------------------------------------------------------------
519-- Transformation by Reordering
520------------------------------------------------------------------------------
522-- XXX Use a compact region list to temporarily store the list, in both reverse
523-- as well as in reverse'.
524--
525-- /Note:/ 'reverse'' is much faster than this, use that when performance
526-- matters.
527--
528-- | Returns the elements of the stream in reverse order. The stream must be
529-- finite. Note that this necessarily buffers the entire stream in memory.
530--
531-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
532--
533{-# INLINE reverse #-}
534reverse :: Stream m a -> Stream m a
535reverse s = fromStreamK $ K.reverse $ toStreamK s
537-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
538--
539-- /O(n) space/
540--
541-- /Pre-release/
542{-# INLINE reverse' #-}
543reverse' :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
544-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
545reverse' =
546 fromStreamD
547 . A.flattenArraysRev -- unfoldMany A.readRev
548 . D.fromStreamK
549 . K.reverse
550 . D.toStreamK
551 . A.chunksOf defaultChunkSize
552 . toStreamD
554------------------------------------------------------------------------------
555-- Combine streams and flatten
556------------------------------------------------------------------------------
558-- | Map a stream producing monadic function on each element of the stream
559-- and then flatten the results into a single stream. Since the stream
560-- generation function is monadic, unlike 'concatMap', it can produce an
561-- effect at the beginning of each iteration of the inner loop.
562--
563-- See 'unfoldMany' for a fusible alternative.
564--
565{-# INLINE concatMapM #-}
566concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
567concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
569-- | Map a stream producing function on each element of the stream and then
570-- flatten the results into a single stream.
571--
572-- >>> concatMap f = Stream.concatMapM (return . f)
573-- >>> concatMap f = Stream.concatMapWith Stream.append f
574-- >>> concatMap f = Stream.concat . fmap f
575-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
576--
577-- See 'unfoldMany' for a fusible alternative.
578--
579{-# INLINE concatMap #-}
580concatMap ::Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
581concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
583-- >>> concatEffect = Stream.concat . lift -- requires (MonadTrans t)
584-- >>> concatEffect = join . lift -- requires (MonadTrans t, Monad (Stream m))
586-- | Given a stream value in the underlying monad, lift and join the underlying
587-- monad with the stream monad.
588--
589-- >>> concatEffect = Stream.concat . Stream.fromEffect
590--
591-- See also: 'concat', 'sequence'
592--
593-- See 'concatEffect2' for a fusible alternative.
594--
595-- /CPS/
596--
597{-# INLINE concatEffect #-}
598concatEffect :: Monad m => m (Stream m a) -> Stream m a
599concatEffect generator =
600 fromStreamK $ K.concatEffect $ fmap toStreamK generator
602{-# INLINE concatEffect2 #-}
603concatEffect2 :: Monad m => m (Stream m a) -> Stream m a
604-- concatEffect generator = concatMapM (\() -> generator) (fromPure ())
605concatEffect2 generator =
606 fromStreamD $ D.concatEffect $ fmap toStreamD generator
608-- XXX Need a more intuitive name, and need to reconcile the names
609-- foldMany/fold/parse/parseMany/parseManyPost etc.
611-- | Like 'foldMany' but evaluates the fold before the stream, and yields its
612-- output even if the stream is empty, therefore, always results in a non-empty
613-- output even on an empty stream (default result of the fold).
614--
615-- Example, empty stream:
616--
617-- >>> f = Fold.take 2 Fold.sum
618-- >>> fmany = Stream.fold Fold.toList . Stream.foldManyPost f
619-- >>> fmany $ Stream.fromList []
620-- [0]
621--
622-- Example, last fold empty:
623--
624-- >>> fmany $ Stream.fromList [1..4]
625-- [3,7,0]
626--
627-- Example, last fold non-empty:
628--
629-- >>> fmany $ Stream.fromList [1..5]
630-- [3,7,5]
631--
632-- Note that using a closed fold e.g. @Fold.take 0@, would result in an
633-- infinite stream without consuming the input.
634--
635-- /Pre-release/
636--
637{-# INLINE foldManyPost #-}
638foldManyPost
639 :: Monad m
640 => Fold m a b
641 -> Stream m a
642 -> Stream m b
643foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)
645------------------------------------------------------------------------------
646-- Zipping
647------------------------------------------------------------------------------
649-- | Like 'zipWith' but using a monadic zipping function.
650--
651{-# INLINE zipWithM #-}
652zipWithM :: Monad m =>
653 (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
654zipWithM f m1 m2 = fromStreamK $ K.zipWithM f (toStreamK m1) (toStreamK m2)
656-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
657-- elements @a@ and @b@ are then zipped using the supplied zip function and the
658-- result @c@ is yielded to the consumer.
659--
660-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
661-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
662--
663-- >>> s1 = Stream.fromList [1,2,3]
664-- >>> s2 = Stream.fromList [4,5,6]
665-- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2
666-- [5,7,9]
667--
668{-# INLINE zipWith #-}
669zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
670zipWith f m1 m2 = fromStreamK $ K.zipWith f (toStreamK m1) (toStreamK m2)