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
52import Control.Concurrent (threadDelay)
53import Control.Monad.IO.Class (MonadIO(..))
54import Foreign.Storable (Storable)
55import Streamly.Internal.Data.Array.Foreign.Type (Array)
56import Streamly.Internal.Data.Fold.Type (Fold (..))
57import Streamly.Internal.Data.Stream.IsStream.Combinators (maxYields)
58import Streamly.Internal.Data.Stream.Prelude (fromStreamS, toStreamS)
59import Streamly.Internal.Data.Stream.Serial (SerialT)
60import Streamly.Internal.Data.Stream.StreamD.Type (fromStreamD, toStreamD)
61import Streamly.Internal.Data.Stream.StreamK.Type (IsStream())
62import Streamly.Internal.Data.SVar (MonadAsync)
63import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
65import qualified Streamly.Internal.Data.Stream.Parallel as Par
66import qualified Streamly.Internal.Data.Stream.Serial as Serial
67import qualified Streamly.Internal.Data.Stream.StreamK as K (repeatM)
68import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
69import qualified Streamly.Internal.Data.Stream.StreamD as D
70#ifdef USE_STREAMK_ONLY
71import qualified Streamly.Internal.Data.Stream.StreamK as S
73import qualified Streamly.Internal.Data.Stream.StreamD as S
76import Prelude hiding (take, takeWhile, drop, reverse, concatMap)
81-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
82-- >>> import qualified Streamly.Prelude as Stream
83-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
84-- >>> import qualified Streamly.Data.Fold as Fold
85-- >>> import qualified Streamly.Data.Array.Foreign as Array
87------------------------------------------------------------------------------
89------------------------------------------------------------------------------
91-- Faster than yieldM because there is no bind.
95-- yield a = a \`cons` nil
98-- Create a singleton stream from a pure value.
100-- The following holds in monadic streams, but not in Zip streams:
104-- yield = yieldM . pure
107-- In Zip applicative streams 'yield' is not the same as 'pure' because in that
108-- case 'pure' is equivalent to 'repeat' instead. 'yield' and 'pure' are
109-- equally efficient, in other cases 'yield' may be slightly more efficient
110-- than the other equivalent definitions.
114yield :: IsStream t => a -> t m a
119-- yieldM m = m \`consM` nil
122-- Create a singleton stream from a monadic action.
125-- > Stream.toList $ Stream.yieldM getLine
132yieldM :: (Monad m, IsStream t) => m a -> t m a
137-- repeatM = fix . consM
138-- repeatM = cycle1 . yieldM
141-- Generate a stream by repeatedly executing a monadic action forever.
144-- drain $ fromSerial $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
145-- drain $ fromAsync $ S.take 10 $ S.repeatM $ (threadDelay 1000000 >> print 1)
148-- /Concurrent, infinite (do not use with 'fromParallel')/
151{-# INLINE_EARLY repeatM #-}
152repeatM :: (IsStream t, MonadAsync m) => m a -> t m a
155{-# RULES "repeatM serial" repeatM = repeatMSerial #-}
156{-# INLINE repeatMSerial #-}
157repeatMSerial :: MonadAsync m => m a -> SerialT m a
158repeatMSerial = fromStreamS . S.repeatM
160------------------------------------------------------------------------------
161-- Generation - Time related
162------------------------------------------------------------------------------
164-- | @timesWith g@ returns a stream of time value tuples. The first component
165-- of the tuple is an absolute time reference (epoch) denoting the start of the
166-- stream and the second component is a time relative to the reference.
168-- The argument @g@ specifies the granularity of the relative time in seconds.
169-- A lower granularity clock gives higher precision but is more expensive in
170-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
172-- >>> import Control.Concurrent (threadDelay)
173-- >>> import Streamly.Internal.Data.Stream.IsStream.Common as Stream (timesWith)
174-- >>> Stream.mapM_ (\x -> print x >> threadDelay 1000000) $ Stream.take 3 $ Stream.timesWith 0.01
175-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
176-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
177-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
179-- Note: This API is not safe on 32-bit machines.
183{-# INLINE timesWith #-}
184timesWith :: (IsStream t, MonadAsync m) => Double -> t m (AbsTime, RelTime64)
185timesWith g = fromStreamD $ D.times g
187-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
188-- granularity @g@ specified in seconds. A low granularity clock is more
189-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
192-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ absTimesWith 0.01
193-- AbsTime (TimeSpec {sec = ..., nsec = ...})
194-- AbsTime (TimeSpec {sec = ..., nsec = ...})
195-- AbsTime (TimeSpec {sec = ..., nsec = ...})
197-- Note: This API is not safe on 32-bit machines.
201{-# INLINE absTimesWith #-}
202absTimesWith :: (IsStream t, MonadAsync m, Functor (t m))
203 => Double -> t m AbsTime
204absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
206-- | @relTimesWith g@ returns a stream of relative time values starting from 0,
207-- using a clock of granularity @g@ specified in seconds. A low granularity
208-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
209-- ms is treated as 1 ms.
211-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
212-- RelTime64 (NanoSecond64 ...)
213-- RelTime64 (NanoSecond64 ...)
214-- RelTime64 (NanoSecond64 ...)
216-- Note: This API is not safe on 32-bit machines.
220{-# INLINE relTimesWith #-}
221relTimesWith :: (IsStream t, MonadAsync m, Functor (t m))
222 => Double -> t m RelTime64
223relTimesWith = fmap snd . timesWith
225------------------------------------------------------------------------------
226-- Elimination - Running a Fold
227------------------------------------------------------------------------------
229-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
230-- expression strictly at each step. The behavior is similar to 'foldl''. A
231-- 'Fold' can terminate early without consuming the full stream. See the
232-- documentation of individual 'Fold's for termination behavior.
234-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
237-- Folds never fail, therefore, they produce a default value even when no input
238-- is provided. It means we can always fold an empty stream and get a valid
239-- result. For example:
241-- >>> Stream.fold Fold.sum Stream.nil
244-- However, 'foldMany' on an empty stream results in an empty stream.
245-- Therefore, @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany
248-- @fold f = Stream.parse (Parser.fromFold f)@
252fold :: Monad m => Fold m a b -> SerialT m a -> m b
254 (b, _) <- fold_ fl strm
258fold_ :: Monad m => Fold m a b -> SerialT m a -> m (b, SerialT m a)
260 (b, str) <- D.fold_ fl $ D.toStreamD strm
261 return $! (b, D.fromStreamD str)
263------------------------------------------------------------------------------
265------------------------------------------------------------------------------
267-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
268-- that it provides an additional @done@ function to be applied on the
269-- accumulator when the stream stops. The result of @done@ is also emitted in
272-- This function can be used to allocate a resource in the beginning of the
273-- scan and release it when the stream ends or to flush the internal state of
274-- the scan at the end.
278{-# INLINE scanlMAfter' #-}
279scanlMAfter' :: (IsStream t, Monad m)
280 => (b -> a -> m b) -> m b -> (b -> m b) -> t m a -> t m b
281scanlMAfter' step initial done stream =
282 fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream
284-- XXX this needs to be concurrent
285-- | Like 'postscanl'' but with a monadic step function and a monadic seed.
289-- /Since: 0.8.0 (signature change)/
290{-# INLINE postscanlM' #-}
291postscanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
292postscanlM' step z m = fromStreamD $ D.postscanlM' step z $ toStreamD m
296-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
297-- a read-only environment. However, that would just be 'mapM'.
299-- The initial action could be @m (s, Maybe b)@, and we can also add a final
300-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
301-- functionality and also to flush the state in the end like scanlMAfter'.
302-- We can also use it along with a fusible version of bracket to get
303-- scanlMAfter' like functionality. See issue #677.
305-- This can be further generalized to a type similar to Fold/Parser, giving it
306-- filtering and parsing capability as well (this is in fact equivalent to
309-- smapM :: (s -> a -> m (Step s b)) -> m s -> t m a -> t m b
312-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
313-- Hopefully, this is a better alternative to @scan@. Separation of state from
314-- the output makes it easier to think in terms of a shared state, and also
315-- makes it easier to keep the state fully strict and the output lazy.
317-- See also: 'scanlM''
322smapM :: (IsStream t, Monad m) =>
327smapM step initial stream =
328 -- XXX implement this directly instead of using scanlM'
329 -- Once we have postscanlM' with monadic initial we can use this code
330 -- let r = postscanlM'
331 -- (\(s, _) a -> step s a)
332 -- (fmap (,undefined) initial)
335 (\(s, _) a -> step s a)
336 (fmap (,undefined) initial)
340------------------------------------------------------------------------------
341-- Transformation - Trimming
342------------------------------------------------------------------------------
344-- | Take first 'n' elements from the stream and discard the rest.
348take :: (IsStream t, Monad m) => Int -> t m a -> t m a
349take n m = fromStreamS $ S.take n $ toStreamS
350 (maxYields (Just (fromIntegral n)) m)
352-- | End the stream as soon as the predicate fails on an element.
355{-# INLINE takeWhile #-}
356takeWhile :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m a
357takeWhile p m = fromStreamS $ S.takeWhile p $ toStreamS m
359-- | Discard first 'n' elements from the stream and take the rest.
363drop :: (IsStream t, Monad m) => Int -> t m a -> t m a
364drop n m = fromStreamS $ S.drop n $ toStreamS m
366------------------------------------------------------------------------------
368------------------------------------------------------------------------------
370-- | Find all the indices where the element in the stream satisfies the given
373-- > findIndices = fold Fold.findIndices
376{-# INLINE findIndices #-}
377findIndices :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m Int
378findIndices p m = fromStreamS $ S.findIndices p (toStreamS m)
380------------------------------------------------------------------------------
381-- Transformation by Inserting
382------------------------------------------------------------------------------
384-- intersperseM = intersperseBySpan 1
386-- | Insert an effect and its output before consuming an element of a stream
387-- except the first one.
389-- >>> Stream.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.fromList "hello"
390-- h.,e.,l.,l.,o"h,e,l,l,o"
393{-# INLINE intersperseM #-}
394intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
395intersperseM m = fromStreamS . S.intersperseM m . toStreamS
397-- | Intersperse a monadic action into the input stream after every @n@
401-- > import Control.Concurrent (threadDelay)
402-- > Stream.drain $ Stream.interjectSuffix 1 (putChar ',') $ Stream.mapM (\x -> threadDelay 1000000 >> putChar x) $ Stream.fromList "hello"
407{-# INLINE interjectSuffix #-}
409 :: (IsStream t, MonadAsync m)
410 => Double -> m a -> t m a -> t m a
411interjectSuffix n f xs = xs `Par.parallelFst` repeatM timed
412 where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
414------------------------------------------------------------------------------
415-- Transformation by Reordering
416------------------------------------------------------------------------------
418-- XXX Use a compact region list to temporarily store the list, in both reverse
419-- as well as in reverse'.
421-- /Note:/ 'reverse'' is much faster than this, use that when performance
424-- > reverse = S.foldlT (flip S.cons) S.nil
426-- | Returns the elements of the stream in reverse order. The stream must be
427-- finite. Note that this necessarily buffers the entire stream in memory.
429-- /Since 0.7.0 (Monad m constraint)/
432{-# INLINE reverse #-}
433reverse :: (IsStream t, Monad m) => t m a -> t m a
434reverse s = fromStreamS $ S.reverse $ toStreamS s
436-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
439{-# INLINE reverse' #-}
440reverse' :: (IsStream t, MonadIO m, Storable a) => t m a -> t m a
441reverse' s = fromStreamD $ D.reverse' $ toStreamD s
443------------------------------------------------------------------------------
444-- Combine streams and flatten
445------------------------------------------------------------------------------
447-- | Map a stream producing monadic function on each element of the stream
448-- and then flatten the results into a single stream. Since the stream
449-- generation function is monadic, unlike 'concatMap', it can produce an
450-- effect at the beginning of each iteration of the inner loop.
453{-# INLINE concatMapM #-}
454concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
455concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
457-- | Map a stream producing function on each element of the stream and then
458-- flatten the results into a single stream.
461-- concatMap f = 'concatMapM' (return . f)
462-- concatMap = 'concatMapWith' 'Serial.serial'
463-- concatMap f = 'concat . map f'
464-- concatMap f = 'unfoldMany' (UF.lmap f UF.fromStream)
468{-# INLINE concatMap #-}
469concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
470concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
472-- | Given a stream value in the underlying monad, lift and join the underlying
473-- monad with the stream monad.
476-- concatM = concat . yieldM
477-- concatM = concat . lift -- requires @(MonadTrans t)@
478-- concatM = join . lift -- requires @(MonadTrans t@, @Monad (t m))@
481-- See also: 'concat', 'sequence'
485{-# INLINE concatM #-}
486concatM :: (IsStream t, Monad m) => m (t m a) -> t m a
487concatM generator = concatMapM (\() -> generator) (yield ())
489------------------------------------------------------------------------------
490-- Split stream and fold
491------------------------------------------------------------------------------
493-- | Like 'splitOn' but the separator is a sequence of elements instead of a
496-- For illustration, let's define a function that operates on pure lists:
498-- >>> splitOnSeq' pat xs = Stream.toList $ Stream.splitOnSeq (Array.fromList pat) Fold.toList (Stream.fromList xs)
500-- >>> splitOnSeq' "" "hello"
501-- ["h","e","l","l","o"]
503-- >>> splitOnSeq' "hello" ""
506-- >>> splitOnSeq' "hello" "hello"
509-- >>> splitOnSeq' "x" "hello"
512-- >>> splitOnSeq' "h" "hello"
515-- >>> splitOnSeq' "o" "hello"
518-- >>> splitOnSeq' "e" "hello"
521-- >>> splitOnSeq' "l" "hello"
524-- >>> splitOnSeq' "ll" "hello"
527-- 'splitOnSeq' is an inverse of 'intercalate'. The following law always holds:
529-- > intercalate . splitOn == id
531-- The following law holds when the separator is non-empty and contains none of
532-- the elements present in the input lists:
534-- > splitOn . intercalate == id
538-- XXX We can use a polymorphic vector implemented by Array# to represent the
539-- sequence, that way we can avoid the Storable constraint. If we still need
540-- Storable Array for performance, we can use a separate splitOnArray API for
541-- that. We can also have an API where the sequence itself is a lazy stream, so
542-- that we can search files in files for example.
543{-# INLINE splitOnSeq #-}
545 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
546 => Array a -> Fold m a b -> t m a -> t m b
547splitOnSeq patt f m = D.fromStreamD $ D.splitOnSeq patt f (D.toStreamD m)
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