on 4e604ef help sign in
725 lines 25 kB history raw
1{-# OPTIONS_GHC -Wno-orphans #-}
3-- |
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
9-- Portability : GHC
10--
11-- Bottom level IsStream module that can be used by all other upper level
12-- IsStream modules.
14module Streamly.Internal.Data.Stream.IsStream.Common
15 (
16 -- * Generation
17 fromPure
18 , fromEffect
19 , repeatM
20 , timesWith
21 , absTimesWith
22 , relTimesWith
24 -- * Elimination
25 , foldOn
26 , fold
27 , fold_
29 -- * Transformation
30 , map
31 , scanlMAfter'
32 , postscanlMAfter'
33 , postscanlM'
34 , smapM
35 -- $smapM_Notes
36 , take
37 , takeWhile
38 , takeEndBy
39 , drop
40 , findIndices
41 , intersperseM
42 , interjectSuffix
43 , reverse
44 , reverse'
46 -- * Concurrent
47 , mkAsync
48 , mkParallel
49 , parallelFst
51 -- * Nesting
52 , concatM
53 , concatMapM
54 , concatMap
55 , splitOnSeq
57 -- * Zipping
58 , zipWithM
59 , zipWith
61 -- * Deprecated
62 , yield
63 , yieldM
64 )
65where
67#include "inline.hs"
69import Control.Concurrent (threadDelay)
70import Control.Monad.IO.Class (MonadIO(..))
71import Foreign.Storable (Storable)
72import Streamly.Internal.Control.Concurrent (MonadAsync)
73import Streamly.Internal.Data.Array.Foreign.Type (Array)
74import Streamly.Internal.Data.Fold.Type (Fold (..))
75import Streamly.Internal.Data.Stream.IsStream.Combinators (maxYields)
76import Streamly.Internal.Data.Stream.IsStream.Type
77 (IsStream(..), fromStreamD, toStreamD, fromStreamS, toStreamS)
78import Streamly.Internal.Data.Stream.Serial (SerialT)
79import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
81import qualified Streamly.Internal.Data.Array.Foreign.Type as A
82import qualified Streamly.Internal.Data.Stream.Async as Async
83import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
84import qualified Streamly.Internal.Data.Stream.Parallel as Par
85import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
86import qualified Streamly.Internal.Data.Stream.StreamD as D
87#ifdef USE_STREAMK_ONLY
88import qualified Streamly.Internal.Data.Stream.StreamK as S
89#else
90import qualified Streamly.Internal.Data.Stream.StreamD as S
91#endif
92import Streamly.Internal.System.IO (defaultChunkSize)
94import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)
96--
97-- $setup
98-- >>> :m
99-- >>> import Control.Concurrent (threadDelay)
100-- >>> import Control.Monad (join)
101-- >>> import Control.Monad.Trans.Class (lift)
102-- >>> import Data.Function (fix, (&))
103-- >>> import Data.Semigroup (cycle1)
104-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
105-- >>> import System.IO.Unsafe (unsafePerformIO)
106-- >>> import qualified Streamly.Prelude as Stream
107-- >>> import Streamly.Internal.Data.Stream.IsStream as Stream
108-- >>> import qualified Streamly.Data.Array.Foreign as Array
109-- >>> import qualified Streamly.Data.Fold as Fold
110-- >>> import qualified Streamly.Internal.Data.Fold as Fold
111-- >>> import qualified Streamly.Data.Unfold as Unfold
113------------------------------------------------------------------------------
114-- Generation
115------------------------------------------------------------------------------
117-- Faster than fromEffect because there is no bind.
118--
119-- |
120-- @
121-- fromPure a = a \`cons` nil
122-- @
123--
124-- Create a singleton stream from a pure value.
125--
126-- The following holds in monadic streams, but not in Zip streams:
127--
128-- @
129-- fromPure = pure
130-- fromPure = fromEffect . pure
131-- @
132--
133-- In Zip applicative streams 'fromPure' is not the same as 'pure' because in that
134-- case 'pure' is equivalent to 'repeat' instead. 'fromPure' and 'pure' are
135-- equally efficient, in other cases 'fromPure' may be slightly more efficient
136-- than the other equivalent definitions.
137--
138-- /Since: 0.8.0 (Renamed yield to fromPure)/
139--
140{-# INLINE fromPure #-}
141fromPure :: IsStream t => a -> t m a
142fromPure = fromStream . K.fromPure
144-- | Same as 'fromPure'
145--
146-- @since 0.4.0
147{-# DEPRECATED yield "Please use fromPure instead." #-}
148{-# INLINE yield #-}
149yield :: IsStream t => a -> t m a
150yield = fromPure
152-- |
153-- @
154-- fromEffect m = m \`consM` nil
155-- @
156--
157-- Create a singleton stream from a monadic action.
158--
159-- @
160-- > Stream.toList $ Stream.fromEffect getLine
161-- hello
162-- ["hello"]
163-- @
164--
165-- /Since: 0.8.0 (Renamed yieldM to fromEffect)/
166--
167{-# INLINE fromEffect #-}
168fromEffect :: (Monad m, IsStream t) => m a -> t m a
169fromEffect = fromStream . K.fromEffect
171-- | Same as 'fromEffect'
172--
173-- @since 0.4.0
174{-# DEPRECATED yieldM "Please use fromEffect instead." #-}
175{-# INLINE yieldM #-}
176yieldM :: (Monad m, IsStream t) => m a -> t m a
177yieldM = fromEffect
178-- |
179-- >>> repeatM = fix . consM
180-- >>> repeatM = cycle1 . fromEffect
181--
182-- Generate a stream by repeatedly executing a monadic action forever.
183--
184-- >>> :{
185-- repeatAsync =
186-- Stream.repeatM (threadDelay 1000000 >> print 1)
187-- & Stream.take 10
188-- & Stream.fromAsync
189-- & Stream.drain
190-- :}
191--
192-- /Concurrent, infinite (do not use with 'fromParallel')/
193--
194-- @since 0.2.0
195{-# INLINE_EARLY repeatM #-}
196repeatM :: (IsStream t, MonadAsync m) => m a -> t m a
197repeatM = K.repeatMWith IsStream.consM
199{-# RULES "repeatM serial" repeatM = repeatMSerial #-}
200{-# INLINE repeatMSerial #-}
201repeatMSerial :: MonadAsync m => m a -> SerialT m a
202repeatMSerial = fromStreamS . S.repeatM
204------------------------------------------------------------------------------
205-- Generation - Time related
206------------------------------------------------------------------------------
208-- | @timesWith g@ returns a stream of time value tuples. The first component
209-- of the tuple is an absolute time reference (epoch) denoting the start of the
210-- stream and the second component is a time relative to the reference.
211--
212-- The argument @g@ specifies the granularity of the relative time in seconds.
213-- A lower granularity clock gives higher precision but is more expensive in
214-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
215--
216-- >>> import Control.Concurrent (threadDelay)
217-- >>> import Streamly.Internal.Data.Stream.IsStream.Common as Stream (timesWith)
218-- >>> Stream.mapM_ (\x -> print x >> threadDelay 1000000) $ Stream.take 3 $ Stream.timesWith 0.01
219-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
220-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
221-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
222--
223-- Note: This API is not safe on 32-bit machines.
224--
225-- /Pre-release/
226--
227{-# INLINE timesWith #-}
228timesWith :: (IsStream t, MonadAsync m) => Double -> t m (AbsTime, RelTime64)
229timesWith g = fromStreamD $ D.times g
231-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of
232-- granularity @g@ specified in seconds. A low granularity clock is more
233-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
234-- as 1 ms.
235--
236-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ absTimesWith 0.01
237-- AbsTime (TimeSpec {sec = ..., nsec = ...})
238-- AbsTime (TimeSpec {sec = ..., nsec = ...})
239-- AbsTime (TimeSpec {sec = ..., nsec = ...})
240--
241-- Note: This API is not safe on 32-bit machines.
242--
243-- /Pre-release/
244--
245{-# INLINE absTimesWith #-}
246absTimesWith :: (IsStream t, MonadAsync m, Functor (t m))
247 => Double -> t m AbsTime
248absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
250-- | @relTimesWith g@ returns a stream of relative time values starting from 0,
251-- using a clock of granularity @g@ specified in seconds. A low granularity
252-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
253-- ms is treated as 1 ms.
254--
255-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
256-- RelTime64 (NanoSecond64 ...)
257-- RelTime64 (NanoSecond64 ...)
258-- RelTime64 (NanoSecond64 ...)
259--
260-- Note: This API is not safe on 32-bit machines.
261--
262-- /Pre-release/
263--
264{-# INLINE relTimesWith #-}
265relTimesWith :: (IsStream t, MonadAsync m, Functor (t m))
266 => Double -> t m RelTime64
267relTimesWith = fmap snd . timesWith
269------------------------------------------------------------------------------
270-- Elimination - Running a Fold
271------------------------------------------------------------------------------
273-- | We can create higher order folds using 'foldOn'. We can fold a number of
274-- streams to a given fold efficiently with full stream fusion. For example, to
275-- fold a list of streams on the same sum fold:
276--
277-- >>> concatFold = Prelude.foldl Stream.foldOn Fold.sum
278--
279-- >>> fold f = Fold.finish . Stream.foldOn f
280--
281-- /Internal/
282{-# INLINE foldOn #-}
283foldOn :: Monad m => Fold m a b -> SerialT m a -> Fold m a b
284foldOn f s = D.foldOn f $ IsStream.toStreamD s
286-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
287-- expression strictly at each step. The behavior is similar to 'foldl''. A
288-- 'Fold' can terminate early without consuming the full stream. See the
289-- documentation of individual 'Fold's for termination behavior.
290--
291-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
292-- 5050
293--
294-- Folds never fail, therefore, they produce a default value even when no input
295-- is provided. It means we can always fold an empty stream and get a valid
296-- result. For example:
297--
298-- >>> Stream.fold Fold.sum Stream.nil
299-- 0
300--
301-- However, 'foldMany' on an empty stream results in an empty stream.
302-- Therefore, @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany
303-- f@.
304--
305-- @fold f = Stream.parse (Parser.fromFold f)@
306--
307-- @since 0.7.0
308{-# INLINE fold #-}
309fold :: Monad m => Fold m a b -> SerialT m a -> m b
310fold fl strm = do
311 (b, _) <- fold_ fl strm
312 return $! b
314{-# INLINE fold_ #-}
315fold_ :: Monad m => Fold m a b -> SerialT m a -> m (b, SerialT m a)
316fold_ fl strm = do
317 (b, str) <- D.fold_ fl $ IsStream.toStreamD strm
318 return $! (b, IsStream.fromStreamD str)
320------------------------------------------------------------------------------
321-- Transformation
322------------------------------------------------------------------------------
324-- |
325-- @
326-- map = fmap
327-- @
328--
329-- Same as 'fmap'.
330--
331-- @
332-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
333-- [2,3,4]
334-- @
335--
336-- @since 0.4.0
337{-# INLINE map #-}
338map :: (IsStream t, Monad m) => (a -> b) -> t m a -> t m b
339map f = fromStreamD . D.map f . toStreamD
341-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
342-- that it provides an additional @done@ function to be applied on the
343-- accumulator when the stream stops. The result of @done@ is also emitted in
344-- the stream.
345--
346-- This function can be used to allocate a resource in the beginning of the
347-- scan and release it when the stream ends or to flush the internal state of
348-- the scan at the end.
349--
350-- /Pre-release/
351--
352{-# INLINE scanlMAfter' #-}
353scanlMAfter' :: (IsStream t, Monad m)
354 => (b -> a -> m b) -> m b -> (b -> m b) -> t m a -> t m b
355scanlMAfter' step initial done stream =
356 fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream
358{-# INLINE postscanlMAfter' #-}
359postscanlMAfter' :: (IsStream t, Monad m)
360 => (b -> a -> m b) -> m b -> (b -> m b) -> t m a -> t m b
361postscanlMAfter' step initial done stream =
362 fromStreamD $ D.postscanlMAfter' step initial done $ toStreamD stream
364-- XXX this needs to be concurrent
365-- | Like 'postscanl'' but with a monadic step function and a monadic seed.
366--
367-- >>> postscanlM' f z xs = Stream.drop 1 $ Stream.scanlM' f z xs
368--
369-- /Since: 0.7.0/
370--
371-- /Since: 0.8.0 (signature change)/
372{-# INLINE postscanlM' #-}
373postscanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> m b -> t m a -> t m b
374postscanlM' step z m = fromStreamD $ D.postscanlM' step z $ toStreamD m
376-- $smapM_Notes
377--
378-- The stateful step function can be simplified to @(s -> a -> m b)@ to provide
379-- a read-only environment. However, that would just be 'mapM'.
380--
381-- The initial action could be @m (s, Maybe b)@, and we can also add a final
382-- action @s -> m (Maybe b)@. This can be used to get pre/post scan like
383-- functionality and also to flush the state in the end like scanlMAfter'.
384-- We can also use it along with a fusible version of bracket to get
385-- scanlMAfter' like functionality. See issue #677.
386--
387-- This can be further generalized to a type similar to Fold/Parser, giving it
388-- filtering and parsing capability as well (this is in fact equivalent to
389-- parseMany):
390--
391-- smapM :: (s -> a -> m (Step s b)) -> m s -> t m a -> t m b
392--
394-- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.
395-- Hopefully, this is a better alternative to @scan@. Separation of state from
396-- the output makes it easier to think in terms of a shared state, and also
397-- makes it easier to keep the state fully strict and the output lazy.
398--
399-- See also: 'scanlM''
400--
401-- /Pre-release/
402--
403{-# INLINE smapM #-}
404smapM :: (IsStream t, Monad m) =>
405 (s -> a -> m (s, b))
406 -> m s
407 -> t m a
408 -> t m b
409smapM step initial stream =
410 -- XXX implement this directly instead of using scanlM'
411 -- Once we have postscanlM' with monadic initial we can use this code
412 -- let r = postscanlM'
413 -- (\(s, _) a -> step s a)
414 -- (fmap (,undefined) initial)
415 -- stream
416 let r = postscanlM'
417 (\(s, _) a -> step s a)
418 (fmap (,undefined) initial)
419 stream
420 in map snd r
422------------------------------------------------------------------------------
423-- Transformation - Trimming
424------------------------------------------------------------------------------
426-- | Take first 'n' elements from the stream and discard the rest.
427--
428-- @since 0.1.0
429{-# INLINE take #-}
430take :: (IsStream t, Monad m) => Int -> t m a -> t m a
431take n m = fromStreamS $ S.take n $ toStreamS
432 (maxYields (Just (fromIntegral n)) m)
434-- | End the stream as soon as the predicate fails on an element.
435--
436-- @since 0.1.0
437{-# INLINE takeWhile #-}
438takeWhile :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m a
439takeWhile p m = fromStreamS $ S.takeWhile p $ toStreamS m
441{-# INLINE takeEndBy #-}
442takeEndBy :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m a
443takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
445-- | Discard first 'n' elements from the stream and take the rest.
446--
447-- @since 0.1.0
448{-# INLINE drop #-}
449drop :: (IsStream t, Monad m) => Int -> t m a -> t m a
450drop n m = fromStreamS $ S.drop n $ toStreamS m
452------------------------------------------------------------------------------
453-- Searching
454------------------------------------------------------------------------------
456-- | Find all the indices where the element in the stream satisfies the given
457-- predicate.
458--
459-- > findIndices = fold Fold.findIndices
460--
461-- @since 0.5.0
462{-# INLINE findIndices #-}
463findIndices :: (IsStream t, Monad m) => (a -> Bool) -> t m a -> t m Int
464findIndices p m = fromStreamS $ S.findIndices p (toStreamS m)
466------------------------------------------------------------------------------
467-- Transformation by Inserting
468------------------------------------------------------------------------------
470-- intersperseM = intersperseBySpan 1
472-- | Insert an effect and its output before consuming an element of a stream
473-- except the first one.
474--
475-- >>> Stream.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.fromList "hello"
476-- h.,e.,l.,l.,o"h,e,l,l,o"
477--
478-- Be careful about the order of effects. In the above example we used trace
479-- after the intersperse, if we use it before the intersperse the output would
480-- be he.l.l.o."h,e,l,l,o".
481--
482-- >>> Stream.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar $ Stream.fromList "hello"
483-- he.l.l.o."h,e,l,l,o"
484--
485-- @since 0.5.0
486{-# INLINE intersperseM #-}
487intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
488intersperseM m = fromStreamS . S.intersperseM m . toStreamS
490-- | Intersperse a monadic action into the input stream after every @n@
491-- seconds.
492--
493-- @
494-- > import Control.Concurrent (threadDelay)
495-- > Stream.drain $ Stream.interjectSuffix 1 (putChar ',') $ Stream.mapM (\x -> threadDelay 1000000 >> putChar x) $ Stream.fromList "hello"
496-- h,e,l,l,o
497-- @
498--
499-- /Pre-release/
500{-# INLINE interjectSuffix #-}
501interjectSuffix
502 :: (IsStream t, MonadAsync m)
503 => Double -> m a -> t m a -> t m a
504interjectSuffix n f xs = xs `parallelFst` repeatM timed
505 where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
507------------------------------------------------------------------------------
508-- Transformation by Reordering
509------------------------------------------------------------------------------
511-- XXX Use a compact region list to temporarily store the list, in both reverse
512-- as well as in reverse'.
513--
514-- /Note:/ 'reverse'' is much faster than this, use that when performance
515-- matters.
516--
517-- | Returns the elements of the stream in reverse order. The stream must be
518-- finite. Note that this necessarily buffers the entire stream in memory.
519--
520-- >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil
521--
522-- /Since 0.7.0 (Monad m constraint)/
523--
524-- /Since: 0.1.1/
525{-# INLINE reverse #-}
526reverse :: (IsStream t, Monad m) => t m a -> t m a
527reverse s = fromStreamS $ S.reverse $ toStreamS s
529-- | Like 'reverse' but several times faster, requires a 'Storable' instance.
530--
531-- /Pre-release/
532{-# INLINE reverse' #-}
533reverse' :: (IsStream t, MonadIO m, Storable a) => t m a -> t m a
534-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
535reverse' =
536 fromStreamD
537 . A.flattenArraysRev -- unfoldMany A.readRev
538 . D.fromStreamK
539 . K.reverse
540 . D.toStreamK
541 . A.arraysOf defaultChunkSize
542 . toStreamD
544------------------------------------------------------------------------------
545-- Concurrent Transformations and Combining
546------------------------------------------------------------------------------
548-- | Make the stream producer and consumer run concurrently by introducing a
549-- buffer between them. The producer thread evaluates the input stream until
550-- the buffer fills, it terminates if the buffer is full and a worker thread is
551-- kicked off again to evaluate the remaining stream when there is space in the
552-- buffer. The consumer consumes the stream lazily from the buffer.
553--
554-- /Since: 0.2.0 (Streamly)/
555--
556-- @since 0.8.0
557--
558{-# INLINE_NORMAL mkAsync #-}
559mkAsync :: (IsStream t, MonadAsync m) => t m a -> t m a
560mkAsync = fromStreamD . Async.mkAsyncD . toStreamD
562-- Compare with mkAsync. mkAsync uses an Async style SVar whereas this uses a
563-- parallel style SVar for evaluation. Currently, parallel style cannot use
564-- rate control whereas Async style can use rate control. In async style SVar
565-- the worker thread terminates when the buffer is full whereas in Parallel
566-- style it blocks.
567--
568-- | Make the stream producer and consumer run concurrently by introducing a
569-- buffer between them. The producer thread evaluates the input stream until
570-- the buffer fills, it blocks if the buffer is full until there is space in
571-- the buffer. The consumer consumes the stream lazily from the buffer.
572--
573-- @mkParallel = IsStream.fromStreamD . mkParallelD . IsStream.toStreamD@
574--
575-- /Pre-release/
576--
577{-# INLINE_NORMAL mkParallel #-}
578mkParallel :: (IsStream t, MonadAsync m) => t m a -> t m a
579mkParallel = fromStreamD . Par.mkParallelD . toStreamD
581-- | Like `parallel` but stops the output as soon as the first stream stops.
582--
583-- /Pre-release/
584{-# INLINE parallelFst #-}
585parallelFst :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
586parallelFst m1 m2 = fromStream $ Par.parallelFstK (toStream m1) (toStream m2)
588------------------------------------------------------------------------------
589-- Combine streams and flatten
590------------------------------------------------------------------------------
592-- | Map a stream producing monadic function on each element of the stream
593-- and then flatten the results into a single stream. Since the stream
594-- generation function is monadic, unlike 'concatMap', it can produce an
595-- effect at the beginning of each iteration of the inner loop.
596--
597-- @since 0.6.0
598{-# INLINE concatMapM #-}
599concatMapM :: (IsStream t, Monad m) => (a -> m (t m b)) -> t m a -> t m b
600concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
602-- | Map a stream producing function on each element of the stream and then
603-- flatten the results into a single stream.
604--
605-- >>> concatMap f = Stream.concatMapM (return . f)
606-- >>> concatMap f = Stream.concatMapWith Stream.serial f
607-- >>> concatMap f = Stream.concat . Stream.map f
608-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)
609--
610-- @since 0.6.0
611{-# INLINE concatMap #-}
612concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
613concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
615-- | Given a stream value in the underlying monad, lift and join the underlying
616-- monad with the stream monad.
617--
618-- >>> concatM = Stream.concat . Stream.fromEffect
619-- >>> concatM = Stream.concat . lift -- requires (MonadTrans t)
620-- >>> concatM = join . lift -- requires (MonadTrans t, Monad (t m))
621--
622-- See also: 'concat', 'sequence'
623--
624-- /Internal/
625--
626{-# INLINE concatM #-}
627concatM :: (IsStream t, Monad m) => m (t m a) -> t m a
628concatM generator = concatMapM (\() -> generator) (fromPure ())
630------------------------------------------------------------------------------
631-- Split stream and fold
632------------------------------------------------------------------------------
634-- $
635-- > intercalate . splitOnSeq == id
636-- prop> (\f -> unsafePerformIO . Stream.toList . f . Stream.fromList) (Stream.intercalate Unfold.fromList " " . Stream.splitOnSeq (Array.fromList " ") Fold.toList) xs == xs
638-- | Like 'splitOn' but the separator is a sequence of elements instead of a
639-- single element.
640--
641-- For illustration, let's define a function that operates on pure lists:
642--
643-- >>> splitOnSeq' pat xs = Stream.toList $ Stream.splitOnSeq (Array.fromList pat) Fold.toList (Stream.fromList xs)
644--
645-- >>> splitOnSeq' "" "hello"
646-- ["h","e","l","l","o"]
647--
648-- >>> splitOnSeq' "hello" ""
649-- [""]
650--
651-- >>> splitOnSeq' "hello" "hello"
652-- ["",""]
653--
654-- >>> splitOnSeq' "x" "hello"
655-- ["hello"]
656--
657-- >>> splitOnSeq' "h" "hello"
658-- ["","ello"]
659--
660-- >>> splitOnSeq' "o" "hello"
661-- ["hell",""]
662--
663-- >>> splitOnSeq' "e" "hello"
664-- ["h","llo"]
665--
666-- >>> splitOnSeq' "l" "hello"
667-- ["he","","o"]
668--
669-- >>> splitOnSeq' "ll" "hello"
670-- ["he","o"]
671--
672-- 'splitOnSeq' is an inverse of 'intercalate'. The following law always holds:
673--
674-- > intercalate . splitOnSeq == id
675--
676-- The following law holds when the separator is non-empty and contains none of
677-- the elements present in the input lists:
678--
679-- > splitOnSeq . intercalate == id
680--
681-- /Pre-release/
683-- XXX We can use a polymorphic vector implemented by Array# to represent the
684-- sequence, that way we can avoid the Storable constraint. If we still need
685-- Storable Array for performance, we can use a separate splitOnArray API for
686-- that. We can also have an API where the sequence itself is a lazy stream, so
687-- that we can search files in files for example.
688{-# INLINE splitOnSeq #-}
689splitOnSeq
690 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
691 => Array a -> Fold m a b -> t m a -> t m b
692splitOnSeq patt f m =
693 IsStream.fromStreamD $ D.splitOnSeq patt f (IsStream.toStreamD m)
695------------------------------------------------------------------------------
696-- Zipping
697------------------------------------------------------------------------------
699-- | Like 'zipWith' but using a monadic zipping function.
700--
701-- @since 0.4.0
702{-# INLINE zipWithM #-}
703zipWithM :: (IsStream t, Monad m) => (a -> b -> m c) -> t m a -> t m b -> t m c
704zipWithM f m1 m2 =
705 IsStream.fromStreamS
706 $ S.zipWithM f (IsStream.toStreamS m1) (IsStream.toStreamS m2)
708-- | Stream @a@ is evaluated first, followed by stream @b@, the resulting
709-- elements @a@ and @b@ are then zipped using the supplied zip function and the
710-- result @c@ is yielded to the consumer.
711--
712-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
713-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
714--
715-- @
716-- > S.toList $ S.zipWith (+) (S.fromList [1,2,3]) (S.fromList [4,5,6])
717-- [5,7,9]
718-- @
719--
720-- @since 0.1.0
721{-# INLINE zipWith #-}
722zipWith :: (IsStream t, Monad m) => (a -> b -> c) -> t m a -> t m b -> t m c
723zipWith f m1 m2 =
724 IsStream.fromStreamS
725 $ S.zipWith f (IsStream.toStreamS m1) (IsStream.toStreamS m2)