2-- Module : Streamly.Internal.Data.Stream.IsStream.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.IsStream.Transform
12 -- | Pass through a 'Pipe'.
21 -- | Stateless one-to-one maps.
27 -- * Mapping Side Effects (Observation)
28 -- | See also the intersperse*_ combinators.
39 -- * Scanning By 'Fold'
45 -- | Left scans. Stateful, mostly one-to-one maps.
57 -- XXX Once we have pipes the contravariant transformations can be
58 -- represented by attaching pipes before a transformation.
71 -- | Produce a subset of the stream using criteria based on the values of
72 -- the elements. We can use a concatMap and scan for filtering but these
73 -- combinators are more efficient and convenient.
87 -- | Produce a subset of the stream trimmed at ends.
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.
111 -- Element agnostic (Opposite of sampling)
113 , intersperseM -- XXX naming
117 , intersperseSuffixBySpan
120 -- , interspersePrefix
121 -- , interspersePrefixBySpan
123 -- * Inserting Side Effects/Time
124 , intersperseM_ -- XXX naming
131 -- * Element Aware Insertion
132 -- | Opposite of filtering
134 -- , intersperseByBefore
135 -- , intersperseByAfter
142 -- * Position Indexing
149 -- , timestampedR -- timer
154 , findIndices -- XXX indicesBy
155 , elemIndices -- XXX indicesOf
158 -- | Map using the previous element.
164 -- Move these to Streamly.Data.Maybe.Stream?
165 , catMaybes -- XXX justs (like lefts/rights)
170 -- Move these to Streamly.Data.Either.Stream?
175 -- * Concurrent Evaluation
176 -- ** Concurrent Pipelines
177 -- | Run streaming stages concurrently.
185 -- ** Concurrency Control
188 -- ** Buffering and Sampling
189 -- | Evaluate strictly using a buffer of results. When the buffer becomes
190 -- full we can block, drop the new elements, drop the oldest element and
191 -- insert the new at the end or keep dropping elements uniformly to match
192 -- the rate of the consumer.
199 -- | Evaluate the stream at uniform intervals to maintain a specified
218import Control.Concurrent (threadDelay)
219import Control.Monad (void)
220import Control.Monad.Catch (MonadCatch)
221import Control.Monad.IO.Class (MonadIO(..))
222import Control.Monad.Trans.Class (MonadTrans(..))
223import Data.Either (isLeft, isRight, fromLeft, fromRight)
224import Data.Kind (Type)
225import Data.Maybe (isJust, fromJust)
226import Streamly.Internal.Data.Fold.Type (Fold (..))
227import Streamly.Internal.Data.Pipe.Type (Pipe (..))
228import Streamly.Internal.Data.Stream.IsStream.Combinators
229 ( inspectMode, maxBuffer, maxThreads, rate, avgRate, minRate
230 , maxRate, constRate)
231import Streamly.Internal.Data.Stream.IsStream.Common
251import Streamly.Internal.Control.Concurrent (MonadAsync)
252import Streamly.Internal.Data.Stream.IsStream.Type
253 (IsStream(..), fromStreamS, toStreamS, fromStreamD, toStreamD, toConsK)
254import Streamly.Internal.Data.Stream.Serial (SerialT)
255import Streamly.Internal.Data.SVar (Rate(..))
256import Streamly.Internal.Data.Time.Units (TimeUnit64, AbsTime, RelTime64)
258import qualified Streamly.Internal.Data.Fold as FL
259import qualified Streamly.Internal.Data.Stream.Parallel as Par
260import qualified Streamly.Internal.Data.Stream.Serial as Serial
261import qualified Streamly.Internal.Data.Stream.StreamD as D
262import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
263#ifdef USE_STREAMK_ONLY
264import qualified Streamly.Internal.Data.Stream.StreamK as S
266import qualified Streamly.Internal.Data.Stream.StreamD as S
268import qualified Prelude
271 ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence
272 , reverse, foldr1 , scanl, scanl1, zipWith)
277-- >>> import Control.Concurrent (threadDelay)
278-- >>> import Data.Function ((&))
279-- >>> import Streamly.Prelude ((|$))
280-- >>> import Prelude hiding ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
281-- >>> import qualified Streamly.Prelude as Stream
282-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
283-- >>> import qualified Streamly.Data.Fold as Fold
284-- >>> import qualified Streamly.Internal.Data.Fold as Fold
285-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
287-- >>> hSetBuffering stdout LineBuffering
289------------------------------------------------------------------------------
291------------------------------------------------------------------------------
293-- | Use a 'Pipe' to transform a stream.
297{-# INLINE transform #-}
298transform :: (IsStream t, Monad m) => Pipe m a b -> t m a -> t m b
299transform pipe xs = fromStreamD $ D.transform pipe (toStreamD xs)
301------------------------------------------------------------------------------
302-- Transformation Folds
303------------------------------------------------------------------------------
305-- | Right fold to a streaming monad.
307-- > foldrS Stream.cons Stream.nil === id
309-- 'foldrS' can be used to perform stateless stream to stream transformations
310-- like map and filter in general. It can be coupled with a scan to perform
311-- stateful transformations. However, note that the custom map and filter
312-- routines can be much more efficient than this due to better stream fusion.
314-- >>> Stream.toList $ Stream.foldrS Stream.cons Stream.nil $ Stream.fromList [1..5]
317-- Find if any element in the stream is 'True':
319-- >>> Stream.toList $ Stream.foldrS (\x xs -> if odd x then return True else xs) (return False) $ (Stream.fromList (2:4:5:undefined) :: Stream.SerialT IO Int)
322-- Map (+2) on odd elements and filter out the even elements:
324-- >>> Stream.toList $ Stream.foldrS (\x xs -> if odd x then (x + 2) `Stream.cons` xs else xs) Stream.nil $ (Stream.fromList [1..5] :: Stream.SerialT IO Int)
327-- 'foldrM' can also be represented in terms of 'foldrS', however, the former
328-- is much more efficient:
330-- > foldrM f z s = runIdentityT $ foldrS (\x xs -> lift $ f x (runIdentityT xs)) (lift z) s
334foldrS :: IsStream t => (a -> t m b -> t m b) -> t m b -> t m a -> t m b
338 (\y ys -> toStream $ f y (fromStream ys))
342{-# INLINE foldrSShared #-}
343foldrSShared :: IsStream t => (a -> t m b -> t m b) -> t m b -> t m a -> t m b
347 (\y ys -> toStream $ f y (fromStream ys))
351-- | Right fold to a transformer monad. This is the most general right fold
352-- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'
353-- implementation can be more efficient:
356-- > foldrM f z s = runIdentityT $ foldrT (\x xs -> lift $ f x (runIdentityT xs)) (lift z) s
358-- 'foldrT' can be used to translate streamly streams to other transformer
359-- monads e.g. to a different streaming type.
363foldrT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
364 => (a -> s m b -> s m b) -> s m b -> t m a -> s m b
365foldrT f z s = S.foldrT f z (toStreamS s)
367------------------------------------------------------------------------------
368-- Transformation by Mapping
369------------------------------------------------------------------------------
373-- mapM f = sequence . map f
376-- Apply a monadic function to each element of the stream and replace it with
377-- the output of the resulting action.
380-- >>> drain $ Stream.mapM putStr $ Stream.fromList ["a", "b", "c"]
384-- drain $ Stream.replicateM 10 (return 1)
385-- & (fromSerial . Stream.mapM (\x -> threadDelay 1000000 >> print x))
391-- > drain $ Stream.replicateM 10 (return 1)
392-- & (fromAsync . Stream.mapM (\x -> threadDelay 1000000 >> print x))
395-- /Concurrent (do not use with 'fromParallel' on infinite streams)/
398{-# INLINE_EARLY mapM #-}
399mapM :: forall t m a b. (IsStream t, MonadAsync m) =>
400 (a -> m b) -> t m a -> t m b
401mapM f = fromStream . K.mapMWith (toConsK (consM @t)) f . toStream
403{-# RULES "mapM serial" mapM = mapMSerial #-}
404{-# INLINE mapMSerial #-}
405mapMSerial :: Monad m => (a -> m b) -> SerialT m a -> SerialT m b
406mapMSerial = Serial.mapM
413-- Replace the elements of a stream of monadic actions with the outputs of
417-- >>> drain $ Stream.sequence $ Stream.fromList [putStr "a", putStr "b", putStrLn "c"]
421-- drain $ Stream.replicateM 3 (return $ threadDelay 1000000 >> print 1)
422-- & (fromSerial . Stream.sequence)
429-- drain $ Stream.replicateM 3 (return $ threadDelay 1000000 >> print 1)
430-- & (fromAsync . Stream.sequence)
438-- /Concurrent (do not use with 'fromParallel' on infinite streams)/
441{-# INLINE sequence #-}
442sequence :: (IsStream t, MonadAsync m) => t m (m a) -> t m a
445------------------------------------------------------------------------------
446-- Mapping side effects
447------------------------------------------------------------------------------
449-- | Tap the data flowing through a stream into a 'Fold'. For example, you may
450-- add a tap to log the contents flowing through the stream. The fold is used
451-- only for effects, its result is discarded.
456-- -----stream m a ---------------stream m a-----
460-- >>> Stream.drain $ Stream.tap (Fold.drainBy print) (Stream.enumerateFromTo 1 2)
464-- Compare with 'trace'.
468tap :: (IsStream t, Monad m) => FL.Fold m a b -> t m a -> t m a
469tap f xs = fromStreamD $ D.tap f (toStreamD xs)
471-- XXX Remove this. It can be expressed in terms of Fold.sampleFromThen.
473-- | @tapOffsetEvery offset n@ taps every @n@th element in the stream
474-- starting at @offset@. @offset@ can be between @0@ and @n - 1@. Offset 0
475-- means start at the first element in the stream. If the offset is outside
476-- this range then @offset `mod` n@ is used as offset.
478-- >>> Stream.drain $ Stream.tapOffsetEvery 0 2 (Fold.rmapM print Fold.toList) $ Stream.enumerateFromTo 0 10
484{-# INLINE tapOffsetEvery #-}
485tapOffsetEvery :: (IsStream t, Monad m)
486 => Int -> Int -> FL.Fold m a b -> t m a -> t m a
487tapOffsetEvery offset n f xs =
488 fromStreamD $ D.tapOffsetEvery offset n f (toStreamD xs)
490-- | Redirect a copy of the stream to a supplied fold and run it concurrently
491-- in an independent thread. The fold may buffer some elements. The buffer size
492-- is determined by the prevailing 'maxBuffer' setting.
497-- -----stream m a ---------------stream m a-----
502-- >>> Stream.drain $ Stream.tapAsync (Fold.drainBy print) (Stream.enumerateFromTo 1 2)
508-- Exceptions from the concurrently running fold are propagated to the current
509-- computation. Note that, because of buffering in the fold, exceptions may be
510-- delayed and may not correspond to the current element being processed in the
511-- parent stream, but we guarantee that before the parent stream stops the tap
512-- finishes and all exceptions from it are drained.
514-- >>> tapAsync f = Stream.tapAsyncK (Stream.fold f . Stream.adapt)
516-- Compare with 'tap'.
519{-# INLINE tapAsync #-}
520tapAsync :: (IsStream t, MonadAsync m) => FL.Fold m a b -> t m a -> t m a
521tapAsync f xs = fromStreamD $ Par.tapAsyncF f (toStreamD xs)
523-- XXX We should keep only tapAsyncK as tapAsync can be implemented in terms
524-- of tapAsyncK and it has better performance. Rename this to tapAsync.
526-- | Like 'tapAsyncF' but uses a stream fold function instead of a 'Fold' type.
529{-# INLINE tapAsyncK #-}
530tapAsyncK :: (IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> t m a
531tapAsyncK f m = fromStream $ Par.tapAsyncK (f . fromStream) (toStream m)
533-- XXX rename this to tapManyAsync_?
535-- | Concurrently distribute a stream to a collection of fold functions,
536-- discarding the outputs of the folds.
539-- > Stream.drain $ Stream.distributeAsync_ [Stream.mapM_ print, Stream.mapM_ print] (Stream.enumerateFromTo 1 2)
548-- distributeAsync_ = flip (foldr tapAsync)
553{-# INLINE distributeAsync_ #-}
554distributeAsync_ :: (Foldable f, IsStream t, MonadAsync m)
555 => f (t m a -> m b) -> t m a -> t m a
556distributeAsync_ = flip (Prelude.foldr tapAsyncK)
558-- | @pollCounts predicate transform fold stream@ counts those elements in the
559-- stream that pass the @predicate@. The resulting count stream is sent to
560-- another thread which transforms it using @transform@ and then folds it using
561-- @fold@. The thread is automatically cleaned up if the stream stops or
562-- aborts due to exception.
564-- For example, to print the count of elements processed every second:
567-- > Stream.drain $ Stream.pollCounts (const True) (Stream.rollingMap (-) . Stream.delayPost 1) (FLold.drainBy print)
568-- $ Stream.enumerateFrom 0
571-- Note: This may not work correctly on 32-bit machines.
575{-# INLINE pollCounts #-}
577 (IsStream t, MonadAsync m)
579 -> (t m Int -> t m Int)
583pollCounts predicate transf f xs =
585 $ D.pollCounts predicate (toStreamD . transf . fromStreamD) f
588-- | Calls the supplied function with the number of elements consumed
589-- every @n@ seconds. The given function is run in a separate thread
590-- until the end of the stream. In case there is an exception in the
591-- stream the thread is killed during the next major GC.
593-- Note: The action is not guaranteed to run if the main thread exits.
596-- > delay n = threadDelay (round $ n * 1000000) >> return n
597-- > Stream.toList $ Stream.tapRate 2 (\n -> print $ show n ++ " elements processed") (delay 1 Stream.|: delay 0.5 Stream.|: delay 0.5 Stream.|: Stream.nil)
598-- "2 elements processed"
600-- "1 elements processed"
603-- Note: This may not work correctly on 32-bit machines.
606{-# INLINE tapRate #-}
608 (IsStream t, MonadAsync m, MonadCatch m)
613tapRate n f xs = fromStreamD $ D.tapRate n f $ toStreamD xs
615-- | Apply a monadic function to each element flowing through the stream and
616-- discard the results.
619-- >>> Stream.drain $ Stream.trace print (Stream.enumerateFromTo 1 2)
625-- Compare with 'tap'.
629trace :: (IsStream t, MonadAsync m) => (a -> m b) -> t m a -> t m a
630trace f = mapM (\x -> void (f x) >> return x)
632-- | Perform a side effect before yielding each element of the stream and
633-- discard the results.
636-- >>> Stream.drain $ Stream.trace_ (print "got here") (Stream.enumerateFromTo 1 2)
642-- Same as 'interspersePrefix_' but always serial.
648trace_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
649trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD
651------------------------------------------------------------------------------
652-- Scanning with a Fold
653------------------------------------------------------------------------------
655-- | Scan a stream using the given monadic fold.
657-- >>> Stream.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum (Stream.fromList [1..10])
662scan :: (IsStream t, Monad m) => Fold m a b -> t m a -> t m b
663scan fld m = fromStreamD $ D.scanOnce fld $ toStreamD m
665-- | Like 'scan' but restarts scanning afresh when the scanning fold
669{-# INLINE scanMany #-}
670scanMany :: (IsStream t, Monad m) => Fold m a b -> t m a -> t m b
671scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m
673-- | Postscan a stream using the given monadic fold.
675-- The following example extracts the input stream up to a point where the
676-- running average of elements is no more than 10:
678-- >>> import Data.Maybe (fromJust)
679-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
682-- $ Stream.map (fromJust . fst)
683-- $ Stream.takeWhile (\(_,x) -> x <= 10)
684-- $ Stream.postscan (Fold.tee Fold.last avg) (Stream.enumerateFromTo 1.0 100.0)
686-- [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]
689{-# INLINE postscan #-}
690postscan :: (IsStream t, Monad m) => Fold m a b -> t m a -> t m b
691postscan fld = fromStreamD . D.postscanOnce fld . toStreamD
693------------------------------------------------------------------------------
694-- Scanning - Transformation by Folding
695------------------------------------------------------------------------------
697-- XXX It may be useful to have a version of scan where we can keep the
698-- accumulator independent of the value emitted. So that we do not necessarily
699-- have to keep a value in the accumulator which we are not using. We can pass
700-- an extraction function that will take the accumulator and the current value
701-- of the element and emit the next value in the stream. That will also make it
702-- possible to modify the accumulator after using it. In fact, the step function
703-- can return new accumulator and the value to be emitted. The signature would
704-- be more like mapAccumL. Or we can change the signature of scanx to
707-- | Strict left scan with an extraction function. Like 'scanl'', but applies a
708-- user supplied extraction function (the third argument) at each step. This is
709-- designed to work with the @foldl@ library. The suffix @x@ is a mnemonic for
714-- /Since: 0.7.0 (Monad m constraint)/
715{-# DEPRECATED scanx "Please use scanl followed by map instead." #-}
717scanx :: (IsStream t, Monad m) => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b
718scanx step begin done = fromStreamS . S.scanlx' step begin done . toStreamS
720-- XXX this needs to be concurrent
721-- XXX because of the use of D.cons for appending, scanlM' has quadratic
722-- complexity when iterated over a stream. We should use StreamK style scanlM'
723-- for linear performance on iteration.
725-- | Like 'scanl'' but with a monadic step function and a monadic seed.
729-- /Since: 0.8.0 (signature change)/
730{-# INLINE scanlM' #-}
731scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
732scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
734-- XXX because of the use of D.cons for appending, scanl' has quadratic
735-- complexity when iterated over a stream. We should use StreamK style scanlM'
736-- for linear performance on iteration.
738-- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,
739-- however it adds an extra element.
742-- >>> Stream.toList $ Stream.scanl' (+) 0 $ fromList [1,2,3,4]
748-- >>> Stream.toList $ Stream.scanl' (flip (:)) [] $ Stream.fromList [1,2,3,4]
749-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
753-- The output of 'scanl'' is the initial value of the accumulator followed by
754-- all the intermediate steps and the final result of 'foldl''.
756-- By streaming the accumulated state after each fold step, we can share the
757-- state across multiple stages of stream composition. Each stage can modify or
758-- extend the state, do some processing with it and emit it for the next stage,
759-- thus modularizing the stream processing. This can be useful in
760-- stateful or event-driven programming.
762-- Consider the following monolithic example, computing the sum and the product
763-- of the elements in a stream in one go using a @foldl'@:
766-- >>> Stream.foldl' (\(s, p) x -> (s + x, p * x)) (0,1) $ Stream.fromList [1,2,3,4]
771-- Using @scanl'@ we can make it modular by computing the sum in the first
772-- stage and passing it down to the next stage for computing the product:
776-- Stream.foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)
777-- $ Stream.scanl' (\(s, _) x -> (s + x, x)) (0,1)
778-- $ Stream.fromList [1,2,3,4]
784-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
785-- lazy expressions inside the accumulator, it is recommended that a strict
786-- data structure is used for accumulator.
788-- >>> scanl' step z = scan (Fold.foldl' step z)
789-- >>> scanl' f z xs = scanlM' (\a b -> return (f a b)) (return z) xs
790-- >>> scanl' f z xs = z `Stream.cons` postscanl' f z xs
792-- See also: 'usingStateT'
796scanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
797scanl' step z m = fromStreamS $ S.scanl' step z $ toStreamS m
799-- | Like 'scanl'' but does not stream the initial value of the accumulator.
801-- >>> postscanl' step z = postscan (Fold.foldl' step z)
802-- >>> postscanl' f z = postscanlM' (\a b -> return (f a b)) (return z)
803-- >>> postscanl' f z xs = Stream.drop 1 $ Stream.scanl' f z xs
806{-# INLINE postscanl' #-}
807postscanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
808postscanl' step z m = fromStreamD $ D.postscanl' step z $ toStreamD m
810-- XXX prescanl does not sound very useful, enable only if there is a
811-- compelling use case.
813-- | Like scanl' but does not stream the final value of the accumulator.
816{-# INLINE prescanl' #-}
817prescanl' :: (IsStream t, Monad m) => (b -> a -> b) -> b -> t m a -> t m b
818prescanl' step z m = fromStreamD $ D.prescanl' step z $ toStreamD m
820-- XXX this needs to be concurrent
821-- | Like prescanl' but with a monadic step function and a monadic seed.
824{-# INLINE prescanlM' #-}
825prescanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
826prescanlM' step z m = fromStreamD $ D.prescanlM' step z $ toStreamD m
828-- XXX this needs to be concurrent
829-- | Like 'scanl1'' but with a monadic step function.
832{-# INLINE scanl1M' #-}
833scanl1M' :: (IsStream t, Monad m) => (a -> a -> m a) -> t m a -> t m a
834scanl1M' step m = fromStreamD $ D.scanl1M' step $ toStreamD m
836-- | Like 'scanl'' but for a non-empty stream. The first element of the stream
837-- is used as the initial value of the accumulator. Does nothing if the stream
841-- >>> Stream.toList $ Stream.scanl1' (+) $ fromList [1,2,3,4]
847{-# INLINE scanl1' #-}
848scanl1' :: (IsStream t, Monad m) => (a -> a -> a) -> t m a -> t m a
849scanl1' step m = fromStreamD $ D.scanl1' step $ toStreamD m
851------------------------------------------------------------------------------
853------------------------------------------------------------------------------
855-- | Modify a @t m a -> t m a@ stream transformation that accepts a predicate
856-- @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a transformation @t m
857-- a -> t m (s, a)@. Convenient to filter with index or time.
860-- filterWithIndex = with indexed filter
861-- filterWithAbsTime = with timestamped filter
862-- filterWithRelTime = with timeIndexed filter
867with :: forall (t :: (Type -> Type) -> Type -> Type) m a b s. Functor (t m) =>
868 (t m a -> t m (s, a))
869 -> (((s, a) -> b) -> t m (s, a) -> t m (s, a))
870 -> (((s, a) -> b) -> t m a -> t m a)
871with f comb g = fmap snd . comb g . f
873-- | Include only those elements that pass a predicate.
877filter :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m a
878filter p m = fromStreamS $ S.filter p $ toStreamS m
880-- | Same as 'filter' but with a monadic predicate.
883{-# INLINE filterM #-}
884filterM :: (IsStream t, Monad m) => (a -> m Bool) -> t m a -> t m a
885filterM p m = fromStreamD $ D.filterM p $ toStreamD m
887-- | Drop repeated elements that are adjacent to each other using the supplied
888-- comparison function.
890-- @uniq = uniqBy (==)
892-- To strip duplicate path separators:
895-- f x y = x == '/' && x == y
896-- Stream.toList $ Stream.uniqBy f $ Stream.fromList "//a//b"
907uniqBy :: (IsStream t, Monad m, Functor (t m)) =>
908 (a -> a -> Bool) -> t m a -> t m a
909uniqBy eq = catMaybes . rollingMap f
916 Just x -> if x `eq` curr then Nothing else Just curr
918-- | Drop repeated elements that are adjacent to each other.
922uniq :: (Eq a, IsStream t, Monad m) => t m a -> t m a
923uniq = fromStreamD . D.uniq . toStreamD
925-- | Strip all leading and trailing occurrences of an element passing a
926-- predicate and make all other consecutive occurrences uniq.
929-- prune p = dropWhileAround p $ uniqBy (x y -> p x && p y)
933-- > Stream.prune isSpace (Stream.fromList " hello world! ")
943 -- (IsStream t, Monad m, Eq a) =>
944 (a -> Bool) -> t m a -> t m a
945prune = error "Not implemented yet!"
947-- Possible implementation:
949-- Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@
951-- 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a
954-- | Emit only repeated elements, once.
957repeated :: -- (IsStream t, Monad m, Eq a) =>
961-- We can have more efficient implementations for nubOrd and nubInt by using
962-- Set and IntSet to find/remove duplication. For Hashable we can use a
963-- hashmap. Use rewrite rules to specialize to more efficient impls.
965-- | Drop repeated elements anywhere in the stream.
967-- /Caution: not scalable for infinite streams/
969-- /See also: nubWindowBy/
974nubBy :: -- (IsStream t, Monad m) =>
975 (a -> a -> Bool) -> t m a -> t m a
976nubBy = undefined -- fromStreamD . D.nubBy . toStreamD
978-- | Drop repeated elements within the specified tumbling window in the stream.
980-- @nubBy = nubWindowBy maxBound@
984{-# INLINE nubWindowBy #-}
985nubWindowBy :: -- (IsStream t, Monad m) =>
986 Int -> (a -> a -> Bool) -> t m a -> t m a
987nubWindowBy = undefined -- fromStreamD . D.nubWithinBy . toStreamD
989-- | Deletes the first occurrence of the element in the stream that satisfies
990-- the given equality predicate.
993-- >>> Stream.toList $ Stream.deleteBy (==) 3 $ Stream.fromList [1,3,3,5]
999{-# INLINE deleteBy #-}
1000deleteBy :: (IsStream t, Monad m) => (a -> a -> Bool) -> a -> t m a -> t m a
1001deleteBy cmp x m = fromStreamS $ S.deleteBy cmp x (toStreamS m)
1003------------------------------------------------------------------------------
1005------------------------------------------------------------------------------
1007-- XXX We could use 'maxBuffer Block/Drop/Rotate/Sample' instead. However we
1008-- may want to have the evaluation rate independent of the sampling rate. To
1009-- support that we can decouple evaluation and sampling in independent stages.
1010-- The sampling stage would strictly evaluate and sample, the evaluation stage
1011-- would control the evaluation rate.
1013-- | Evaluate the input stream continuously and keep only the oldest @n@
1014-- elements in the buffer, discard the new ones when the buffer is full. When
1015-- the output stream is evaluated it consumes the values from the buffer in a
1020{-# INLINE sampleOld #-}
1021sampleOld :: -- (IsStream t, Monad m) =>
1022 Int -> t m a -> t m a
1023sampleOld = undefined
1025-- | Evaluate the input stream continuously and keep only the latest @n@
1026-- elements in a ring buffer, keep discarding the older ones to make space for
1027-- the new ones. When the output stream is evaluated it consumes the values
1028-- from the buffer in a FIFO manner.
1032{-# INLINE sampleNew #-}
1033sampleNew :: -- (IsStream t, Monad m) =>
1034 Int -> t m a -> t m a
1035sampleNew = undefined
1037-- | Like 'sampleNew' but samples at uniform intervals to match the consumer
1038-- rate. Note that 'sampleNew' leads to non-uniform sampling depending on the
1043{-# INLINE sampleRate #-}
1044sampleRate :: -- (IsStream t, Monad m) =>
1045 Double -> t m a -> t m a
1046sampleRate = undefined
1048------------------------------------------------------------------------------
1050------------------------------------------------------------------------------
1052-- | Same as 'takeWhile' but with a monadic predicate.
1055{-# INLINE takeWhileM #-}
1056takeWhileM :: (IsStream t, Monad m) => (a -> m Bool) -> t m a -> t m a
1057takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
1059-- See the lastN fold for impl hints. Use a Data.Array based ring buffer.
1061-- takeLast n = Stream.concatM . fmap Array.toStream . fold Fold.lastN
1063-- | Take @n@ elements at the end of the stream.
1065-- O(n) space, where n is the number elements taken.
1068{-# INLINE takeLast #-}
1069takeLast :: -- (IsStream t, Monad m) =>
1070 Int -> t m a -> t m a
1071takeLast = undefined -- fromStreamD $ D.takeLast n $ toStreamD m
1073-- | Take time interval @i@ seconds at the end of the stream.
1075-- O(n) space, where n is the number elements taken.
1078{-# INLINE takeLastInterval #-}
1079takeLastInterval :: -- (IsStream t, Monad m) =>
1080 Double -> t m a -> t m a
1081takeLastInterval = undefined -- fromStreamD $ D.takeLast n $ toStreamD m
1083-- | Take all consecutive elements at the end of the stream for which the
1084-- predicate is true.
1086-- O(n) space, where n is the number elements taken.
1089{-# INLINE takeWhileLast #-}
1090takeWhileLast :: -- (IsStream t, Monad m) =>
1091 (a -> Bool) -> t m a -> t m a
1092takeWhileLast = undefined -- fromStreamD $ D.takeWhileLast n $ toStreamD m
1094-- | Like 'takeWhile' and 'takeWhileLast' combined.
1096-- O(n) space, where n is the number elements taken from the end.
1099{-# INLINE takeWhileAround #-}
1100takeWhileAround :: -- (IsStream t, Monad m) =>
1101 (a -> Bool) -> t m a -> t m a
1102takeWhileAround = undefined -- fromStreamD $ D.takeWhileAround n $ toStreamD m
1104-- | @takeInterval duration@ yields stream elements upto specified time
1105-- @duration@. The duration starts when the stream is evaluated for the first
1106-- time, before the first element is yielded. The time duration is checked
1107-- before generating each element, if the duration has expired the stream
1110-- The total time taken in executing the stream is guaranteed to be /at least/
1111-- @duration@, however, because the duration is checked before generating an
1112-- element, the upper bound is indeterminate and depends on the time taken in
1113-- generating and processing the last element.
1115-- No element is yielded if the duration is zero. At least one element is
1116-- yielded if the duration is non-zero.
1120{-# INLINE takeInterval #-}
1121takeInterval ::(MonadIO m, IsStream t, TimeUnit64 d) => d -> t m a -> t m a
1122takeInterval d = fromStreamD . D.takeByTime d . toStreamD
1124-- | Drop elements in the stream as long as the predicate succeeds and then
1125-- take the rest of the stream.
1128{-# INLINE dropWhile #-}
1129dropWhile :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m a
1130dropWhile p m = fromStreamS $ S.dropWhile p $ toStreamS m
1132-- | Same as 'dropWhile' but with a monadic predicate.
1135{-# INLINE dropWhileM #-}
1136dropWhileM :: (IsStream t, Monad m) => (a -> m Bool) -> t m a -> t m a
1137dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
1139-- | @dropInterval duration@ drops stream elements until specified @duration@ has
1140-- passed. The duration begins when the stream is evaluated for the first
1141-- time. The time duration is checked /after/ generating a stream element, the
1142-- element is yielded if the duration has expired otherwise it is dropped.
1144-- The time elapsed before starting to generate the first element is /at most/
1145-- @duration@, however, because the duration expiry is checked after the
1146-- element is generated, the lower bound is indeterminate and depends on the
1147-- time taken in generating an element.
1149-- All elements are yielded if the duration is zero.
1153{-# INLINE dropInterval #-}
1154dropInterval ::(MonadIO m, IsStream t, TimeUnit64 d) => d -> t m a -> t m a
1155dropInterval d = fromStreamD . D.dropByTime d . toStreamD
1157-- | Drop @n@ elements at the end of the stream.
1159-- O(n) space, where n is the number elements dropped.
1162{-# INLINE dropLast #-}
1163dropLast :: -- (IsStream t, Monad m) =>
1164 Int -> t m a -> t m a
1165dropLast = undefined -- fromStreamD $ D.dropLast n $ toStreamD m
1167-- | Drop time interval @i@ seconds at the end of the stream.
1169-- O(n) space, where n is the number elements dropped.
1172{-# INLINE dropLastInterval #-}
1173dropLastInterval :: -- (IsStream t, Monad m) =>
1174 Int -> t m a -> t m a
1175dropLastInterval = undefined
1177-- | Drop all consecutive elements at the end of the stream for which the
1178-- predicate is true.
1180-- O(n) space, where n is the number elements dropped.
1183{-# INLINE dropWhileLast #-}
1184dropWhileLast :: -- (IsStream t, Monad m) =>
1185 (a -> Bool) -> t m a -> t m a
1186dropWhileLast = undefined -- fromStreamD $ D.dropWhileLast n $ toStreamD m
1188-- | Like 'dropWhile' and 'dropWhileLast' combined.
1190-- O(n) space, where n is the number elements dropped from the end.
1193{-# INLINE dropWhileAround #-}
1194dropWhileAround :: -- (IsStream t, Monad m) =>
1195 (a -> Bool) -> t m a -> t m a
1196dropWhileAround = undefined -- fromStreamD $ D.dropWhileAround n $ toStreamD m
1198------------------------------------------------------------------------------
1199-- Inserting Elements
1200------------------------------------------------------------------------------
1202-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
1203-- @stream@ that is less than @elem@ when compared using @cmp@.
1206-- insertBy cmp x = 'mergeBy' cmp ('fromPure' x)
1210-- >>> Stream.toList $ Stream.insertBy compare 2 $ Stream.fromList [1,3,5]
1216{-# INLINE insertBy #-}
1218 (IsStream t, Monad m) => (a -> a -> Ordering) -> a -> t m a -> t m a
1219insertBy cmp x m = fromStreamS $ S.insertBy cmp x (toStreamS m)
1221-- | Insert a pure value between successive elements of a stream.
1223-- >>> Stream.toList $ Stream.intersperse ',' $ Stream.fromList "hello"
1227{-# INLINE intersperse #-}
1228intersperse :: (IsStream t, MonadAsync m) => a -> t m a -> t m a
1229intersperse a = fromStreamS . S.intersperse a . toStreamS
1231-- | Insert a side effect before consuming an element of a stream except the
1234-- >>> Stream.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') $ Stream.fromList "hello"
1238{-# INLINE intersperseM_ #-}
1239intersperseM_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
1240intersperseM_ m = fromStreamD . D.intersperseM_ m . toStreamD
1242-- | Intersperse a monadic action into the input stream after every @n@
1246-- > Stream.toList $ Stream.intersperseBySpan 2 (return ',') $ Stream.fromList "hello"
1252{-# INLINE intersperseBySpan #-}
1253intersperseBySpan :: -- IsStream t =>
1254 Int -> m a -> t m a -> t m a
1255intersperseBySpan _n _f _xs = undefined
1257-- | Insert an effect and its output after consuming an element of a stream.
1259-- >>> Stream.toList $ Stream.trace putChar $ intersperseSuffix (putChar '.' >> return ',') $ Stream.fromList "hello"
1260-- h.,e.,l.,l.,o.,"h,e,l,l,o,"
1263{-# INLINE intersperseSuffix #-}
1264intersperseSuffix :: (IsStream t, Monad m) => m a -> t m a -> t m a
1265intersperseSuffix m = fromStreamD . D.intersperseSuffix m . toStreamD
1267-- | Insert a side effect after consuming an element of a stream.
1270-- >>> Stream.mapM_ putChar $ Stream.intersperseSuffix_ (threadDelay 1000000) $ Stream.fromList "hello"
1277{-# INLINE intersperseSuffix_ #-}
1278intersperseSuffix_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
1279intersperseSuffix_ m = fromStreamD . D.intersperseSuffix_ m . toStreamD
1281-- XXX Use an offset argument, like tapOffsetEvery
1283-- | Like 'intersperseSuffix' but intersperses an effectful action into the
1284-- input stream after every @n@ elements and after the last element.
1286-- >>> Stream.toList $ Stream.intersperseSuffixBySpan 2 (return ',') $ Stream.fromList "hello"
1291{-# INLINE intersperseSuffixBySpan #-}
1292intersperseSuffixBySpan :: (IsStream t, Monad m)
1293 => Int -> m a -> t m a -> t m a
1294intersperseSuffixBySpan n eff =
1295 fromStreamD . D.intersperseSuffixBySpan n eff . toStreamD
1297-- | Insert a side effect before consuming an element of a stream.
1299-- >>> Stream.toList $ Stream.trace putChar $ Stream.interspersePrefix_ (putChar '.' >> return ',') $ Stream.fromList "hello"
1302-- Same as 'trace_' but may be concurrent.
1308{-# INLINE interspersePrefix_ #-}
1309interspersePrefix_ :: (IsStream t, MonadAsync m) => m b -> t m a -> t m a
1310interspersePrefix_ m = mapM (\x -> void m >> return x)
1312------------------------------------------------------------------------------
1314------------------------------------------------------------------------------
1316-- Note: delay must be serial.
1318-- | Introduce a delay of specified seconds before consuming an element of the
1319-- stream except the first one.
1321-- >>> Stream.mapM_ print $ Stream.timestamped $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1322-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
1323-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
1324-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
1329delay :: (IsStream t, MonadIO m) => Double -> t m a -> t m a
1330delay n = intersperseM_ $ liftIO $ threadDelay $ round $ n * 1000000
1332-- Note: delay must be serial.
1334-- | Introduce a delay of specified seconds after consuming an element of a
1337-- >>> Stream.mapM_ print $ Stream.timestamped $ Stream.delayPost 1 $ Stream.enumerateFromTo 1 3
1338-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
1339-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
1340-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
1344{-# INLINE delayPost #-}
1345delayPost :: (IsStream t, MonadIO m) => Double -> t m a -> t m a
1346delayPost n = intersperseSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000
1348-- Note: delay must be serial, that's why 'trace_' is used.
1350-- | Introduce a delay of specified seconds before consuming an element of a
1353-- >>> Stream.mapM_ print $ Stream.timestamped $ Stream.delayPre 1 $ Stream.enumerateFromTo 1 3
1354-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
1355-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
1356-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
1360{-# INLINE delayPre #-}
1361delayPre :: (IsStream t, MonadIO m) => Double -> t m a -> t m a
1362delayPre n = trace_ $ liftIO $ threadDelay $ round $ n * 1000000
1364------------------------------------------------------------------------------
1365-- Reorder in sequence
1366------------------------------------------------------------------------------
1368-- | Buffer until the next element in sequence arrives. The function argument
1369-- determines the difference in sequence numbers. This could be useful in
1370-- implementing sequenced streams, for example, TCP reassembly.
1374{-# INLINE reassembleBy #-}
1376 :: -- (IsStream t, Monad m) =>
1381reassembleBy = undefined
1383------------------------------------------------------------------------------
1385------------------------------------------------------------------------------
1388-- > indexed = Stream.postscanl' (\(i, _) x -> (i + 1, x)) (-1,undefined)
1389-- > indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)
1391-- Pair each element in a stream with its index, starting from index 0.
1393-- >>> Stream.toList $ Stream.indexed $ Stream.fromList "hello"
1394-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]
1397{-# INLINE indexed #-}
1398indexed :: (IsStream t, Monad m) => t m a -> t m (Int, a)
1399indexed = fromStreamD . D.indexed . toStreamD
1402-- > indexedR n = Stream.postscanl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)
1403-- > indexedR n = Stream.zipWith (,) (Stream.enumerateFromThen n (n - 1))
1405-- Pair each element in a stream with its index, starting from the
1406-- given index @n@ and counting down.
1408-- >>> Stream.toList $ Stream.indexedR 10 $ Stream.fromList "hello"
1409-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]
1412{-# INLINE indexedR #-}
1413indexedR :: (IsStream t, Monad m) => Int -> t m a -> t m (Int, a)
1414indexedR n = fromStreamD . D.indexedR n . toStreamD
1416-------------------------------------------------------------------------------
1418-------------------------------------------------------------------------------
1420-- Note: The timestamp stream must be the second stream in the zip so that the
1421-- timestamp is generated after generating the stream element and not before.
1422-- If we do not do that then the following example will generate the same
1423-- timestamp for first two elements:
1425-- Stream.mapM_ print $ Stream.timestamped $ Stream.delay $ Stream.enumerateFromTo 1 3
1427-- | Pair each element in a stream with an absolute timestamp, using a clock of
1428-- specified granularity. The timestamp is generated just before the element
1431-- >>> Stream.mapM_ print $ Stream.timestampWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1432-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),1)
1433-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),2)
1434-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),3)
1438{-# INLINE timestampWith #-}
1439timestampWith :: (IsStream t, MonadAsync m, Functor (t m))
1440 => Double -> t m a -> t m (AbsTime, a)
1441timestampWith g stream = zipWith (flip (,)) stream (absTimesWith g)
1443-- TBD: check performance vs a custom implementation without using zipWith.
1447{-# INLINE timestamped #-}
1448timestamped :: (IsStream t, MonadAsync m, Functor (t m))
1449 => t m a -> t m (AbsTime, a)
1450timestamped = timestampWith 0.01
1452-- | Pair each element in a stream with relative times starting from 0, using a
1453-- clock with the specified granularity. The time is measured just before the
1454-- element is consumed.
1456-- >>> Stream.mapM_ print $ Stream.timeIndexWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1457-- (RelTime64 (NanoSecond64 ...),1)
1458-- (RelTime64 (NanoSecond64 ...),2)
1459-- (RelTime64 (NanoSecond64 ...),3)
1463{-# INLINE timeIndexWith #-}
1464timeIndexWith :: (IsStream t, MonadAsync m, Functor (t m))
1465 => Double -> t m a -> t m (RelTime64, a)
1466timeIndexWith g stream = zipWith (flip (,)) stream (relTimesWith g)
1468-- | Pair each element in a stream with relative times starting from 0, using a
1469-- 10 ms granularity clock. The time is measured just before the element is
1472-- >>> Stream.mapM_ print $ Stream.timeIndexed $ Stream.delay 1 $ Stream.enumerateFromTo 1 3
1473-- (RelTime64 (NanoSecond64 ...),1)
1474-- (RelTime64 (NanoSecond64 ...),2)
1475-- (RelTime64 (NanoSecond64 ...),3)
1479{-# INLINE timeIndexed #-}
1480timeIndexed :: (IsStream t, MonadAsync m, Functor (t m))
1481 => t m a -> t m (RelTime64, a)
1482timeIndexed = timeIndexWith 0.01
1484------------------------------------------------------------------------------
1486------------------------------------------------------------------------------
1488-- | Find all the indices where the value of the element in the stream is equal
1489-- to the given value.
1491-- > elemIndices a = findIndices (== a)
1494{-# INLINE elemIndices #-}
1495elemIndices :: (IsStream t, Eq a, Monad m) => a -> t m a -> t m Int
1496elemIndices a = findIndices (== a)
1498------------------------------------------------------------------------------
1500------------------------------------------------------------------------------
1502-- XXX this is not a one-to-one map so calling it map may not be right.
1503-- We can perhaps call it zipWithTail or rollWith.
1505-- | Apply a function on every two successive elements of a stream. The first
1506-- argument of the map function is the previous element and the second argument
1507-- is the current element. When the current element is the first element, the
1508-- previous element is 'Nothing'.
1512{-# INLINE rollingMap #-}
1513rollingMap :: (IsStream t, Monad m) => (Maybe a -> a -> b) -> t m a -> t m b
1514rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
1516-- | Like 'rollingMap' but with an effectful map function.
1520{-# INLINE rollingMapM #-}
1521rollingMapM :: (IsStream t, Monad m) => (Maybe a -> a -> m b) -> t m a -> t m b
1522rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
1524-- | Like 'rollingMap' but requires at least two elements in the stream,
1525-- returns an empty stream otherwise.
1527-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.
1531{-# INLINE rollingMap2 #-}
1532rollingMap2 :: (IsStream t, Monad m) => (a -> a -> b) -> t m a -> t m b
1533rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m
1535------------------------------------------------------------------------------
1537------------------------------------------------------------------------------
1539-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
1540-- elements, and return a stream of values extracted from 'Just'.
1545-- mapMaybe f = Stream.map 'fromJust' . Stream.filter 'isJust' . Stream.map f
1549{-# INLINE mapMaybe #-}
1550mapMaybe :: (IsStream t, Monad m) => (a -> Maybe b) -> t m a -> t m b
1551mapMaybe f m = fromStreamS $ S.mapMaybe f $ toStreamS m
1553-- | Like 'mapMaybe' but maps a monadic function.
1558-- mapMaybeM f = Stream.map 'fromJust' . Stream.filter 'isJust' . Stream.mapM f
1561-- /Concurrent (do not use with 'fromParallel' on infinite streams)/
1564{-# INLINE_EARLY mapMaybeM #-}
1565mapMaybeM :: (IsStream t, MonadAsync m, Functor (t m))
1566 => (a -> m (Maybe b)) -> t m a -> t m b
1567mapMaybeM f = fmap fromJust . filter isJust . mapM f
1569{-# RULES "mapMaybeM serial" mapMaybeM = mapMaybeMSerial #-}
1570{-# INLINE mapMaybeMSerial #-}
1571mapMaybeMSerial :: Monad m => (a -> m (Maybe b)) -> SerialT m a -> SerialT m b
1572mapMaybeMSerial f m = fromStreamD $ D.mapMaybeM f $ toStreamD m
1574-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
1578{-# INLINE catMaybes #-}
1579catMaybes :: (IsStream t, Monad m, Functor (t m)) => t m (Maybe a) -> t m a
1580catMaybes = fmap fromJust . filter isJust
1582------------------------------------------------------------------------------
1584------------------------------------------------------------------------------
1586-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
1591lefts :: (IsStream t, Monad m, Functor (t m)) => t m (Either a b) -> t m a
1592lefts = fmap (fromLeft undefined) . filter isLeft
1594-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
1598{-# INLINE rights #-}
1599rights :: (IsStream t, Monad m, Functor (t m)) => t m (Either a b) -> t m b
1600rights = fmap (fromRight undefined) . filter isRight
1602-- | Remove the either wrapper and flatten both lefts and as well as rights in
1603-- the output stream.
1608both :: Functor (t m) => t m (Either a a) -> t m a
1609both = fmap (either id id)
1611------------------------------------------------------------------------------
1612-- Concurrent Application
1613------------------------------------------------------------------------------
1615-- | Parallel transform application operator; applies a stream transformation
1616-- function @t m a -> t m b@ to a stream @t m a@ concurrently; the input stream
1617-- is evaluated asynchronously in an independent thread yielding elements to a
1618-- buffer and the transformation function runs in another thread consuming the
1619-- input from the buffer. '|$' is just like regular function application
1620-- operator '$' except that it is concurrent.
1622-- If you read the signature as @(t m a -> t m b) -> (t m a -> t m b)@ you can
1623-- look at it as a transformation that converts a transform function to a
1624-- buffered concurrent transform function.
1626-- The following code prints a value every second even though each stage adds a
1632-- Stream.mapM (\x -> threadDelay 1000000 >> print x)
1633-- |$ Stream.replicateM 3 (threadDelay 1000000 >> return 1)
1641-- /Since: 0.3.0 ("Streamly")/
1645(|$) :: (IsStream t, MonadAsync m) => (t m a -> t m b) -> (t m a -> t m b)
1646-- (|$) f = f . mkAsync
1647(|$) f = f . mkParallel
1655{-# INLINE applyAsync #-}
1656applyAsync :: (IsStream t, MonadAsync m)
1657 => (t m a -> t m b) -> (t m a -> t m b)
1660-- | Same as '|$' but with arguments reversed.
1666-- /Since: 0.3.0 ("Streamly")/
1670(|&) :: (IsStream t, MonadAsync m) => t m a -> (t m a -> t m b) -> t m b
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