on ffcb268 help sign in
1026 lines 33 kB history raw
1-- |
2-- Module : Streamly.Internal.Data.Stream.Transform
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
7-- Portability : GHC
9module Streamly.Internal.Data.Stream.Transform
10 (
11 -- * Piping
12 -- | Pass through a 'Pipe'.
13 transform
15 -- * Folding
16 , foldrS
18 -- * Mapping
19 -- | Stateless one-to-one maps.
20 , sequence
21 , mapM
23 -- * Mapping Side Effects (Observation)
24 -- | See also the intersperse*_ combinators.
25 , trace
26 , trace_
27 , tap
29 -- * Scanning
30 , scan
31 , scanMany
32 , postscan
33 , smapM
34 , scanlMAfter'
36 -- * Filtering
37 -- | Produce a subset of the stream using criteria based on the values of
38 -- the elements. We can use a concatMap and scan for filtering but these
39 -- combinators are more efficient and convenient.
41 -- mapMaybeM is a general filtering combinator as we can map the stream to
42 -- Just/Nothing using any stateful fold and then use this to filter out.
43 , mapMaybeM
44 , mapMaybe
45 , catMaybes
46 , scanMaybe
48 , with
49 , deleteBy
50 , filter
51 , filterM
53 -- Stateful/scanning filters
54 , uniq
55 , uniqBy
56 , prune
57 , repeated
59 -- * Trimming
60 -- | Produce a subset of the stream trimmed at ends.
62 , take
63 , takeWhile
64 , takeWhileM
65 , takeWhileLast
66 , takeWhileAround
67 , drop
68 , dropLast
69 , dropWhile
70 , dropWhileM
71 , dropWhileLast
72 , dropWhileAround
74 -- * Position Indexing
75 , indexed
76 , indexedR
78 -- * Time Indexing
79 , timestamped
80 , timestampWith
81 , timeIndexed
82 , timeIndexWith
84 -- * Searching
85 , findIndices -- XXX indicesBy
86 , elemIndices -- XXX indicesOf
88 -- * Rolling map
89 -- | Map using the previous element.
90 , rollingMapM
91 , rollingMap
92 , rollingMap2
94 -- Merge
96 -- * Inserting Elements
97 -- | Produce a superset of the stream. This is the opposite of
98 -- filtering/sampling. We can always use concatMap and scan for inserting
99 -- but these combinators are more efficient and convenient.
101 -- Element agnostic (Opposite of sampling)
102 , intersperse
103 , intersperseM -- XXX naming
104 , intersperseBySpan
106 , intersperseSuffix
107 , intersperseSuffixBySpan
109 -- , interspersePrefix
110 -- , interspersePrefixBySpan
112 -- * Inserting Side Effects/Time
113 , intersperseM_ -- XXX naming
114 , delay
115 , intersperseSuffix_
116 , delayPost
117 , interspersePrefix_
118 , delayPre
120 -- * Element Aware Insertion
121 -- | Opposite of filtering
122 , insertBy
123 -- , intersperseByBefore
124 -- , intersperseByAfter
126 -- Fold and Unfold, Buffering
128 -- * Reordering
129 , reverse
130 , reverse'
131 , reassembleBy
133 -- * Either Streams
134 -- Move these to Streamly.Data.Either.Stream?
135 , catLefts
136 , catRights
137 , catEithers
138 )
139where
141#include "inline.hs"
143import Control.Concurrent (threadDelay)
144import Control.Monad (void)
145import Control.Monad.IO.Class (MonadIO (liftIO))
146import Data.Either (fromLeft, isLeft, isRight, fromRight)
147import Data.Maybe (isJust, fromJust)
149import Streamly.Internal.Data.Fold.Type (Fold)
150import Streamly.Internal.Data.Pipe (Pipe)
151import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64)
153import qualified Streamly.Internal.Data.Fold as FL
154-- import qualified Streamly.Internal.Data.Fold.Window as Window
155import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
156import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
157import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
160import Streamly.Internal.Data.Stream.Bottom
161import Streamly.Internal.Data.Stream.Type
163import Prelude hiding
164 ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence
165 , reverse, foldr1 , repeat, scanl, scanl1, zipWith)
167--
168-- $setup
169-- >>> :m
170-- >>> import Control.Concurrent (threadDelay)
171-- >>> import Control.Monad.IO.Class (MonadIO (liftIO))
172-- >>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
173-- >>> import Data.Function ((&))
174-- >>> import Data.Maybe (fromJust, isJust)
175-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
176-- >>> import Streamly.Internal.Data.Stream (Stream)
177-- >>> import qualified Streamly.Data.Fold as Fold
178-- >>> import qualified Streamly.Data.Unfold as Unfold
179-- >>> import qualified Streamly.Internal.Data.Fold as Fold (filtering)
180-- >>> import qualified Streamly.Internal.Data.Fold.Window as Window
181-- >>> import qualified Streamly.Internal.Data.Stream as Stream
182-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
183--
184-- >>> hSetBuffering stdout LineBuffering
186-- XXX because of the use of D.cons for appending, folds and scans have
187-- quadratic complexity when iterated over a stream. We should use StreamK for
188-- linear performance on iteration.
190------------------------------------------------------------------------------
191-- Piping
192------------------------------------------------------------------------------
194-- | Use a 'Pipe' to transform a stream.
195--
196-- /Pre-release/
197--
198{-# INLINE transform #-}
199transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b
200transform pipe xs = fromStreamD $ D.transform pipe (toStreamD xs)
202------------------------------------------------------------------------------
203-- Transformation Folds
204------------------------------------------------------------------------------
206-- | Right fold to a streaming monad.
207--
208-- > foldrS Stream.cons Stream.nil === id
209--
210-- 'foldrS' can be used to perform stateless stream to stream transformations
211-- like map and filter in general. It can be coupled with a scan to perform
212-- stateful transformations. However, note that the custom map and filter
213-- routines can be much more efficient than this due to better stream fusion.
214--
215-- >>> input = Stream.fromList [1..5]
216-- >>> Stream.fold Fold.toList $ Stream.foldrS Stream.cons Stream.nil input
217-- [1,2,3,4,5]
218--
219-- Find if any element in the stream is 'True':
220--
221-- >>> step x xs = if odd x then Stream.fromPure True else xs
222-- >>> input = Stream.fromList (2:4:5:undefined) :: Stream IO Int
223-- >>> Stream.fold Fold.toList $ Stream.foldrS step (Stream.fromPure False) input
224-- [True]
225--
226-- Map (+2) on odd elements and filter out the even elements:
227--
228-- >>> step x xs = if odd x then (x + 2) `Stream.cons` xs else xs
229-- >>> input = Stream.fromList [1..5] :: Stream IO Int
230-- >>> Stream.fold Fold.toList $ Stream.foldrS step Stream.nil input
231-- [3,5,7]
232--
233-- /Pre-release/
234{-# INLINE foldrS #-}
235foldrS ::
236 (a -> Stream m b -> Stream m b)
237 -> Stream m b
238 -> Stream m a
239 -> Stream m b
240foldrS f z xs =
241 fromStreamK
242 $ K.foldrS
243 (\y ys -> toStreamK $ f y (fromStreamK ys))
244 (toStreamK z)
245 (toStreamK xs)
247------------------------------------------------------------------------------
248-- Transformation by Mapping
249------------------------------------------------------------------------------
251-- |
252-- >>> mapM f = Stream.sequence . fmap f
253--
254-- Apply a monadic function to each element of the stream and replace it with
255-- the output of the resulting action.
256--
257-- >>> s = Stream.fromList ["a", "b", "c"]
258-- >>> Stream.fold Fold.drain $ Stream.mapM putStr s
259-- abc
260--
261{-# INLINE mapM #-}
262mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
263mapM f m = fromStreamK $ D.toStreamK $ D.mapM f $ toStreamD m
265-- |
266-- >>> sequence = Stream.mapM id
267--
268-- Replace the elements of a stream of monadic actions with the outputs of
269-- those actions.
270--
271-- >>> s = Stream.fromList [putStr "a", putStr "b", putStrLn "c"]
272-- >>> Stream.fold Fold.drain $ Stream.sequence s
273-- abc
274--
275{-# INLINE sequence #-}
276sequence :: Monad m => Stream m (m a) -> Stream m a
277sequence = mapM id
279------------------------------------------------------------------------------
280-- Mapping side effects
281------------------------------------------------------------------------------
283-- | Tap the data flowing through a stream into a 'Fold'. For example, you may
284-- add a tap to log the contents flowing through the stream. The fold is used
285-- only for effects, its result is discarded.
286--
287-- @
288-- Fold m a b
289-- |
290-- -----stream m a ---------------stream m a-----
291--
292-- @
293--
294-- >>> s = Stream.enumerateFromTo 1 2
295-- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainMapM print) s
296-- 1
297-- 2
298--
299-- Compare with 'trace'.
300--
301{-# INLINE tap #-}
302tap :: Monad m => FL.Fold m a b -> Stream m a -> Stream m a
303tap f xs = fromStreamD $ D.tap f (toStreamD xs)
305-- | Apply a monadic function to each element flowing through the stream and
306-- discard the results.
307--
308-- >>> s = Stream.enumerateFromTo 1 2
309-- >>> Stream.fold Fold.drain $ Stream.trace print s
310-- 1
311-- 2
312--
313-- Compare with 'tap'.
314--
315{-# INLINE trace #-}
316trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a
317trace f = mapM (\x -> void (f x) >> return x)
319-- | Perform a side effect before yielding each element of the stream and
320-- discard the results.
321--
322-- >>> s = Stream.enumerateFromTo 1 2
323-- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s
324-- "got here"
325-- "got here"
326--
327-- Same as 'interspersePrefix_' but always serial.
328--
329-- See also: 'trace'
330--
331-- /Pre-release/
332{-# INLINE trace_ #-}
333trace_ :: Monad m => m b -> Stream m a -> Stream m a
334trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD
336-------------------------------------------------------------------------------
337-- Scanning
338-------------------------------------------------------------------------------
340-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
341-- that it provides an additional @done@ function to be applied on the
342-- accumulator when the stream stops. The result of @done@ is also emitted in
343-- the stream.
344--
345-- This function can be used to allocate a resource in the beginning of the
346-- scan and release it when the stream ends or to flush the internal state of
347-- the scan at the end.
348--
349-- /Pre-release/
350--
351{-# INLINE scanlMAfter' #-}
352scanlMAfter' ::
353 Monad m
354 => (b -> a -> m b)
355 -> m b
356 -> (b -> m b)
357 -> Stream m a
358 -> Stream m b
359scanlMAfter' step initial done stream =
360 fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream
362------------------------------------------------------------------------------
363-- Scanning with a Fold
364------------------------------------------------------------------------------
366-- XXX It may be useful to have a version of scan where we can keep the
367-- accumulator independent of the value emitted. So that we do not necessarily
368-- have to keep a value in the accumulator which we are not using. We can pass
369-- an extraction function that will take the accumulator and the current value
370-- of the element and emit the next value in the stream. That will also make it
371-- possible to modify the accumulator after using it. In fact, the step function
372-- can return new accumulator and the value to be emitted. The signature would
373-- be more like mapAccumL.
375-- | Strict left scan. Scan a stream using the given monadic fold.
376--
377-- >>> s = Stream.fromList [1..10]
378-- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s
379-- [0,1,3,6]
380--
381-- See also: 'usingStateT'
382--
384-- EXPLANATION:
385-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)
386--
387-- Like 'map', 'scanl'' too is a one to one transformation,
388-- however it adds an extra element.
389--
390-- >>> s = Stream.fromList [1,2,3,4]
391-- >>> Stream.fold Fold.toList $ scanl' (+) 0 s
392-- [0,1,3,6,10]
393--
394-- >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s
395-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
396--
397-- The output of 'scanl'' is the initial value of the accumulator followed by
398-- all the intermediate steps and the final result of 'foldl''.
399--
400-- By streaming the accumulated state after each fold step, we can share the
401-- state across multiple stages of stream composition. Each stage can modify or
402-- extend the state, do some processing with it and emit it for the next stage,
403-- thus modularizing the stream processing. This can be useful in
404-- stateful or event-driven programming.
405--
406-- Consider the following monolithic example, computing the sum and the product
407-- of the elements in a stream in one go using a @foldl'@:
408--
409-- >>> foldl' step z = Stream.fold (Fold.foldl' step z)
410-- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s
411-- (10,24)
412--
413-- Using @scanl'@ we can make it modular by computing the sum in the first
414-- stage and passing it down to the next stage for computing the product:
415--
416-- >>> :{
417-- foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)
418-- $ scanl' (\(s, _) x -> (s + x, x)) (0,1)
419-- $ Stream.fromList [1,2,3,4]
420-- :}
421-- (10,24)
422--
423-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
424-- lazy expressions inside the accumulator, it is recommended that a strict
425-- data structure is used for accumulator.
426--
427{-# INLINE scan #-}
428scan :: Monad m => Fold m a b -> Stream m a -> Stream m b
429scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
431-- | Like 'scan' but restarts scanning afresh when the scanning fold
432-- terminates.
433--
434{-# INLINE scanMany #-}
435scanMany :: Monad m => Fold m a b -> Stream m a -> Stream m b
436scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m
438------------------------------------------------------------------------------
439-- Filtering
440------------------------------------------------------------------------------
442-- | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a
443-- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a
444-- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with
445-- index or time.
446--
447-- >>> filterWithIndex = Stream.with Stream.indexed Stream.filter
448--
449-- /Pre-release/
450{-# INLINE with #-}
451with :: Monad m =>
452 (Stream m a -> Stream m (s, a))
453 -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))
454 -> (((s, a) -> b) -> Stream m a -> Stream m a)
455with f comb g = fmap snd . comb g . f
457-- | Include only those elements that pass a predicate.
458--
459-- >>> filter p = Stream.filterM (return . p)
460-- >>> filter p = Stream.mapMaybe (\x -> if p x then Just x else Nothing)
461-- >>> filter p = Stream.scanMaybe (Fold.filtering p)
462--
463{-# INLINE filter #-}
464filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
465-- filter p = scanMaybe (FL.filtering p)
466filter p m = fromStreamD $ D.filter p $ toStreamD m
468-- | Same as 'filter' but with a monadic predicate.
469--
470-- >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing
471-- >>> filterM p = Stream.mapMaybeM (f p)
472--
473{-# INLINE filterM #-}
474filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
475filterM p m = fromStreamD $ D.filterM p $ toStreamD m
477-- | Drop repeated elements that are adjacent to each other using the supplied
478-- comparison function.
479--
480-- >>> uniq = Stream.uniqBy (==)
481--
482-- To strip duplicate path separators:
483--
484-- >>> input = Stream.fromList "//a//b"
485-- >>> f x y = x == '/' && y == '/'
486-- >>> Stream.fold Fold.toList $ Stream.uniqBy f input
487-- "/a/b"
488--
489-- Space: @O(1)@
490--
491-- /Pre-release/
492--
493{-# INLINE uniqBy #-}
494uniqBy :: Monad m =>
495 (a -> a -> Bool) -> Stream m a -> Stream m a
496-- uniqBy eq = scanMaybe (FL.uniqBy eq)
497uniqBy eq = catMaybes . rollingMap f
499 where
501 f pre curr =
502 case pre of
503 Nothing -> Just curr
504 Just x -> if x `eq` curr then Nothing else Just curr
506-- | Drop repeated elements that are adjacent to each other.
507--
508-- >>> uniq = Stream.uniqBy (==)
509--
510{-# INLINE uniq #-}
511uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
512-- uniq = scanMaybe FL.uniq
513uniq = fromStreamD . D.uniq . toStreamD
515-- | Strip all leading and trailing occurrences of an element passing a
516-- predicate and make all other consecutive occurrences uniq.
517--
518-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)
519--
520-- @
521-- > Stream.prune isSpace (Stream.fromList " hello world! ")
522-- "hello world!"
523--
524-- @
525--
526-- Space: @O(1)@
527--
528-- /Unimplemented/
529{-# INLINE prune #-}
530prune ::
531 -- (Monad m, Eq a) =>
532 (a -> Bool) -> Stream m a -> Stream m a
533prune = error "Not implemented yet!"
535-- Possible implementation:
536-- @repeated =
537-- Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@
538--
539-- 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a
540-- single element.
542-- | Emit only repeated elements, once.
543--
544-- /Unimplemented/
545repeated :: -- (Monad m, Eq a) =>
546 Stream m a -> Stream m a
547repeated = undefined
549-- | Deletes the first occurrence of the element in the stream that satisfies
550-- the given equality predicate.
551--
552-- >>> input = Stream.fromList [1,3,3,5]
553-- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input
554-- [1,3,5]
555--
556{-# INLINE deleteBy #-}
557deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
558-- deleteBy cmp x = scanMaybe (FL.deleteBy cmp x)
559deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
561------------------------------------------------------------------------------
562-- Trimming
563------------------------------------------------------------------------------
565-- | Same as 'takeWhile' but with a monadic predicate.
566--
567{-# INLINE takeWhileM #-}
568takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
569-- takeWhileM p = scanMaybe (FL.takingEndByM_ (\x -> not <$> p x))
570takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
572-- | Take all consecutive elements at the end of the stream for which the
573-- predicate is true.
574--
575-- O(n) space, where n is the number elements taken.
576--
577-- /Unimplemented/
578{-# INLINE takeWhileLast #-}
579takeWhileLast :: -- Monad m =>
580 (a -> Bool) -> Stream m a -> Stream m a
581takeWhileLast = undefined -- fromStreamD $ D.takeWhileLast n $ toStreamD m
583-- | Like 'takeWhile' and 'takeWhileLast' combined.
584--
585-- O(n) space, where n is the number elements taken from the end.
586--
587-- /Unimplemented/
588{-# INLINE takeWhileAround #-}
589takeWhileAround :: -- Monad m =>
590 (a -> Bool) -> Stream m a -> Stream m a
591takeWhileAround = undefined -- fromStreamD $ D.takeWhileAround n $ toStreamD m
593-- | Drop elements in the stream as long as the predicate succeeds and then
594-- take the rest of the stream.
595--
596{-# INLINE dropWhile #-}
597dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
598-- dropWhile p = scanMaybe (FL.droppingWhile p)
599dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m
601-- | Same as 'dropWhile' but with a monadic predicate.
602--
603{-# INLINE dropWhileM #-}
604dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
605-- dropWhileM p = scanMaybe (FL.droppingWhileM p)
606dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
608-- | Drop @n@ elements at the end of the stream.
609--
610-- O(n) space, where n is the number elements dropped.
611--
612-- /Unimplemented/
613{-# INLINE dropLast #-}
614dropLast :: -- Monad m =>
615 Int -> Stream m a -> Stream m a
616dropLast = undefined -- fromStreamD $ D.dropLast n $ toStreamD m
618-- | Drop all consecutive elements at the end of the stream for which the
619-- predicate is true.
620--
621-- O(n) space, where n is the number elements dropped.
622--
623-- /Unimplemented/
624{-# INLINE dropWhileLast #-}
625dropWhileLast :: -- Monad m =>
626 (a -> Bool) -> Stream m a -> Stream m a
627dropWhileLast = undefined -- fromStreamD $ D.dropWhileLast n $ toStreamD m
629-- | Like 'dropWhile' and 'dropWhileLast' combined.
630--
631-- O(n) space, where n is the number elements dropped from the end.
632--
633-- /Unimplemented/
634{-# INLINE dropWhileAround #-}
635dropWhileAround :: -- Monad m =>
636 (a -> Bool) -> Stream m a -> Stream m a
637dropWhileAround = undefined -- fromStreamD $ D.dropWhileAround n $ toStreamD m
639------------------------------------------------------------------------------
640-- Inserting Elements
641------------------------------------------------------------------------------
643-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
644-- @stream@ that is less than @elem@ when compared using @cmp@.
645--
646-- >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)
647--
648-- >>> input = Stream.fromList [1,3,5]
649-- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input
650-- [1,2,3,5]
651--
652{-# INLINE insertBy #-}
653insertBy ::Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
654insertBy cmp x m = fromStreamD $ D.insertBy cmp x (toStreamD m)
656-- | Insert a pure value between successive elements of a stream.
657--
658-- >>> input = Stream.fromList "hello"
659-- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input
660-- "h,e,l,l,o"
661--
662{-# INLINE intersperse #-}
663intersperse :: Monad m => a -> Stream m a -> Stream m a
664intersperse a = fromStreamD . D.intersperse a . toStreamD
666-- | Insert a side effect before consuming an element of a stream except the
667-- first one.
668--
669-- >>> input = Stream.fromList "hello"
670-- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input
671-- h.e.l.l.o
672--
673-- /Pre-release/
674{-# INLINE intersperseM_ #-}
675intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a
676intersperseM_ m = fromStreamD . D.intersperseM_ m . toStreamD
678-- | Intersperse a monadic action into the input stream after every @n@
679-- elements.
680--
681-- >> input = Stream.fromList "hello"
682-- >> Stream.fold Fold.toList $ Stream.intersperseBySpan 2 (return ',') input
683-- "he,ll,o"
684--
685-- /Unimplemented/
686{-# INLINE intersperseBySpan #-}
687intersperseBySpan :: -- Monad m =>
688 Int -> m a -> Stream m a -> Stream m a
689intersperseBySpan _n _f _xs = undefined
691-- | Insert an effect and its output after consuming an element of a stream.
692--
693-- >>> input = Stream.fromList "hello"
694-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseSuffix (putChar '.' >> return ',') input
695-- h.,e.,l.,l.,o.,"h,e,l,l,o,"
696--
697-- /Pre-release/
698{-# INLINE intersperseSuffix #-}
699intersperseSuffix :: Monad m => m a -> Stream m a -> Stream m a
700intersperseSuffix m = fromStreamD . D.intersperseSuffix m . toStreamD
702-- | Insert a side effect after consuming an element of a stream.
703--
704-- >>> input = Stream.fromList "hello"
705-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffix_ (threadDelay 1000000) input
706-- "hello"
707--
708-- /Pre-release/
709--
710{-# INLINE intersperseSuffix_ #-}
711intersperseSuffix_ :: Monad m => m b -> Stream m a -> Stream m a
712intersperseSuffix_ m = fromStreamD . D.intersperseSuffix_ m . toStreamD
714-- XXX Use an offset argument, like tapOffsetEvery
716-- | Like 'intersperseSuffix' but intersperses an effectful action into the
717-- input stream after every @n@ elements and after the last element.
718--
719-- >>> input = Stream.fromList "hello"
720-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffixBySpan 2 (return ',') input
721-- "he,ll,o,"
722--
723-- /Pre-release/
724--
725{-# INLINE intersperseSuffixBySpan #-}
726intersperseSuffixBySpan :: Monad m
727 => Int -> m a -> Stream m a -> Stream m a
728intersperseSuffixBySpan n eff =
729 fromStreamD . D.intersperseSuffixBySpan n eff . toStreamD
731-- | Insert a side effect before consuming an element of a stream.
732--
733-- >>> input = Stream.fromList "hello"
734-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.interspersePrefix_ (putChar '.' >> return ',') input
735-- .h.e.l.l.o"hello"
736--
737-- Same as 'trace_'.
738--
739-- /Pre-release/
740--
741{-# INLINE interspersePrefix_ #-}
742interspersePrefix_ :: Monad m => m b -> Stream m a -> Stream m a
743interspersePrefix_ m = mapM (\x -> void m >> return x)
745------------------------------------------------------------------------------
746-- Inserting Time
747------------------------------------------------------------------------------
749-- | Introduce a delay of specified seconds between elements of the stream.
750--
751-- >>> input = Stream.enumerateFromTo 1 3
752-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delay 1 input
753-- 1
754-- 2
755-- 3
756--
757-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000
758-- >>> delay n = Stream.intersperseM_ $ sleep n
759--
760{-# INLINE delay #-}
761delay :: MonadIO m => Double -> Stream m a -> Stream m a
762delay n = intersperseM_ $ liftIO $ threadDelay $ round $ n * 1000000
764-- | Introduce a delay of specified seconds after consuming an element of a
765-- stream.
766--
767-- >>> input = Stream.enumerateFromTo 1 3
768-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPost 1 input
769-- 1
770-- 2
771-- 3
772--
773-- /Pre-release/
774--
775{-# INLINE delayPost #-}
776delayPost :: MonadIO m => Double -> Stream m a -> Stream m a
777delayPost n = intersperseSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000
779-- | Introduce a delay of specified seconds before consuming an element of a
780-- stream.
781--
782-- >>> input = Stream.enumerateFromTo 1 3
783-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPre 1 input
784-- 1
785-- 2
786-- 3
787--
788-- /Pre-release/
789--
790{-# INLINE delayPre #-}
791delayPre :: MonadIO m => Double -> Stream m a -> Stream m a
792delayPre n = trace_ $ liftIO $ threadDelay $ round $ n * 1000000
794------------------------------------------------------------------------------
795-- Reorder in sequence
796------------------------------------------------------------------------------
798-- | Buffer until the next element in sequence arrives. The function argument
799-- determines the difference in sequence numbers. This could be useful in
800-- implementing sequenced streams, for example, TCP reassembly.
801--
802-- /Unimplemented/
803--
804{-# INLINE reassembleBy #-}
805reassembleBy
806 :: -- Monad m =>
807 Fold m a b
808 -> (a -> a -> Int)
809 -> Stream m a
810 -> Stream m b
811reassembleBy = undefined
813------------------------------------------------------------------------------
814-- Position Indexing
815------------------------------------------------------------------------------
817-- |
818-- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
819-- >>> indexed = Stream.postscan f
820-- >>> indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)
821-- >>> indexedR n = fmap (\(i, a) -> (n - i, a)) . indexed
822--
823-- Pair each element in a stream with its index, starting from index 0.
824--
825-- >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.fromList "hello"
826-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
827--
828{-# INLINE indexed #-}
829indexed :: Monad m => Stream m a -> Stream m (Int, a)
830-- indexed = scanMaybe FL.indexing
831indexed = fromStreamD . D.indexed . toStreamD
833-- |
834-- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
835-- >>> indexedR n = Stream.postscan (f n)
836--
837-- >>> s n = Stream.enumerateFromThen n (n - 1)
838-- >>> indexedR n = Stream.zipWith (,) (s n)
839--
840-- Pair each element in a stream with its index, starting from the
841-- given index @n@ and counting down.
842--
843-- >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.fromList "hello"
844-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
845--
846{-# INLINE indexedR #-}
847indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
848-- indexedR n = scanMaybe (FL.indexingRev n)
849indexedR n = fromStreamD . D.indexedR n . toStreamD
851-------------------------------------------------------------------------------
852-- Time Indexing
853-------------------------------------------------------------------------------
855-- Note: The timestamp stream must be the second stream in the zip so that the
856-- timestamp is generated after generating the stream element and not before.
857-- If we do not do that then the following example will generate the same
858-- timestamp for first two elements:
859--
860-- Stream.fold Fold.toList $ Stream.timestamped $ Stream.delay $ Stream.enumerateFromTo 1 3
861--
862-- | Pair each element in a stream with an absolute timestamp, using a clock of
863-- specified granularity. The timestamp is generated just before the element
864-- is consumed.
865--
866-- >>> Stream.fold Fold.toList $ Stream.timestampWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
867-- [(AbsTime (TimeSpec {sec = ..., nsec = ...}),1),(AbsTime (TimeSpec {sec = ..., nsec = ...}),2),(AbsTime (TimeSpec {sec = ..., nsec = ...}),3)]
868--
869-- /Pre-release/
870--
871{-# INLINE timestampWith #-}
872timestampWith :: (MonadIO m)
873 => Double -> Stream m a -> Stream m (AbsTime, a)
874timestampWith g stream = zipWith (flip (,)) stream (absTimesWith g)
876-- TBD: check performance vs a custom implementation without using zipWith.
877--
878-- /Pre-release/
879--
880{-# INLINE timestamped #-}
881timestamped :: (MonadIO m)
882 => Stream m a -> Stream m (AbsTime, a)
883timestamped = timestampWith 0.01
885-- | Pair each element in a stream with relative times starting from 0, using a
886-- clock with the specified granularity. The time is measured just before the
887-- element is consumed.
888--
889-- >>> Stream.fold Fold.toList $ Stream.timeIndexWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
890-- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]
891--
892-- /Pre-release/Monad
893--
894{-# INLINE timeIndexWith #-}
895timeIndexWith :: (MonadIO m)
896 => Double -> Stream m a -> Stream m (RelTime64, a)
897timeIndexWith g stream = zipWith (flip (,)) stream (relTimesWith g)
899-- | Pair each element in a stream with relative times starting from 0, using a
900-- 10 ms granularity clock. The time is measured just before the element is
901-- consumed.
902--
903-- >>> Stream.fold Fold.toList $ Stream.timeIndexed $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
904-- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]
905--
906-- /Pre-release/
907--
908{-# INLINE timeIndexed #-}
909timeIndexed :: (MonadIO m)
910 => Stream m a -> Stream m (RelTime64, a)
911timeIndexed = timeIndexWith 0.01
913------------------------------------------------------------------------------
914-- Searching
915------------------------------------------------------------------------------
917-- | Find all the indices where the value of the element in the stream is equal
918-- to the given value.
919--
920-- >>> elemIndices a = Stream.findIndices (== a)
921--
922{-# INLINE elemIndices #-}
923elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
924elemIndices a = findIndices (== a)
926------------------------------------------------------------------------------
927-- Rolling map
928------------------------------------------------------------------------------
930-- XXX this is not a one-to-one map so calling it map may not be right.
931-- We can perhaps call it zipWithTail or rollWith.
933-- | Apply a function on every two successive elements of a stream. The first
934-- argument of the map function is the previous element and the second argument
935-- is the current element. When the current element is the first element, the
936-- previous element is 'Nothing'.
937--
938-- /Pre-release/
939--
940{-# INLINE rollingMap #-}
941rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
942-- rollingMap f = scanMaybe (FL.slide2 $ Window.rollingMap f)
943rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
945-- | Like 'rollingMap' but with an effectful map function.
946--
947-- /Pre-release/
948--
949{-# INLINE rollingMapM #-}
950rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
951-- rollingMapM f = scanMaybe (FL.slide2 $ Window.rollingMapM f)
952rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
954-- | Like 'rollingMap' but requires at least two elements in the stream,
955-- returns an empty stream otherwise.
956--
957-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.
958--
959-- /Pre-release/
960--
961{-# INLINE rollingMap2 #-}
962rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b
963rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m
965------------------------------------------------------------------------------
966-- Maybe Streams
967------------------------------------------------------------------------------
969-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
970-- elements, and return a stream of values extracted from 'Just'.
971--
972-- Equivalent to:
973--
974-- >>> mapMaybe f = Stream.catMaybes . fmap f
975--
976{-# INLINE mapMaybe #-}
977mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
978mapMaybe f m = fromStreamD $ D.mapMaybe f $ toStreamD m
980-- | Like 'mapMaybe' but maps a monadic function.
981--
982-- Equivalent to:
983--
984-- >>> mapMaybeM f = Stream.catMaybes . Stream.mapM f
985--
986-- >>> mapM f = Stream.mapMaybeM (\x -> Just <$> f x)
987--
988{-# INLINE_EARLY mapMaybeM #-}
989mapMaybeM :: Monad m
990 => (a -> m (Maybe b)) -> Stream m a -> Stream m b
991mapMaybeM f = fmap fromJust . filter isJust . mapM f
993------------------------------------------------------------------------------
994-- Either streams
995------------------------------------------------------------------------------
997-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
998--
999-- >>> catLefts = fmap (fromLeft undefined) . Stream.filter isLeft
1001-- /Pre-release/
1003{-# INLINE catLefts #-}
1004catLefts :: Monad m => Stream m (Either a b) -> Stream m a
1005catLefts = fmap (fromLeft undefined) . filter isLeft
1007-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
1009-- >>> catRights = fmap (fromRight undefined) . Stream.filter isRight
1011-- /Pre-release/
1013{-# INLINE catRights #-}
1014catRights :: Monad m => Stream m (Either a b) -> Stream m b
1015catRights = fmap (fromRight undefined) . filter isRight
1017-- | Remove the either wrapper and flatten both lefts and as well as rights in
1018-- the output stream.
1020-- >>> catEithers = fmap (either id id)
1022-- /Pre-release/
1024{-# INLINE catEithers #-}
1025catEithers :: Monad m => Stream m (Either a a) -> Stream m a
1026catEithers = fmap (either id id)