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