1{-# OPTIONS_GHC -Wno-orphans #-}
4-- Module : Streamly.Internal.Data.Stream.IsStream.Common
5-- Copyright : (c) 2017 Composewell Technologies
6-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11-- Bottom level IsStream module that can be used by all other upper level
14module Streamly.Internal.Data.Stream.IsStream.Common
67import Control.Concurrent (threadDelay)
68import Control.Monad.IO.Class (MonadIO(..))
69import Foreign.Storable (Storable)
70import Streamly.Internal.Control.Concurrent (MonadAsync)
71import Streamly.Internal.Data.Array.Foreign.Type (Array)
72import Streamly.Internal.Data.Fold.Type (Fold (..))
73import Streamly.Internal.Data.Stream.IsStream.Combinators (maxYields)
74import Streamly.Internal.Data.Stream.IsStream.Type
75 (IsStream(..), fromStreamD, toStreamD, fromStreamS, toStreamS)
76import Streamly.Internal.Data.Stream.Serial (SerialT)
77import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
79import qualified Streamly.Internal.Data.Array.Foreign.Type as A
80import qualified Streamly.Internal.Data.Stream.Async as Async
81import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
82import qualified Streamly.Internal.Data.Stream.Parallel as Par
83import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
84import qualified Streamly.Internal.Data.Stream.StreamD as D
85#ifdef USE_STREAMK_ONLY
86import qualified Streamly.Internal.Data.Stream.StreamK as S
88import qualified Streamly.Internal.Data.Stream.StreamD as S
90import Streamly.Internal.System.IO (defaultChunkSize)
92import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
97-- >>> import Control.Concurrent (threadDelay)
98-- >>> import Control.Monad (join)
99-- >>> import Control.Monad.Trans.Class (lift)
100-- >>> import Data.Function (fix, (&))
101-- >>> import Data.Semigroup (cycle1)
102-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
103-- >>> import System.IO.Unsafe (unsafePerformIO)
104-- >>> import qualified Streamly.Prelude as Stream
105-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
106-- >>> import qualified Streamly.Data.Array.Foreign as Array
107-- >>> import qualified Streamly.Data.Fold as Fold
108-- >>> import qualified Streamly.Internal.Data.Fold as Fold
109-- >>> import qualified Streamly.Data.Unfold as Unfold
111------------------------------------------------------------------------------
113------------------------------------------------------------------------------
115-- Faster than fromEffect because there is no bind.
119-- fromPure a = a \`cons` nil
122-- Create a singleton stream from a pure value.
124-- The following holds in monadic streams, but not in Zip streams:
128-- fromPure = fromEffect . pure
131-- In Zip applicative streams 'fromPure' is not the same as 'pure' because in that
132-- case 'pure' is equivalent to 'repeat' instead. 'fromPure' and 'pure' are
133-- equally efficient, in other cases 'fromPure' may be slightly more efficient
134-- than the other equivalent definitions.
136-- /Since: 0.8.0 (Renamed yield to fromPure)/
138{-# INLINE fromPure #-}
139fromPure :: IsStream t => a -> t m a
140fromPure = fromStream . K.fromPure
142-- | Same as 'fromPure'
145{-# DEPRECATED yield "Please use fromPure instead." #-}
147yield :: IsStream t => a -> t m a
152-- fromEffect m = m \`consM` nil
155-- Create a singleton stream from a monadic action.
158-- > Stream.toList $ Stream.fromEffect getLine
163-- /Since: 0.8.0 (Renamed yieldM to fromEffect)/
165{-# INLINE fromEffect #-}
166fromEffect :: (Monad m, IsStream t) => m a -> t m a
167fromEffect = fromStream . K.fromEffect
169-- | Same as 'fromEffect'
172{-# DEPRECATED yieldM "Please use fromEffect instead." #-}
174yieldM :: (Monad m, IsStream t) => m a -> t m a
177-- >>> repeatM = fix . consM
178-- >>> repeatM = cycle1 . fromEffect
180-- Generate a stream by repeatedly executing a monadic action forever.
184-- Stream.repeatM (threadDelay 1000000 >> print 1)
190-- /Concurrent, infinite (do not use with 'fromParallel')/
193{-# INLINE_EARLY repeatM #-}
194repeatM :: (IsStream t, MonadAsync m) => m a -> t m a
195repeatM = K.repeatMWith IsStream.consM
197{-# RULES "repeatM serial" repeatM = repeatMSerial #-}
198{-# INLINE repeatMSerial #-}
199repeatMSerial :: MonadAsync m => m a -> SerialT m a
200repeatMSerial = fromStreamS . S.repeatM
202------------------------------------------------------------------------------
203-- Generation - Time related
204------------------------------------------------------------------------------
206-- | @timesWith g@ returns a stream of time value tuples. The first component
207-- of the tuple is an absolute time reference (epoch) denoting the start of the
208-- stream and the second component is a time relative to the reference.
210-- The argument @g@ specifies the granularity of the relative time in seconds.
211-- A lower granularity clock gives higher precision but is more expensive in
212-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
214-- >>> import Control.Concurrent (threadDelay)
215-- >>> import Streamly.Internal.Data.Stream.IsStream.Common as Stream (timesWith)
216-- >>> Stream.mapM_ (\x -> print x >> threadDelay 1000000) $ Stream.take 3 $ Stream.timesWith 0.01
217-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
218-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
219-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
221-- Note: This API is not safe on 32-bit machines.
225{-# INLINE timesWith #-}
226timesWith :: (IsStream t, MonadAsync m) => Double -> t m (AbsTime, RelTime64)
227timesWith g = fromStreamD $ D.times g
229-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
230-- granularity @g@ specified in seconds. A low granularity clock is more
231-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
234-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ absTimesWith 0.01
235-- AbsTime (TimeSpec {sec = ..., nsec = ...})
236-- AbsTime (TimeSpec {sec = ..., nsec = ...})
237-- AbsTime (TimeSpec {sec = ..., nsec = ...})
239-- Note: This API is not safe on 32-bit machines.
243{-# INLINE absTimesWith #-}
244absTimesWith :: (IsStream t, MonadAsync m, Functor (t m))
245 => Double -> t m AbsTime
246absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
248-- | @relTimesWith g@ returns a stream of relative time values starting from 0,
249-- using a clock of granularity @g@ specified in seconds. A low granularity
250-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
251-- ms is treated as 1 ms.
253-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
254-- RelTime64 (NanoSecond64 ...)
255-- RelTime64 (NanoSecond64 ...)
256-- RelTime64 (NanoSecond64 ...)
258-- Note: This API is not safe on 32-bit machines.
262{-# INLINE relTimesWith #-}
263relTimesWith :: (IsStream t, MonadAsync m, Functor (t m))
264 => Double -> t m RelTime64
265relTimesWith = fmap snd . timesWith
267------------------------------------------------------------------------------
268-- Elimination - Running a Fold
269------------------------------------------------------------------------------
271-- | We can create higher order folds using 'foldOn'. We can fold a number of
272-- streams to a given fold efficiently with full stream fusion. For example, to
273-- fold a list of streams on the same sum fold:
275-- >>> concatFold = Prelude.foldl Stream.foldOn Fold.sum
277-- >>> fold f = Fold.finish . Stream.foldOn f
281foldOn :: Monad m => Fold m a b -> SerialT m a -> Fold m a b
282foldOn f s = D.foldOn f $ IsStream.toStreamD s
284-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
285-- expression strictly at each step. The behavior is similar to 'foldl''. A
286-- 'Fold' can terminate early without consuming the full stream. See the
287-- documentation of individual 'Fold's for termination behavior.
289-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
292-- Folds never fail, therefore, they produce a default value even when no input
293-- is provided. It means we can always fold an empty stream and get a valid
294-- result. For example:
296-- >>> Stream.fold Fold.sum Stream.nil
299-- However, 'foldMany' on an empty stream results in an empty stream.
300-- Therefore, @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany
303-- @fold f = Stream.parse (Parser.fromFold f)@
307fold :: Monad m => Fold m a b -> SerialT m a -> m b
309 (b, _) <- fold_ fl strm
313fold_ :: Monad m => Fold m a b -> SerialT m a -> m (b, SerialT m a)
315 (b, str) <- D.fold_ fl $ IsStream.toStreamD strm
316 return $! (b, IsStream.fromStreamD str)
318------------------------------------------------------------------------------
320------------------------------------------------------------------------------
330-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
336map :: (IsStream t, Monad m) => (a -> b) -> t m a -> t m b
337map f = fromStreamD . D.map f . toStreamD
339-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
340-- that it provides an additional @done@ function to be applied on the
341-- accumulator when the stream stops. The result of @done@ is also emitted in
344-- This function can be used to allocate a resource in the beginning of the
345-- scan and release it when the stream ends or to flush the internal state of
346-- the scan at the end.
350{-# INLINE scanlMAfter' #-}
351scanlMAfter' :: (IsStream t, Monad m)
352 => (b -> a -> m b) -> m b -> (b -> m b) -> t m a -> t m b
353scanlMAfter' step initial done stream =
354 fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream
356-- XXX this needs to be concurrent
357-- | Like 'postscanl'' but with a monadic step function and a monadic seed.
359-- >>> postscanlM' f z xs = Stream.drop 1 $ Stream.scanlM' f z xs
363-- /Since: 0.8.0 (signature change)/
364{-# INLINE postscanlM' #-}
365postscanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
366postscanlM' step z m = fromStreamD $ D.postscanlM' step z $ toStreamD m
370-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
371-- a read-only environment. However, that would just be 'mapM'.
373-- The initial action could be @m (s, Maybe b)@, and we can also add a final
374-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
375-- functionality and also to flush the state in the end like scanlMAfter'.
376-- We can also use it along with a fusible version of bracket to get
377-- scanlMAfter' like functionality. See issue #677.
379-- This can be further generalized to a type similar to Fold/Parser, giving it
380-- filtering and parsing capability as well (this is in fact equivalent to
383-- smapM :: (s -> a -> m (Step s b)) -> m s -> t m a -> t m b
386-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
387-- Hopefully, this is a better alternative to @scan@. Separation of state from
388-- the output makes it easier to think in terms of a shared state, and also
389-- makes it easier to keep the state fully strict and the output lazy.
391-- See also: 'scanlM''
396smapM :: (IsStream t, Monad m) =>
401smapM step initial stream =
402 -- XXX implement this directly instead of using scanlM'
403 -- Once we have postscanlM' with monadic initial we can use this code
404 -- let r = postscanlM'
405 -- (\(s, _) a -> step s a)
406 -- (fmap (,undefined) initial)
409 (\(s, _) a -> step s a)
410 (fmap (,undefined) initial)
414------------------------------------------------------------------------------
415-- Transformation - Trimming
416------------------------------------------------------------------------------
418-- | Take first 'n' elements from the stream and discard the rest.
422take :: (IsStream t, Monad m) => Int -> t m a -> t m a
423take n m = fromStreamS $ S.take n $ toStreamS
424 (maxYields (Just (fromIntegral n)) m)
426-- | End the stream as soon as the predicate fails on an element.
429{-# INLINE takeWhile #-}
430takeWhile :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m a
431takeWhile p m = fromStreamS $ S.takeWhile p $ toStreamS m
433-- | Discard first 'n' elements from the stream and take the rest.
437drop :: (IsStream t, Monad m) => Int -> t m a -> t m a
438drop n m = fromStreamS $ S.drop n $ toStreamS m
440------------------------------------------------------------------------------
442------------------------------------------------------------------------------
444-- | Find all the indices where the element in the stream satisfies the given
447-- > findIndices = fold Fold.findIndices
450{-# INLINE findIndices #-}
451findIndices :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m Int
452findIndices p m = fromStreamS $ S.findIndices p (toStreamS m)
454------------------------------------------------------------------------------
455-- Transformation by Inserting
456------------------------------------------------------------------------------
458-- intersperseM = intersperseBySpan 1
460-- | Insert an effect and its output before consuming an element of a stream
461-- except the first one.
463-- >>> Stream.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.fromList "hello"
464-- h.,e.,l.,l.,o"h,e,l,l,o"
467{-# INLINE intersperseM #-}
468intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
469intersperseM m = fromStreamS . S.intersperseM m . toStreamS
471-- | Intersperse a monadic action into the input stream after every @n@
475-- > import Control.Concurrent (threadDelay)
476-- > Stream.drain $ Stream.interjectSuffix 1 (putChar ',') $ Stream.mapM (\x -> threadDelay 1000000 >> putChar x) $ Stream.fromList "hello"
481{-# INLINE interjectSuffix #-}
483 :: (IsStream t, MonadAsync m)
484 => Double -> m a -> t m a -> t m a
485interjectSuffix n f xs = xs `parallelFst` repeatM timed
486 where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
488------------------------------------------------------------------------------
489-- Transformation by Reordering
490------------------------------------------------------------------------------
492-- XXX Use a compact region list to temporarily store the list, in both reverse
493-- as well as in reverse'.
495-- /Note:/ 'reverse'' is much faster than this, use that when performance
498-- | Returns the elements of the stream in reverse order. The stream must be
499-- finite. Note that this necessarily buffers the entire stream in memory.
501-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
503-- /Since 0.7.0 (Monad m constraint)/
506{-# INLINE reverse #-}
507reverse :: (IsStream t, Monad m) => t m a -> t m a
508reverse s = fromStreamS $ S.reverse $ toStreamS s
510-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
513{-# INLINE reverse' #-}
514reverse' :: (IsStream t, MonadIO m, Storable a) => t m a -> t m a
515-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
518 . A.flattenArraysRev -- unfoldMany A.readRev
522 . A.arraysOf defaultChunkSize
525------------------------------------------------------------------------------
526-- Concurrent Transformations and Combining
527------------------------------------------------------------------------------
529-- | Make the stream producer and consumer run concurrently by introducing a
530-- buffer between them. The producer thread evaluates the input stream until
531-- the buffer fills, it terminates if the buffer is full and a worker thread is
532-- kicked off again to evaluate the remaining stream when there is space in the
533-- buffer. The consumer consumes the stream lazily from the buffer.
535-- /Since: 0.2.0 (Streamly)/
539{-# INLINE_NORMAL mkAsync #-}
540mkAsync :: (IsStream t, MonadAsync m) => t m a -> t m a
541mkAsync = fromStreamD . Async.mkAsyncD . toStreamD
543-- Compare with mkAsync. mkAsync uses an Async style SVar whereas this uses a
544-- parallel style SVar for evaluation. Currently, parallel style cannot use
545-- rate control whereas Async style can use rate control. In async style SVar
546-- the worker thread terminates when the buffer is full whereas in Parallel
549-- | Make the stream producer and consumer run concurrently by introducing a
550-- buffer between them. The producer thread evaluates the input stream until
551-- the buffer fills, it blocks if the buffer is full until there is space in
552-- the buffer. The consumer consumes the stream lazily from the buffer.
554-- @mkParallel = IsStream.fromStreamD . mkParallelD . IsStream.toStreamD@
558{-# INLINE_NORMAL mkParallel #-}
559mkParallel :: (IsStream t, MonadAsync m) => t m a -> t m a
560mkParallel = fromStreamD . Par.mkParallelD . toStreamD
562-- | Like `parallel` but stops the output as soon as the first stream stops.
565{-# INLINE parallelFst #-}
566parallelFst :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
567parallelFst m1 m2 = fromStream $ Par.parallelFstK (toStream m1) (toStream m2)
569------------------------------------------------------------------------------
570-- Combine streams and flatten
571------------------------------------------------------------------------------
573-- | Map a stream producing monadic function on each element of the stream
574-- and then flatten the results into a single stream. Since the stream
575-- generation function is monadic, unlike 'concatMap', it can produce an
576-- effect at the beginning of each iteration of the inner loop.
579{-# INLINE concatMapM #-}
580concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
581concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
583-- | Map a stream producing function on each element of the stream and then
584-- flatten the results into a single stream.
586-- >>> concatMap f = Stream.concatMapM (return . f)
587-- >>> concatMap f = Stream.concatMapWith Stream.serial f
588-- >>> concatMap f = Stream.concat . Stream.map f
589-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
592{-# INLINE concatMap #-}
593concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
594concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
596-- | Given a stream value in the underlying monad, lift and join the underlying
597-- monad with the stream monad.
599-- >>> concatM = Stream.concat . Stream.fromEffect
600-- >>> concatM = Stream.concat . lift -- requires (MonadTrans t)
601-- >>> concatM = join . lift -- requires (MonadTrans t, Monad (t m))
603-- See also: 'concat', 'sequence'
607{-# INLINE concatM #-}
608concatM :: (IsStream t, Monad m) => m (t m a) -> t m a
609concatM generator = concatMapM (\() -> generator) (fromPure ())
611------------------------------------------------------------------------------
612-- Split stream and fold
613------------------------------------------------------------------------------
616-- > intercalate . splitOnSeq == id
617-- prop> (\f -> unsafePerformIO . Stream.toList . f . Stream.fromList) (Stream.intercalate Unfold.fromList " " . Stream.splitOnSeq (Array.fromList " ") Fold.toList) xs == xs
619-- | Like 'splitOn' but the separator is a sequence of elements instead of a
622-- For illustration, let's define a function that operates on pure lists:
624-- >>> splitOnSeq' pat xs = Stream.toList $ Stream.splitOnSeq (Array.fromList pat) Fold.toList (Stream.fromList xs)
626-- >>> splitOnSeq' "" "hello"
627-- ["h","e","l","l","o"]
629-- >>> splitOnSeq' "hello" ""
632-- >>> splitOnSeq' "hello" "hello"
635-- >>> splitOnSeq' "x" "hello"
638-- >>> splitOnSeq' "h" "hello"
641-- >>> splitOnSeq' "o" "hello"
644-- >>> splitOnSeq' "e" "hello"
647-- >>> splitOnSeq' "l" "hello"
650-- >>> splitOnSeq' "ll" "hello"
653-- 'splitOnSeq' is an inverse of 'intercalate'. The following law always holds:
655-- > intercalate . splitOnSeq == id
657-- The following law holds when the separator is non-empty and contains none of
658-- the elements present in the input lists:
660-- > splitOnSeq . intercalate == id
664-- XXX We can use a polymorphic vector implemented by Array# to represent the
665-- sequence, that way we can avoid the Storable constraint. If we still need
666-- Storable Array for performance, we can use a separate splitOnArray API for
667-- that. We can also have an API where the sequence itself is a lazy stream, so
668-- that we can search files in files for example.
669{-# INLINE splitOnSeq #-}
671 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
672 => Array a -> Fold m a b -> t m a -> t m b
674 IsStream.fromStreamD $ D.splitOnSeq patt f (IsStream.toStreamD m)
676------------------------------------------------------------------------------
678------------------------------------------------------------------------------
680-- | Like 'zipWith' but using a monadic zipping function.
683{-# INLINABLE zipWithM #-}
684zipWithM :: (IsStream t, Monad m) => (a -> b -> m c) -> t m a -> t m b -> t m c
687 $ S.zipWithM f (IsStream.toStreamS m1) (IsStream.toStreamS m2)
689-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
690-- elements @a@ and @b@ are then zipped using the supplied zip function and the
691-- result @c@ is yielded to the consumer.
693-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
694-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
697-- > S.toList $ S.zipWith (+) (S.fromList [1,2,3]) (S.fromList [4,5,6])
702{-# INLINABLE zipWith #-}
703zipWith :: (IsStream t, Monad m) => (a -> b -> c) -> t m a -> t m b -> t m c
706 $ S.zipWith f (IsStream.toStreamS m1) (IsStream.toStreamS m2)
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