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
9module Streamly.Internal.Data.Stream.Transform
12 -- | Pass through a 'Pipe'.
20 -- | Stateless one-to-one maps.
24 -- * Mapping Side Effects (Observation)
25 -- | See also the intersperse*_ combinators.
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.
53 -- | Produce a subset of the stream trimmed at ends.
62 -- * Inserting Elements
63 -- | Produce a superset of the stream. This is the opposite of
64 -- filtering/sampling. We can always use concatMap and scan for inserting
65 -- but these combinators are more efficient and convenient.
67 -- Element agnostic (Opposite of sampling)
69 , intersperseM -- XXX naming
73 , intersperseSuffixBySpan
75 -- , interspersePrefix
76 -- , interspersePrefixBySpan
78 -- * Inserting Side Effects/Time
79 , intersperseM_ -- XXX naming
86 -- * Element Aware Insertion
87 -- | Opposite of filtering
89 -- , intersperseByBefore
90 -- , intersperseByAfter
97 -- * Position Indexing
102 , findIndices -- XXX indicesBy
103 , elemIndices -- XXX indicesOf
106 -- | Map using the previous element.
112 -- Move these to Streamly.Data.Maybe.Stream?
113 , catMaybes -- XXX justs (like lefts/rights)
118 -- Move these to Streamly.Data.Either.Stream?
127import Control.Concurrent (threadDelay)
128import Control.Monad (void)
129import Control.Monad.IO.Class (MonadIO (liftIO))
130import Control.Monad.Trans.Class (MonadTrans(..))
131import Data.Either (fromLeft, isLeft, isRight, fromRight)
132import Data.Maybe (isJust, fromJust)
134import Streamly.Internal.Data.Fold.Type (Fold)
135import Streamly.Internal.Data.Pipe (Pipe)
137import qualified Streamly.Internal.Data.Fold as FL
138import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
139import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
140import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
142import Streamly.Internal.Data.Stream.Bottom
143import Streamly.Internal.Data.Stream.Type
146 ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence
147 , reverse, foldr1 , repeat, scanl, scanl1, zipWith)
152-- >>> import Control.Concurrent (threadDelay)
153-- >>> import Control.Monad.IO.Class (MonadIO (liftIO))
154-- >>> import Control.Monad.Trans (lift)
155-- >>> import Control.Monad.Trans.Identity (runIdentityT)
156-- >>> import Data.Function ((&))
157-- >>> import Data.Maybe (fromJust, isJust)
158-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
159-- >>> import Streamly.Internal.Data.Stream as Stream
160-- >>> import qualified Streamly.Data.Fold as Fold
161-- >>> import qualified Streamly.Internal.Data.Fold as Fold
162-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
163-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
165-- >>> hSetBuffering stdout LineBuffering
167-- XXX because of the use of D.cons for appending, folds and scans have
168-- quadratic complexity when iterated over a stream. We should use StreamK for
169-- linear performance on iteration.
171------------------------------------------------------------------------------
173------------------------------------------------------------------------------
175-- | Use a 'Pipe' to transform a stream.
179{-# INLINE transform #-}
180transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b
181transform pipe xs = fromStreamD $ D.transform pipe (toStreamD xs)
183------------------------------------------------------------------------------
184-- Transformation Folds
185------------------------------------------------------------------------------
187-- | Right fold to a streaming monad.
189-- > foldrS Stream.cons Stream.nil === id
191-- 'foldrS' can be used to perform stateless stream to stream transformations
192-- like map and filter in general. It can be coupled with a scan to perform
193-- stateful transformations. However, note that the custom map and filter
194-- routines can be much more efficient than this due to better stream fusion.
196-- >>> input = Stream.fromList [1..5]
197-- >>> Stream.fold Fold.toList $ Stream.foldrS Stream.cons Stream.nil input
200-- Find if any element in the stream is 'True':
202-- >>> step x xs = if odd x then return True else xs
203-- >>> input = Stream.fromList (2:4:5:undefined) :: Stream IO Int
204-- >>> Stream.fold Fold.toList $ Stream.foldrS step (return False) input
207-- Map (+2) on odd elements and filter out the even elements:
209-- >>> step x xs = if odd x then (x + 2) `Stream.cons` xs else xs
210-- >>> input = Stream.fromList [1..5] :: Stream IO Int
211-- >>> Stream.fold Fold.toList $ Stream.foldrS step Stream.nil input
217 (a -> Stream m b -> Stream m b)
224 (\y ys -> toStreamK $ f y (fromStreamK ys))
228-- | Right fold to a transformer monad. This is the most general right fold
229-- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'
230-- implementation can be more efficient:
232-- >>> foldrS = foldrT
234-- >>> step f x xs = lift $ f x (runIdentityT xs)
235-- >>> foldrM f z s = runIdentityT $ foldrT (step f) (lift z) s
237-- 'foldrT' can be used to translate streamly streams to other transformer
238-- monads e.g. to a different streaming type.
242foldrT :: (Monad m, Monad (s m), MonadTrans s)
243 => (a -> s m b -> s m b) -> s m b -> Stream m a -> s m b
244foldrT f z s = D.foldrT f z (toStreamD s)
246------------------------------------------------------------------------------
247-- Transformation by Mapping
248------------------------------------------------------------------------------
251-- >>> mapM f = Stream.sequence . fmap f
253-- Apply a monadic function to each element of the stream and replace it with
254-- the output of the resulting action.
256-- >>> s = Stream.fromList ["a", "b", "c"]
257-- >>> Stream.fold Fold.drain $ Stream.mapM putStr s
263mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
264mapM f m = fromStreamK $ D.toStreamK $ D.mapM f $ toStreamD m
267-- >>> sequence = Stream.mapM id
269-- Replace the elements of a stream of monadic actions with the outputs of
272-- >>> s = Stream.fromList [putStr "a", putStr "b", putStrLn "c"]
273-- >>> Stream.fold Fold.drain $ Stream.sequence s
276{-# INLINE sequence #-}
277sequence :: Monad m => Stream m (m a) -> Stream m a
280------------------------------------------------------------------------------
281-- Mapping side effects
282------------------------------------------------------------------------------
284-- | Tap the data flowing through a stream into a 'Fold'. For example, you may
285-- add a tap to log the contents flowing through the stream. The fold is used
286-- only for effects, its result is discarded.
291-- -----stream m a ---------------stream m a-----
295-- >>> s = Stream.enumerateFromTo 1 2
296-- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainBy print) s
300-- Compare with 'trace'.
303tap :: Monad m => FL.Fold m a b -> Stream m a -> Stream m a
304tap f xs = fromStreamD $ D.tap f (toStreamD xs)
306-- | Apply a monadic function to each element flowing through the stream and
307-- discard the results.
309-- >>> s = Stream.enumerateFromTo 1 2
310-- >>> Stream.fold Fold.drain $ Stream.trace print s
314-- Compare with 'tap'.
317trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a
318trace f = mapM (\x -> void (f x) >> return x)
320-- | Perform a side effect before yielding each element of the stream and
321-- discard the results.
323-- >>> s = Stream.enumerateFromTo 1 2
324-- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s
328-- Same as 'interspersePrefix_' but always serial.
334trace_ :: Monad m => m b -> Stream m a -> Stream m a
335trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD
337------------------------------------------------------------------------------
338-- Scanning with a Fold
339------------------------------------------------------------------------------
341-- XXX It may be useful to have a version of scan where we can keep the
342-- accumulator independent of the value emitted. So that we do not necessarily
343-- have to keep a value in the accumulator which we are not using. We can pass
344-- an extraction function that will take the accumulator and the current value
345-- of the element and emit the next value in the stream. That will also make it
346-- possible to modify the accumulator after using it. In fact, the step function
347-- can return new accumulator and the value to be emitted. The signature would
348-- be more like mapAccumL.
350-- | Strict left scan. Scan a stream using the given monadic fold.
352-- >>> s = Stream.fromList [1..10]
353-- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s
356-- See also: 'usingStateT'
360-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)
362-- Like 'map', 'scanl'' too is a one to one transformation,
363-- however it adds an extra element.
365-- >>> s = Stream.fromList [1,2,3,4]
366-- >>> Stream.fold Fold.toList $ scanl' (+) 0 s
369-- >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s
370-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
372-- The output of 'scanl'' is the initial value of the accumulator followed by
373-- all the intermediate steps and the final result of 'foldl''.
375-- By streaming the accumulated state after each fold step, we can share the
376-- state across multiple stages of stream composition. Each stage can modify or
377-- extend the state, do some processing with it and emit it for the next stage,
378-- thus modularizing the stream processing. This can be useful in
379-- stateful or event-driven programming.
381-- Consider the following monolithic example, computing the sum and the product
382-- of the elements in a stream in one go using a @foldl'@:
384-- >>> foldl' step z = Stream.fold (Fold.foldl' step z)
385-- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s
388-- Using @scanl'@ we can make it modular by computing the sum in the first
389-- stage and passing it down to the next stage for computing the product:
392-- foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)
393-- $ scanl' (\(s, _) x -> (s + x, x)) (0,1)
394-- $ Stream.fromList [1,2,3,4]
398-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
399-- lazy expressions inside the accumulator, it is recommended that a strict
400-- data structure is used for accumulator.
403scan :: Monad m => Fold m a b -> Stream m a -> Stream m b
404scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
406-- | Like 'scan' but restarts scanning afresh when the scanning fold
410{-# INLINE scanMany #-}
411scanMany :: Monad m => Fold m a b -> Stream m a -> Stream m b
412scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m
414------------------------------------------------------------------------------
416------------------------------------------------------------------------------
418-- | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a
419-- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a
420-- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with
423-- >>> filterWithIndex = Stream.with Stream.indexed Stream.filter
428 (Stream m a -> Stream m (s, a))
429 -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))
430 -> (((s, a) -> b) -> Stream m a -> Stream m a)
431with f comb g = fmap snd . comb g . f
433-- | Include only those elements that pass a predicate.
436filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
437filter p m = fromStreamD $ D.filter p $ toStreamD m
439-- | Same as 'filter' but with a monadic predicate.
441{-# INLINE filterM #-}
442filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
443filterM p m = fromStreamD $ D.filterM p $ toStreamD m
445-- | Use a filtering fold on a stream.
447-- >>> input = Stream.fromList [1..10]
448-- >>> Stream.fold Fold.sum $ Stream.foldFilter (Fold.satisfy (> 5)) input
453{-# INLINE foldFilter #-}
454foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
455foldFilter p = fromStreamD . D.foldFilter p . toStreamD
457-- | Drop repeated elements that are adjacent to each other using the supplied
458-- comparison function.
460-- >>> uniq = Stream.uniqBy (==)
462-- To strip duplicate path separators:
464-- >>> input = Stream.fromList "//a//b"
465-- >>> f x y = x == '/' && y == '/'
466-- >>> Stream.fold Fold.toList $ Stream.uniqBy f input
477 (a -> a -> Bool) -> Stream m a -> Stream m a
478uniqBy eq = catMaybes . rollingMap f
485 Just x -> if x `eq` curr then Nothing else Just curr
487-- | Drop repeated elements that are adjacent to each other.
490uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
491uniq = fromStreamD . D.uniq . toStreamD
493-- | Strip all leading and trailing occurrences of an element passing a
494-- predicate and make all other consecutive occurrences uniq.
496-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)
499-- > Stream.prune isSpace (Stream.fromList " hello world! ")
509 -- (Monad m, Eq a) =>
510 (a -> Bool) -> Stream m a -> Stream m a
511prune = error "Not implemented yet!"
513-- Possible implementation:
515-- Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@
517-- 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a
520-- | Emit only repeated elements, once.
523repeated :: -- (Monad m, Eq a) =>
524 Stream m a -> Stream m a
527-- We can have more efficient implementations for nubOrd and nubInt by using
528-- Set and IntSet to find/remove duplication. For Hashable we can use a
529-- hashmap. Use rewrite rules to specialize to more efficient impls.
531-- | Drop repeated elements anywhere in the stream.
533-- /Caution: not scalable for infinite streams/
538nubBy :: -- Monad m =>
539 (a -> a -> Bool) -> Stream m a -> Stream m a
540nubBy = undefined -- fromStreamD . D.nubBy . toStreamD
542-- | Deletes the first occurrence of the element in the stream that satisfies
543-- the given equality predicate.
545-- >>> input = Stream.fromList [1,3,3,5]
546-- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input
549{-# INLINE deleteBy #-}
550deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
551deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
553------------------------------------------------------------------------------
555------------------------------------------------------------------------------
557-- | Same as 'takeWhile' but with a monadic predicate.
559{-# INLINE takeWhileM #-}
560takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
561takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
563-- | Drop elements in the stream as long as the predicate succeeds and then
564-- take the rest of the stream.
566{-# INLINE dropWhile #-}
567dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
568dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m
570-- | Same as 'dropWhile' but with a monadic predicate.
572{-# INLINE dropWhileM #-}
573dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
574dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
576------------------------------------------------------------------------------
578------------------------------------------------------------------------------
580-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
581-- @stream@ that is less than @elem@ when compared using @cmp@.
583-- >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)
585-- >>> input = Stream.fromList [1,3,5]
586-- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input
589{-# INLINE insertBy #-}
590insertBy ::Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
591insertBy cmp x m = fromStreamD $ D.insertBy cmp x (toStreamD m)
593-- | Insert a pure value between successive elements of a stream.
595-- >>> input = Stream.fromList "hello"
596-- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input
599{-# INLINE intersperse #-}
600intersperse :: Monad m => a -> Stream m a -> Stream m a
601intersperse a = fromStreamD . D.intersperse a . toStreamD
603-- | Insert a side effect before consuming an element of a stream except the
606-- >>> input = Stream.fromList "hello"
607-- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input
611{-# INLINE intersperseM_ #-}
612intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a
613intersperseM_ m = fromStreamD . D.intersperseM_ m . toStreamD
615-- | Intersperse a monadic action into the input stream after every @n@
618-- >> input = Stream.fromList "hello"
619-- >> Stream.fold Fold.toList $ Stream.intersperseBySpan 2 (return ',') input
623{-# INLINE intersperseBySpan #-}
624intersperseBySpan :: -- Monad m =>
625 Int -> m a -> t m a -> t m a
626intersperseBySpan _n _f _xs = undefined
628-- | Insert an effect and its output after consuming an element of a stream.
630-- >>> input = Stream.fromList "hello"
631-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ intersperseSuffix (putChar '.' >> return ',') input
632-- h.,e.,l.,l.,o.,"h,e,l,l,o,"
635{-# INLINE intersperseSuffix #-}
636intersperseSuffix :: Monad m => m a -> Stream m a -> Stream m a
637intersperseSuffix m = fromStreamD . D.intersperseSuffix m . toStreamD
639-- | Insert a side effect after consuming an element of a stream.
641-- >>> input = Stream.fromList "hello"
642-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffix_ (threadDelay 1000000) input
647{-# INLINE intersperseSuffix_ #-}
648intersperseSuffix_ :: Monad m => m b -> Stream m a -> Stream m a
649intersperseSuffix_ m = fromStreamD . D.intersperseSuffix_ m . toStreamD
651-- XXX Use an offset argument, like tapOffsetEvery
653-- | Like 'intersperseSuffix' but intersperses an effectful action into the
654-- input stream after every @n@ elements and after the last element.
656-- >>> input = Stream.fromList "hello"
657-- >>> Stream.fold Fold.toList $ Stream.intersperseSuffixBySpan 2 (return ',') input
662{-# INLINE intersperseSuffixBySpan #-}
663intersperseSuffixBySpan :: Monad m
664 => Int -> m a -> Stream m a -> Stream m a
665intersperseSuffixBySpan n eff =
666 fromStreamD . D.intersperseSuffixBySpan n eff . toStreamD
668-- | Insert a side effect before consuming an element of a stream.
670-- >>> input = Stream.fromList "hello"
671-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.interspersePrefix_ (putChar '.' >> return ',') input
678{-# INLINE interspersePrefix_ #-}
679interspersePrefix_ :: Monad m => m b -> Stream m a -> Stream m a
680interspersePrefix_ m = mapM (\x -> void m >> return x)
682------------------------------------------------------------------------------
684------------------------------------------------------------------------------
686-- | Introduce a delay of specified seconds between elements of the stream.
688-- >>> input = Stream.enumerateFromTo 1 3
689-- >>> Stream.fold (Fold.drainBy print) $ Stream.delay 1 input
694-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000
695-- >>> delay n = Stream.intersperseM_ $ sleep n
698delay :: MonadIO m => Double -> Stream m a -> Stream m a
699delay n = intersperseM_ $ liftIO $ threadDelay $ round $ n * 1000000
701-- | Introduce a delay of specified seconds after consuming an element of a
704-- >>> input = Stream.enumerateFromTo 1 3
705-- >>> Stream.fold (Fold.drainBy print) $ Stream.delayPost 1 input
712{-# INLINE delayPost #-}
713delayPost :: MonadIO m => Double -> Stream m a -> Stream m a
714delayPost n = intersperseSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000
716-- | Introduce a delay of specified seconds before consuming an element of a
719-- >>> input = Stream.enumerateFromTo 1 3
720-- >>> Stream.fold (Fold.drainBy print) $ Stream.delayPre 1 input
727{-# INLINE delayPre #-}
728delayPre :: MonadIO m => Double -> Stream m a -> Stream m a
729delayPre n = trace_ $ liftIO $ threadDelay $ round $ n * 1000000
731------------------------------------------------------------------------------
732-- Reorder in sequence
733------------------------------------------------------------------------------
735-- | Buffer until the next element in sequence arrives. The function argument
736-- determines the difference in sequence numbers. This could be useful in
737-- implementing sequenced streams, for example, TCP reassembly.
741{-# INLINE reassembleBy #-}
748reassembleBy = undefined
750------------------------------------------------------------------------------
752------------------------------------------------------------------------------
755-- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
756-- >>> indexed = Stream.postscan f
757-- >>> indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)
759-- Pair each element in a stream with its index, starting from index 0.
761-- >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.fromList "hello"
762-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
764{-# INLINE indexed #-}
765indexed :: Monad m => Stream m a -> Stream m (Int, a)
766indexed = fromStreamD . D.indexed . toStreamD
769-- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
770-- >>> indexedR n = Stream.postscan (f n)
772-- >>> s n = Stream.enumerateFromThen n (n - 1)
773-- >>> indexedR n = Stream.zipWith (,) (s n)
775-- Pair each element in a stream with its index, starting from the
776-- given index @n@ and counting down.
778-- >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.fromList "hello"
779-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
781{-# INLINE indexedR #-}
782indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
783indexedR n = fromStreamD . D.indexedR n . toStreamD
785------------------------------------------------------------------------------
787------------------------------------------------------------------------------
789-- | Find all the indices where the value of the element in the stream is equal
790-- to the given value.
792-- >>> elemIndices a = Stream.findIndices (== a)
794{-# INLINE elemIndices #-}
795elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
796elemIndices a = findIndices (== a)
798------------------------------------------------------------------------------
800------------------------------------------------------------------------------
802-- XXX this is not a one-to-one map so calling it map may not be right.
803-- We can perhaps call it zipWithTail or rollWith.
805-- | Apply a function on every two successive elements of a stream. The first
806-- argument of the map function is the previous element and the second argument
807-- is the current element. When the current element is the first element, the
808-- previous element is 'Nothing'.
812{-# INLINE rollingMap #-}
813rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
814rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
816-- | Like 'rollingMap' but with an effectful map function.
820{-# INLINE rollingMapM #-}
821rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
822rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
824-- | Like 'rollingMap' but requires at least two elements in the stream,
825-- returns an empty stream otherwise.
827-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.
831{-# INLINE rollingMap2 #-}
832rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b
833rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m
835------------------------------------------------------------------------------
837------------------------------------------------------------------------------
839-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
840-- elements, and return a stream of values extracted from 'Just'.
844-- >>> mapMaybe f = fmap fromJust . Stream.filter isJust . fmap f
846{-# INLINE mapMaybe #-}
847mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
848mapMaybe f m = fromStreamD $ D.mapMaybe f $ toStreamD m
850-- | Like 'mapMaybe' but maps a monadic function.
854-- >>> mapMaybeM f = fmap fromJust . Stream.filter isJust . Stream.mapM f
856{-# INLINE_EARLY mapMaybeM #-}
858 => (a -> m (Maybe b)) -> Stream m a -> Stream m b
859mapMaybeM f = fmap fromJust . filter isJust . mapM f
861-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
865{-# INLINE catMaybes #-}
866catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
867catMaybes = fmap fromJust . filter isJust
869------------------------------------------------------------------------------
871------------------------------------------------------------------------------
873-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
878lefts :: Monad m => Stream m (Either a b) -> Stream m a
879lefts = fmap (fromLeft undefined) . filter isLeft
881-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
886rights :: Monad m => Stream m (Either a b) -> Stream m b
887rights = fmap (fromRight undefined) . filter isRight
889-- | Remove the either wrapper and flatten both lefts and as well as rights in
895both :: Monad m => Stream m (Either a a) -> Stream m a
896both = fmap (either id id)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list