on master help sign in
Add unfoldWords/interpose/intercalate etc.

* Rearrange, update benchmarks for words/unwords, lines/unlines * Add interpose/intercalate/interleaveInfix/interleaveSuffix to Prelude * Add unfoldWords to Data.String Use the newly added rotuines to write words/unwords and lines/unlines more idiomatically and with improved performance.

commita9f3f9a0fb6c40903ed1560f2f619f3c27f1545c authorHarendra Kumar <harendra.kumar@gmail.com> dateTue 15 Oct 2019, 00:34 (7 years ago) parentfbec11f
9 files changed+644−97
benchmark/FileIO.hs+20−8 src/Streamly/Benchmark/FileIO/Array.hs+5−6 src/Streamly/Benchmark/FileIO/Stream.hs+56−16 src/Streamly/Data/String.hs+12−8 src/Streamly/Internal/Data/Unfold.hs+10−2 src/Streamly/Internal/Memory/ArrayStream.hs+11−3 src/Streamly/Internal/Prelude.hs+192−41 src/Streamly/Streams/StreamD.hs+329−12 src/Streamly/Streams/StreamK.hs+9−1
benchmark/FileIO.hs+20−8
@@ -201,20 +201,32 @@ main = do
201201 Handles inh _ <- readIORef href
202202 BFS.chunksOf 1000 inh
203203 ]
204− , bgroup "group-ungroup"
205− [ mkBench "lines-unlines" href $ do
204+ , bgroup "group-ungroup-stream"
205+ [ mkBench "lines-unlines-[Char]" href $ do
206206 Handles inh outh <- readIORef href
207207 BFS.linesUnlinesCopy inh outh
208− , mkBench "lines-unlines-arrays" href $ do
208+ , mkBench "lines-unlines-Word8Array" href $ do
209209 Handles inh outh <- readIORef href
210− BFA.linesUnlinesCopy inh outh
211− , mkBench "words-unwords" href $ do
210+ BFS.linesUnlinesArrayWord8Copy inh outh
211+ , mkBench "lines-unlines-CharArray" href $ do
212212 Handles inh outh <- readIORef href
213− BFS.wordsUnwordsCopy inh outh
214− , mkBench "words-unwords-word8" href $ do
213+ BFS.linesUnlinesArrayCharCopy inh outh
214+ , mkBench "words-unwords-[Word8]" href $ do
215215 Handles inh outh <- readIORef href
216216 BFS.wordsUnwordsCopyWord8 inh outh
217− , mkBench "words-unwords-arrays" href $ do
217+ , mkBench "words-unwords-[Char]" href $ do
218+ Handles inh outh <- readIORef href
219+ BFS.wordsUnwordsCopy inh outh
220+ , mkBench "words-unwords-CharArray" href $ do
221+ Handles inh outh <- readIORef href
222+ BFS.wordsUnwordsCharArrayCopy inh outh
223+ ]
224+
225+ , bgroup "group-ungroup-array-stream"
226+ [ mkBench "lines-unlines-Word8Array" href $ do
227+ Handles inh outh <- readIORef href
228+ BFA.linesUnlinesCopy inh outh
229+ , mkBench "words-unwords-Word8Array" href $ do
218230 Handles inh outh <- readIORef href
219231 BFA.wordsUnwordsCopy inh outh
220232 ]
src/Streamly/Benchmark/FileIO/Array.hs+5−6
@@ -48,7 +48,6 @@ import qualified Streamly.Prelude as S
4848import qualified Streamly.Data.String as SS
4949
5050import qualified Streamly.Internal.FileSystem.Handle as IFH
51−import qualified Streamly.Internal.Prelude as Internal
5251import qualified Streamly.Internal.Memory.Array as IA
5352import qualified Streamly.Internal.Memory.ArrayStream as AS
5453import qualified Streamly.Internal.Data.Unfold as IUF
@@ -186,8 +185,8 @@ inspect $ 'copy `hasNoType` ''Step
186185{-# INLINE linesUnlinesCopy #-}
187186linesUnlinesCopy :: Handle -> Handle -> IO ()
188187linesUnlinesCopy inh outh =
189− S.fold (IFH.writeArraysInChunksOf (1024*1024) outh)
190− $ Internal.insertAfterEach (return $ A.fromList [10])
188+ S.fold (IFH.writeInChunksOf (1024*1024) outh)
189+ $ AS.interposeSuffix 10
191190 $ AS.splitOnSuffix 10
192191 $ IFH.toStreamArraysOf (1024*1024) inh
193192
@@ -200,9 +199,9 @@ inspect $ hasNoTypeClassesExcept 'linesUnlinesCopy [''Storable]
200199{-# INLINE wordsUnwordsCopy #-}
201200wordsUnwordsCopy :: Handle -> Handle -> IO ()
202201wordsUnwordsCopy inh outh =
203− S.fold (IFH.writeArraysInChunksOf (1024*1024) outh)
204− $ S.intersperse (A.fromList [32])
205− -- XXX use a word splitting combinator
202+ S.fold (IFH.writeInChunksOf (1024*1024) outh)
203+ $ AS.interpose 32
204+ -- XXX this is not correct word splitting combinator
206205 $ AS.splitOn 32
207206 $ IFH.toStreamArraysOf (1024*1024) inh
208207
src/Streamly/Benchmark/FileIO/Stream.hs+56−16
@@ -40,8 +40,12 @@ module Streamly.Benchmark.FileIO.Stream
4040 , catFinallyStream
4141 , copy
4242 , linesUnlinesCopy
43+ , linesUnlinesArrayWord8Copy
44+ , linesUnlinesArrayCharCopy
45+ -- , linesUnlinesArrayUtf8Copy
4346 , wordsUnwordsCopyWord8
4447 , wordsUnwordsCopy
48+ , wordsUnwordsCharArrayCopy
4549 , readWord8
4650 , decodeChar8
4751 , copyCodecChar8
@@ -68,9 +72,11 @@ import Prelude hiding (last, length)
6872import qualified Streamly.FileSystem.Handle as FH
6973import qualified Streamly.Internal.FileSystem.Handle as IFH
7074import qualified Streamly.Memory.Array as A
75+-- import qualified Streamly.Internal.Memory.Array as IA
7176import qualified Streamly.Internal.Memory.Array.Types as AT
7277import qualified Streamly.Prelude as S
7378import qualified Streamly.Data.Fold as FL
79+-- import qualified Streamly.Internal.Data.Fold as IFL
7480import qualified Streamly.Data.String as SS
7581import qualified Streamly.Internal.Data.Unfold as IUF
7682import qualified Streamly.Internal.Prelude as IP
@@ -400,11 +406,29 @@ inspect $ 'chunksOfD `hasNoType` ''AT.FlattenState
400406inspect $ 'chunksOfD `hasNoType` ''D.ConcatMapUState
401407#endif
402408
403−-- XXX splitSuffixOn requires -funfolding-use-threshold=150 for better fusion
404−-- | Lines and unlines
405409{-# INLINE linesUnlinesCopy #-}
406410linesUnlinesCopy :: Handle -> Handle -> IO ()
407411linesUnlinesCopy inh outh =
412+ S.fold (FH.write outh)
413+ $ SS.encodeChar8
414+ $ SS.unfoldLines IUF.fromList
415+ $ S.splitOnSuffix (== '\n') FL.toList
416+ $ SS.decodeChar8
417+ $ S.unfold FH.read inh
418+
419+{-# INLINE linesUnlinesArrayWord8Copy #-}
420+linesUnlinesArrayWord8Copy :: Handle -> Handle -> IO ()
421+linesUnlinesArrayWord8Copy inh outh =
422+ S.fold (FH.write outh)
423+ $ IP.interposeSuffix 10 A.read
424+ $ S.splitOnSuffix (== 10) A.write
425+ $ S.unfold FH.read inh
426+
427+-- XXX splitSuffixOn requires -funfolding-use-threshold=150 for better fusion
428+-- | Lines and unlines
429+{-# INLINE linesUnlinesArrayCharCopy #-}
430+linesUnlinesArrayCharCopy :: Handle -> Handle -> IO ()
431+linesUnlinesArrayCharCopy inh outh =
408432 S.fold (FH.write outh)
409433 $ SS.encodeChar8
410434 $ SS.unlines
@@ -413,12 +437,26 @@ linesUnlinesCopy inh outh =
413437 $ S.unfold FH.read inh
414438
415439#ifdef INSPECTION
416−inspect $ hasNoTypeClassesExcept 'linesUnlinesCopy [''Storable]
417−-- inspect $ 'linesUnlinesCopy `hasNoType` ''Step
418−-- inspect $ 'linesUnlinesCopy `hasNoType` ''AT.FlattenState
419−-- inspect $ 'linesUnlinesCopy `hasNoType` ''D.ConcatMapUState
440+inspect $ hasNoTypeClassesExcept 'linesUnlinesArrayCharCopy [''Storable]
441+-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''Step
442+-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''AT.FlattenState
443+-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''D.ConcatMapUState
420444#endif
421445
446+-- XXX to write this we need to be able to map decodeUtf8 on the A.read fold.
447+-- For that we have to write decodeUtf8 as a Pipe.
448+{-
449+{-# INLINE linesUnlinesArrayUtf8Copy #-}
450+linesUnlinesArrayUtf8Copy :: Handle -> Handle -> IO ()
451+linesUnlinesArrayUtf8Copy inh outh =
452+ S.fold (FH.write outh)
453+ $ SS.encodeChar8
454+ $ IP.intercalate (A.fromList [10]) (pipe SS.decodeUtf8P A.read)
455+ $ S.splitOnSuffix (== '\n') (IFL.lmap SS.encodeUtf8 A.write)
456+ $ SS.decodeChar8
457+ $ S.unfold FH.read inh
458+-}
459+
422460foreign import ccall unsafe "u_iswspace"
423461 iswspace :: Int -> Int
424462
@@ -446,8 +484,7 @@ isSp = isSpace . chr . fromIntegral
446484wordsUnwordsCopyWord8 :: Handle -> Handle -> IO ()
447485wordsUnwordsCopyWord8 inh outh =
448486 S.fold (FH.write outh)
449− $ S.concatUnfold IUF.fromList
450− $ S.intersperse [32]
487+ $ IP.interposeSuffix 32 IUF.fromList
451488 $ S.wordsBy isSp FL.toList
452489 $ S.unfold FH.read inh
453490
@@ -463,14 +500,7 @@ wordsUnwordsCopy :: Handle -> Handle -> IO ()
463500wordsUnwordsCopy inh outh =
464501 S.fold (FH.write outh)
465502 $ SS.encodeChar8
466− $ S.concatUnfold IUF.fromList
467− $ S.intersperse " "
468− -- Array allocation is too expensive for such small strings. So just use
469− -- lists instead.
470− --
471− -- -- $ SS.unwords
472− -- -- $ SS.words
473− --
503+ $ SS.unfoldWords IUF.fromList
474504 -- XXX This pipeline does not fuse with wordsBy but fuses with splitOn
475505 -- with -funfolding-use-threshold=300. With wordsBy it does not fuse
476506 -- even with high limits for inlining and spec-constr ghc options. With
@@ -490,6 +520,16 @@ wordsUnwordsCopy inh outh =
490520-- inspect $ 'wordsUnwordsCopy `hasNoType` ''D.ConcatMapUState
491521#endif
492522
523+{-# INLINE wordsUnwordsCharArrayCopy #-}
524+wordsUnwordsCharArrayCopy :: Handle -> Handle -> IO ()
525+wordsUnwordsCharArrayCopy inh outh =
526+ S.fold (FH.write outh)
527+ $ SS.encodeChar8
528+ $ SS.unwords
529+ $ SS.words
530+ $ SS.decodeChar8
531+ $ S.unfold FH.read inh
532+
493533lf :: Word8
494534lf = fromIntegral (ord '\n')
495535
src/Streamly/Data/String.hs+12−8
@@ -74,6 +74,7 @@ module Streamly.Data.String
7474 , foldLines
7575 , foldWords
7676 , unfoldLines
77+ , unfoldWords
7778
7879 -- * Streams of Strings
7980 , lines
@@ -95,9 +96,7 @@ import Streamly.Internal.Data.Unfold (Unfold)
9596
9697import qualified Streamly.Internal.Prelude as S
9798import qualified Streamly.Memory.Array as A
98−import qualified Streamly.Internal.Memory.ArrayStream as AS
9999import qualified Streamly.Streams.StreamD as D
100−import qualified Streamly.Internal.Data.Unfold as UF
101100
102101-- type String = List Char
103102
@@ -269,11 +268,9 @@ words = foldWords A.write
269268-- | Unfold a stream to character streams using the supplied 'Unfold'
270269-- and concat the results suffixing a newline character @\\n@ to each stream.
271270--
272−-- > unfoldLines = S.intercalateSuffix UF.identity '\n'
273−--
274271{-# INLINE unfoldLines #-}
275272unfoldLines :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
276−unfoldLines unf = S.intercalateSuffix UF.identity '\n' unf
273+unfoldLines = S.interposeSuffix '\n'
277274
278275-- | Flattens the stream of @Array Char@, after appending a terminating
279276-- newline to each string.
@@ -292,6 +289,14 @@ unfoldLines unf = S.intercalateSuffix UF.identity '\n' unf
292289unlines :: (MonadIO m, IsStream t) => t m (Array Char) -> t m Char
293290unlines = unfoldLines A.read
294291
292+-- | Unfold the elements of a stream to character streams using the supplied
293+-- 'Unfold' and concat the results with a whitespace character infixed between
294+-- the streams.
295+--
296+{-# INLINE unfoldWords #-}
297+unfoldWords :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
298+unfoldWords = S.interpose ' '
299+
295300-- | Flattens the stream of @Array Char@, after appending a separating
296301-- space to each string.
297302--
@@ -300,12 +305,11 @@ unlines = unfoldLines A.read
300305-- >>> S.toList $ unwords $ S.fromList ["unwords", "this", "string"]
301306-- "unwords this string"
302307--
303−--
304−-- > unwords = A.concat . (S.intersperse (A.fromList " "))
308+-- > unwords = unfoldWords A.read
305309--
306310-- Note that, in general
307311--
308312-- > unwords . words /= id
309313{-# INLINE unwords #-}
310314unwords :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
311−unwords = AS.concat . (S.intersperse (A.fromList " "))
315+unwords = unfoldWords A.read
src/Streamly/Internal/Data/Unfold.hs+10−2
@@ -83,6 +83,7 @@ module Streamly.Internal.Data.Unfold
8383 , effect
8484 , singleton
8585 , identity
86+ , const
8687 , replicateM
8788 , fromList
8889 , fromListM
@@ -119,7 +120,7 @@ where
119120import Control.Exception (Exception)
120121import Data.Void (Void)
121122import GHC.Types (SPEC(..))
122−import Prelude hiding (concat, map, takeWhile, take, filter)
123+import Prelude hiding (concat, map, takeWhile, take, filter, const)
123124
124125import Streamly.Streams.StreamD.Type (Stream(..), Step(..))
125126#if __GLASGOW_HASKELL__ < 800
@@ -130,6 +131,7 @@ import Streamly.Internal.Data.Fold.Types (Fold(..))
130131import Streamly.Internal.Data.SVar (defState)
131132import Control.Monad.Catch (MonadCatch)
132133
134+import qualified Prelude
133135import qualified Control.Monad.Catch as MC
134136import qualified Data.Tuple as Tuple
135137import qualified Streamly.Streams.StreamK as K
@@ -152,7 +154,7 @@ lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
152154--
153155{-# INLINE_NORMAL supply #-}
154156supply :: Unfold m a b -> a -> Unfold m Void b
155−supply unf a = lmap (const a) unf
157+supply unf a = lmap (Prelude.const a) unf
156158
157159-- | Supply the first component of the tuple to an unfold that accepts a tuple
158160-- as a seed resulting in a fold that accepts the second component of the tuple
@@ -344,6 +346,12 @@ singleton f = Unfold step inject
344346identity :: Monad m => Unfold m a a
345347identity = singleton return
346348
349+const :: Monad m => m b -> Unfold m a b
350+const m = Unfold step inject
351+ where
352+ inject _ = return ()
353+ step () = m >>= \r -> return $ Yield r ()
354+
347355-- | Generates a stream replicating the seed @n@ times.
348356--
349357{-# INLINE replicateM #-}
src/Streamly/Internal/Memory/ArrayStream.hs+11−3
@@ -26,6 +26,7 @@ module Streamly.Internal.Memory.ArrayStream
2626 -- * Flattening to elements
2727 , concat
2828 , concatRev
29+ , interpose
2930 , interposeSuffix
3031 , intercalateSuffix
3132
@@ -51,7 +52,6 @@ import Streamly.Internal.Memory.Array.Types (Array(..), length)
5152import Streamly.Streams.Serial (SerialT)
5253import Streamly.Streams.StreamK.Type (IsStream)
5354
54−import qualified Streamly.Internal.Data.Unfold as UF
5555import qualified Streamly.Internal.Memory.Array as A
5656import qualified Streamly.Internal.Memory.Array.Types as A
5757import qualified Streamly.Internal.Prelude as S
@@ -86,10 +86,18 @@ concat m = D.fromStreamD $ D.concatMapU A.read (D.toStreamD m)
8686concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
8787concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
8888
89+-- | Flatten a stream of arrays after inserting the given element between
90+-- arrays.
91+--
92+-- /Internal/
93+{-# INLINE interpose #-}
94+interpose :: (MonadIO m, IsStream t, Storable a) => a -> t m (Array a) -> t m a
95+interpose x = S.interpose x A.read
96+
8997{-# INLINE intercalateSuffix #-}
9098intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
9199 => Array a -> t m (Array a) -> t m a
92−intercalateSuffix arr = S.intercalateSuffix A.read arr A.read
100+intercalateSuffix arr = S.intercalateSuffix arr A.read
93101
94102-- | Flatten a stream of arrays appending the given element after each
95103-- array.
@@ -99,7 +107,7 @@ intercalateSuffix arr = S.intercalateSuffix A.read arr A.read
99107interposeSuffix :: (MonadIO m, IsStream t, Storable a)
100108 => a -> t m (Array a) -> t m a
101109-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
102−interposeSuffix x = S.intercalateSuffix UF.identity x A.read
110+interposeSuffix x = S.interposeSuffix x A.read
103111
104112-- | Split a stream of arrays on a given separator byte, dropping the separator
105113-- and coalescing all the arrays between two separators into a single array.
src/Streamly/Internal/Prelude.hs+192−41
@@ -33,7 +33,7 @@ module Streamly.Internal.Prelude
3333 -- ** From Values
3434 , yield
3535 , yieldM
36− , K.repeat
36+ , repeat
3737 , repeatM
3838 , replicate
3939 , replicateM
@@ -205,9 +205,9 @@ module Streamly.Internal.Prelude
205205 , insertBy
206206 , intersperseM
207207 , intersperse
208− , insertAfterEach
208+ , intersperseSuffix
209209 -- , intersperseBySpan
210− , interject
210+ , interjectSuffix
211211
212212 -- ** Reordering
213213 , reverse
@@ -220,8 +220,9 @@ module Streamly.Internal.Prelude
220220
221221 -- ** Interleaving
222222 , interleave
223− , interleaveFst
224223 , interleaveMin
224+ , interleaveSuffix
225+ , interleaveInfix
225226
226227 , Serial.wSerialFst
227228 , Serial.wSerialMin
@@ -254,8 +255,12 @@ module Streamly.Internal.Prelude
254255 , concatUnfoldRoundrobin
255256 , concatMap
256257 , concatMapWith
257− -- , intercalate
258+ , gintercalate
259+ , gintercalateSuffix
260+ , intercalate
258261 , intercalateSuffix
262+ , interpose
263+ , interposeSuffix
259264
260265 -- -- ** Breaking
261266
@@ -400,7 +405,8 @@ import Prelude
400405 foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
401406 notElem, maximum, minimum, head, last, tail, length, null,
402407 reverse, iterate, init, and, or, lookup, foldr1, (!!),
403− scanl, scanl1, replicate, concatMap, span, splitAt, break)
408+ scanl, scanl1, replicate, concatMap, span, splitAt, break,
409+ repeat)
404410
405411import qualified Data.Heap as H
406412import qualified Data.Map.Strict as Map
@@ -671,6 +677,14 @@ replicateMSerial n = fromStreamS . S.replicateM n
671677replicate :: (IsStream t, Monad m) => Int -> a -> t m a
672678replicate n = fromStreamS . S.replicate n
673679
680+-- |
681+-- Generate an infinite stream by repeating a pure value.
682+--
683+-- @since 0.4.0
684+{-# INLINE_NORMAL repeat #-}
685+repeat :: (IsStream t, Monad m) => a -> t m a
686+repeat = fromStreamS . S.repeat
687+
674688-- |
675689-- @
676690-- repeatM = fix . consM
@@ -687,9 +701,14 @@ replicate n = fromStreamS . S.replicate n
687701-- /Concurrent, infinite (do not use with 'parallely')/
688702--
689703-- @since 0.2.0
704+{-# INLINE_EARLY repeatM #-}
690705repeatM :: (IsStream t, MonadAsync m) => m a -> t m a
691−repeatM = go
692− where go m = m |: go m
706+repeatM = K.repeatM
707+
708+{-# RULES "repeatM serial" repeatM = repeatMSerial #-}
709+{-# INLINE repeatMSerial #-}
710+repeatMSerial :: MonadAsync m => m a -> SerialT m a
711+repeatMSerial = fromStreamS . S.repeatM
693712
694713-- |
695714-- @
@@ -1831,9 +1850,9 @@ intersperse a = fromStreamS . S.intersperse a . toStreamS
18311850-- | Insert a monadic action after each element in the stream.
18321851--
18331852-- @since 0.7.0
1834−{-# INLINE insertAfterEach #-}
1835−insertAfterEach :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1836−insertAfterEach m = fromStreamD . D.insertAfterEach m . toStreamD
1853+{-# INLINE intersperseSuffix #-}
1854+intersperseSuffix :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1855+intersperseSuffix m = fromStreamD . D.intersperseSuffix m . toStreamD
18371856
18381857{-
18391858-- | Intersperse a monadic action into the input stream after every @n@
@@ -1854,16 +1873,16 @@ intersperseBySpan _n _f _xs = undefined
18541873-- seconds.
18551874--
18561875-- @
1857−-- > S.drain $ S.interject 1 (putChar ',') $ S.mapM (\\x -> threadDelay 1000000 >> putChar x) $ S.fromList "hello"
1876+-- > S.drain $ S.interjectSuffix 1 (putChar ',') $ S.mapM (\\x -> threadDelay 1000000 >> putChar x) $ S.fromList "hello"
18581877-- "h,e,l,l,o"
18591878-- @
18601879--
18611880-- @since 0.7.0
1862−{-# INLINE interject #-}
1863−interject
1881+{-# INLINE interjectSuffix #-}
1882+interjectSuffix
18641883 :: (IsStream t, MonadAsync m)
18651884 => Double -> m a -> t m a -> t m a
1866−interject n f xs = xs `Par.parallelFst` repeatM timed
1885+interjectSuffix n f xs = xs `Par.parallelFst` repeatM timed
18671886 where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
18681887
18691888-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in
@@ -2144,13 +2163,26 @@ concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
21442163append ::(IsStream t, Monad m) => t m b -> t m b -> t m b
21452164append m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)
21462165
2147−-- Same as 'wSerial'. We should perhaps rename wSerial. If named explicitly
2148−-- this would be interleaveMax.
2166+-- XXX Same as 'wSerial'. We should perhaps rename wSerial to interleave.
2167+-- XXX Document the interleaving behavior of side effects in all the
2168+-- interleaving combinators.
2169+-- XXX Write time-domain equivalents of these. In the time domain we can
2170+-- interleave two streams such that the value of second stream is always taken
2171+-- from its last value even if no new value is being yielded, like
2172+-- zipWithLatest. It would be something like interleaveWithLatest.
21492173--
21502174-- | Interleaves the outputs of two streams, yielding elements from each stream
21512175-- alternately, starting from the first stream. If any of the streams finishes
21522176-- early the other stream continues alone until it too finishes.
21532177--
2178+-- >>> :set -XOverloadedStrings
2179+-- >>> interleave "ab" ",,,," :: SerialT Identity Char
2180+-- fromList "a,b,,,"
2181+-- >>> interleave "abcd" ",," :: SerialT Identity Char
2182+-- fromList "a,b,cd"
2183+--
2184+-- 'interleave' is dual to 'interleaveMin', it can be called @interleaveMax@.
2185+--
21542186-- Do not use at scale in concatMapWith.
21552187--
21562188-- @since 0.7.0
@@ -2160,18 +2192,63 @@ interleave m1 m2 = fromStreamD $ D.interleave (toStreamD m1) (toStreamD m2)
21602192
21612193-- | Interleaves the outputs of two streams, yielding elements from each stream
21622194-- alternately, starting from the first stream. As soon as the first stream
2163−-- finishes the output stops discarding the second stream.
2195+-- finishes, the output stops, discarding the remaining part of the second
2196+-- stream. In this case, the last element in the resulting stream would be from
2197+-- the second stream. If the second stream finishes early then the first stream
2198+-- still continues to yield elements until it finishes.
2199+--
2200+-- >>> :set -XOverloadedStrings
2201+-- >>> interleaveSuffix "abc" ",,,," :: SerialT Identity Char
2202+-- fromList "a,b,c,"
2203+-- >>> interleaveSuffix "abc" "," :: SerialT Identity Char
2204+-- fromList "a,bc"
2205+--
2206+-- 'interleaveSuffix' is a dual of 'interleaveInfix'.
2207+--
2208+-- Do not use at scale in concatMapWith.
2209+--
2210+-- @since 0.7.0
2211+{-# INLINE interleaveSuffix #-}
2212+interleaveSuffix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
2213+interleaveSuffix m1 m2 =
2214+ fromStreamD $ D.interleaveSuffix (toStreamD m1) (toStreamD m2)
2215+
2216+-- | Interleaves the outputs of two streams, yielding elements from each stream
2217+-- alternately, starting from the first stream and ending at the first stream.
2218+-- If the second stream is longer than the first, elements from the second
2219+-- stream are infixed with elements from the first stream. If the first stream
2220+-- is longer then it continues yielding elements even after the second stream
2221+-- has finished.
2222+--
2223+-- >>> :set -XOverloadedStrings
2224+-- >>> interleaveInfix "abc" ",,,," :: SerialT Identity Char
2225+-- fromList "a,b,c"
2226+-- >>> interleaveInfix "abc" "," :: SerialT Identity Char
2227+-- fromList "a,bc"
2228+--
2229+-- 'interleaveInfix' is a dual of 'interleaveSuffix'.
21642230--
21652231-- Do not use at scale in concatMapWith.
21662232--
21672233-- @since 0.7.0
2168−{-# INLINE interleaveFst #-}
2169−interleaveFst ::(IsStream t, Monad m) => t m b -> t m b -> t m b
2170−interleaveFst m1 m2 = fromStreamD $ D.interleaveFst (toStreamD m1) (toStreamD m2)
2234+{-# INLINE interleaveInfix #-}
2235+interleaveInfix ::(IsStream t, Monad m) => t m b -> t m b -> t m b
2236+interleaveInfix m1 m2 =
2237+ fromStreamD $ D.interleaveInfix (toStreamD m1) (toStreamD m2)
21712238
21722239-- | Interleaves the outputs of two streams, yielding elements from each stream
21732240-- alternately, starting from the first stream. The output stops as soon as any
2174−-- of the two streams finishes discarding the other.
2241+-- of the two streams finishes, discarding the remaining part of the other
2242+-- stream. The last element of the resulting stream would be from the longer
2243+-- stream.
2244+--
2245+-- >>> :set -XOverloadedStrings
2246+-- >>> interleaveMin "ab" ",,,," :: SerialT Identity Char
2247+-- fromList "a,b,"
2248+-- >>> interleaveMin "abcd" ",," :: SerialT Identity Char
2249+-- fromList "a,b,c"
2250+--
2251+-- 'interleaveMin' is dual to 'interleave'.
21752252--
21762253-- Do not use at scale in concatMapWith.
21772254--
@@ -2235,29 +2312,103 @@ concatUnfoldRoundrobin ::(IsStream t, Monad m)
22352312concatUnfoldRoundrobin u m =
22362313 fromStreamD $ D.concatUnfoldRoundrobin u (toStreamD m)
22372314
2238−{-
2239−-- | Insert a stream between segements of streams and flatten.
2240−intercalate :: (IsStream t, MonadAsync m)
2241− => SerialT Identity b -> (a -> t m b) -> t m a -> t m b
2242−intercalate = undefined
2243−-}
2315+-- XXX we can swap the order of arguments to gintercalate so that the
2316+-- definition of concatUnfold becomes simpler? The first stream should be
2317+-- infixed inside the second one. However, if we change the order in
2318+-- "interleave" as well similarly, then that will make it a bit unintuitive.
2319+--
2320+-- > concatUnfold unf str =
2321+-- > gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())
2322+--
2323+-- | 'interleaveInfix' followed by unfold and concat.
2324+--
2325+-- /Internal/
2326+{-# INLINE gintercalate #-}
2327+gintercalate
2328+ :: (IsStream t, Monad m)
2329+ => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
2330+gintercalate unf1 str1 unf2 str2 =
2331+ D.fromStreamD $ D.gintercalate
2332+ unf1 (D.toStreamD str1)
2333+ unf2 (D.toStreamD str2)
22442334
2245−-- | @intercalateSuffix genSuffix seed gen stream@ generates streams from seeds
2246−-- in the input @stream@ using the generator @gen@ and concatenates them
2247−-- after suffixing the stream generated by @genSuffix@ using @seed@.
2335+-- XXX The order of arguments in "intercalate" is consistent with the list
2336+-- intercalate but inconsistent with gintercalate and other stream interleaving
2337+-- combinators. We can change the order of the arguments in other combinators
2338+-- but then 'interleave' combinator may become a bit unintuitive because we
2339+-- will be starting with the second stream.
2340+
2341+-- > intercalate seed unf str = gintercalate unf str unf (repeatM seed)
2342+-- > intercalate a unf str = concatUnfold unf $ intersperse a str
22482343--
2249−-- For example to insert CRLF in a stream of character strings:
2344+-- | 'intersperse' followed by unfold and concat.
22502345--
2251−-- > unlines = intercalateSuffix UF.fromList "\r\n" UF.fromList
2346+-- > unwords = intercalate " " UF.fromList
22522347--
2253−{-# INLINE intercalateSuffix #-}
2254−intercalateSuffix
2348+-- >>> intercalate " " UF.fromList ["abc", "def", "ghi"]
2349+-- > "abc def ghi"
2350+--
2351+{-# INLINE intercalate #-}
2352+intercalate :: (IsStream t, Monad m)
2353+ => b -> Unfold m b c -> t m b -> t m c
2354+intercalate seed unf str = D.fromStreamD $
2355+ D.concatMapU unf $ D.intersperse seed (toStreamD str)
2356+
2357+-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)
2358+--
2359+-- | Unfold the elements of a stream, intersperse the given element between the
2360+-- unfolded streams and then concat them into a single stream.
2361+--
2362+-- > unwords = S.interpose ' '
2363+--
2364+-- /Internal/
2365+{-# INLINE interpose #-}
2366+interpose :: (IsStream t, Monad m)
2367+ => c -> Unfold m b c -> t m b -> t m c
2368+interpose x unf str =
2369+ D.fromStreamD $ D.interpose (return x) unf (D.toStreamD str)
2370+
2371+-- | 'interleaveSuffix' followed by unfold and concat.
2372+--
2373+-- /Internal/
2374+{-# INLINE gintercalateSuffix #-}
2375+gintercalateSuffix
22552376 :: (IsStream t, Monad m)
2256− => Unfold m a c -> a -> Unfold m b c -> t m b -> t m c
2257−intercalateSuffix suffix seed unf str =
2258− D.fromStreamD $ D.interleaveFstThenConcat
2259− unf (D.toStreamD str)
2260− suffix (D.repeat seed)
2377+ => Unfold m a c -> t m a -> Unfold m b c -> t m b -> t m c
2378+gintercalateSuffix unf1 str1 unf2 str2 =
2379+ D.fromStreamD $ D.gintercalateSuffix
2380+ unf1 (D.toStreamD str1)
2381+ unf2 (D.toStreamD str2)
2382+
2383+-- > intercalateSuffix seed unf str = gintercalateSuffix unf str unf (repeatM seed)
2384+-- > intercalateSuffix a unf str = concatUnfold unf $ intersperseSuffix a str
2385+--
2386+-- | 'intersperseSuffix' followed by unfold and concat.
2387+--
2388+-- > unlines = intercalateSuffix "\n" UF.fromList
2389+--
2390+-- >>> intercalate "\n" UF.fromList ["abc", "def", "ghi"]
2391+-- > "abc\ndef\nghi\n"
2392+--
2393+{-# INLINE intercalateSuffix #-}
2394+intercalateSuffix :: (IsStream t, Monad m)
2395+ => b -> Unfold m b c -> t m b -> t m c
2396+intercalateSuffix seed unf str = fromStreamD $ D.concatMapU unf
2397+ $ D.intersperseSuffix (return seed) (D.toStreamD str)
2398+
2399+-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)
2400+--
2401+-- | Unfold the elements of a stream, append the given element after each
2402+-- unfolded stream and then concat them into a single stream.
2403+--
2404+-- > unlines = S.interposeSuffix '\n'
2405+--
2406+-- /Internal/
2407+{-# INLINE interposeSuffix #-}
2408+interposeSuffix :: (IsStream t, Monad m)
2409+ => c -> Unfold m b c -> t m b -> t m c
2410+interposeSuffix x unf str =
2411+ D.fromStreamD $ D.interposeSuffix (return x) unf (D.toStreamD str)
22612412
22622413------------------------------------------------------------------------------
22632414-- Grouping/Splitting
@@ -2407,7 +2558,7 @@ intervalsOf
24072558 => Double -> Fold m a b -> t m a -> t m b
24082559intervalsOf n f xs =
24092560 splitWithSuffix isNothing (FL.lcatMaybes f)
2410− (interject n (return Nothing) (Serial.map Just xs))
2561+ (interjectSuffix n (return Nothing) (Serial.map Just xs))
24112562
24122563------------------------------------------------------------------------------
24132564-- Element Aware APIs
src/Streamly/Streams/StreamD.hs+329−12
@@ -65,6 +65,7 @@ module Streamly.Streams.StreamD
6565 -- ** Specialized Generation
6666 -- | Generate a monadic stream from a seed.
6767 , repeat
68+ , repeatM
6869 , replicate
6970 , replicateM
7071 , fromIndices
@@ -148,9 +149,13 @@ module Streamly.Streams.StreamD
148149 , InterleaveState(..)
149150 , interleave
150151 , interleaveMin
151− , interleaveFst
152+ , interleaveSuffix
153+ , interleaveInfix
152154 , roundRobin -- interleaveFair?/ParallelFair
153− , interleaveFstThenConcat
155+ , gintercalateSuffix
156+ , interposeSuffix
157+ , gintercalate
158+ , interpose
154159
155160 -- ** Grouping
156161 , groupsOf
@@ -229,7 +234,7 @@ module Streamly.Streams.StreamD
229234 -- * Inserting
230235 , intersperseM
231236 , intersperse
232− , insertAfterEach
237+ , intersperseSuffix
233238 , insertBy
234239
235240 -- * Deleting
@@ -419,6 +424,9 @@ unfold (Unfold ustep inject) seed = Stream step Nothing
419424-- Specialized Generation
420425------------------------------------------------------------------------------
421426
427+repeatM :: Monad m => m a -> Stream m a
428+repeatM x = Stream (\_ _ -> x >>= \r -> return $ Yield r ()) ()
429+
422430repeat :: Monad m => a -> Stream m a
423431repeat x = Stream (\_ _ -> return $ Yield x ()) ()
424432
@@ -2103,9 +2111,9 @@ interleaveMin (Stream step1 state1) (Stream step2 state2) =
21032111 step _ (InterleaveFirstOnly _) = undefined
21042112 step _ (InterleaveSecondOnly _) = undefined
21052113
2106−{-# INLINE_NORMAL interleaveFst #-}
2107−interleaveFst :: Monad m => Stream m a -> Stream m a -> Stream m a
2108−interleaveFst (Stream step1 state1) (Stream step2 state2) =
2114+{-# INLINE_NORMAL interleaveSuffix #-}
2115+interleaveSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a
2116+interleaveSuffix (Stream step1 state1) (Stream step2 state2) =
21092117 Stream step (InterleaveFirst state1 state2)
21102118
21112119 where
@@ -2134,6 +2142,52 @@ interleaveFst (Stream step1 state1) (Stream step2 state2) =
21342142
21352143 step _ (InterleaveSecondOnly _) = undefined
21362144
2145+data InterleaveInfixState s1 s2 a
2146+ = InterleaveInfixFirst s1 s2
2147+ | InterleaveInfixSecondBuf s1 s2
2148+ | InterleaveInfixSecondYield s1 s2 a
2149+ | InterleaveInfixFirstYield s1 s2 a
2150+ | InterleaveInfixFirstOnly s1
2151+
2152+{-# INLINE_NORMAL interleaveInfix #-}
2153+interleaveInfix :: Monad m => Stream m a -> Stream m a -> Stream m a
2154+interleaveInfix (Stream step1 state1) (Stream step2 state2) =
2155+ Stream step (InterleaveInfixFirst state1 state2)
2156+
2157+ where
2158+
2159+ {-# INLINE_LATE step #-}
2160+ step gst (InterleaveInfixFirst st1 st2) = do
2161+ r <- step1 gst st1
2162+ return $ case r of
2163+ Yield a s -> Yield a (InterleaveInfixSecondBuf s st2)
2164+ Skip s -> Skip (InterleaveInfixFirst s st2)
2165+ Stop -> Stop
2166+
2167+ step gst (InterleaveInfixSecondBuf st1 st2) = do
2168+ r <- step2 gst st2
2169+ return $ case r of
2170+ Yield a s -> Skip (InterleaveInfixSecondYield st1 s a)
2171+ Skip s -> Skip (InterleaveInfixSecondBuf st1 s)
2172+ Stop -> Skip (InterleaveInfixFirstOnly st1)
2173+
2174+ step gst (InterleaveInfixSecondYield st1 st2 x) = do
2175+ r <- step1 gst st1
2176+ return $ case r of
2177+ Yield a s -> Yield x (InterleaveInfixFirstYield s st2 a)
2178+ Skip s -> Skip (InterleaveInfixSecondYield s st2 x)
2179+ Stop -> Stop
2180+
2181+ step _ (InterleaveInfixFirstYield st1 st2 x) = do
2182+ return $ Yield x (InterleaveInfixSecondBuf st1 st2)
2183+
2184+ step gst (InterleaveInfixFirstOnly st1) = do
2185+ r <- step1 gst st1
2186+ return $ case r of
2187+ Yield a s -> Yield a (InterleaveInfixFirstOnly s)
2188+ Skip s -> Skip (InterleaveInfixFirstOnly s)
2189+ Stop -> Stop
2190+
21372191{-# INLINE_NORMAL roundRobin #-}
21382192roundRobin :: Monad m => Stream m a -> Stream m a -> Stream m a
21392193roundRobin (Stream step1 state1) (Stream step2 state2) =
@@ -2190,11 +2244,11 @@ data ICUState s1 s2 i1 i2 =
21902244-- => [streamA1, streamB1, streamA2...StreamAn, streamBn]
21912245-- => [a11, a12, ...a1j, b11, b12, ...b1k, a21, a22, ...]
21922246--
2193−{-# INLINE_NORMAL interleaveFstThenConcat #-}
2194−interleaveFstThenConcat
2247+{-# INLINE_NORMAL gintercalateSuffix #-}
2248+gintercalateSuffix
21952249 :: Monad m
21962250 => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
2197−interleaveFstThenConcat
2251+gintercalateSuffix
21982252 (Unfold istep1 inject1) (Stream step1 state1)
21992253 (Unfold istep2 inject2) (Stream step2 state2) =
22002254 Stream step (ICUFirst state1 state2)
@@ -2253,6 +2307,269 @@ interleaveFstThenConcat
22532307 step _ (ICUSecondOnly _s2) = undefined
22542308 step _ (ICUSecondOnlyInner _s2 _i2) = undefined
22552309
2310+data InterposeSuffixState s1 i1 =
2311+ InterposeSuffixFirst s1
2312+ -- | InterposeSuffixFirstYield s1 i1
2313+ | InterposeSuffixFirstInner s1 i1
2314+ | InterposeSuffixSecond s1
2315+
2316+-- Note that if an unfolded layer turns out to be nil we still emit the
2317+-- separator effect. An alternate behavior could be to emit the separator
2318+-- effect only if at least one element has been yielded by the unfolding.
2319+-- However, that becomes a bit complicated, so we have chosen the former
2320+-- behvaior for now.
2321+{-# INLINE_NORMAL interposeSuffix #-}
2322+interposeSuffix
2323+ :: Monad m
2324+ => m c -> Unfold m b c -> Stream m b -> Stream m c
2325+interposeSuffix
2326+ action
2327+ (Unfold istep1 inject1) (Stream step1 state1) =
2328+ Stream step (InterposeSuffixFirst state1)
2329+
2330+ where
2331+
2332+ {-# INLINE_LATE step #-}
2333+ step gst (InterposeSuffixFirst s1) = do
2334+ r <- step1 (adaptState gst) s1
2335+ case r of
2336+ Yield a s -> do
2337+ i <- inject1 a
2338+ i `seq` return (Skip (InterposeSuffixFirstInner s i))
2339+ -- i `seq` return (Skip (InterposeSuffixFirstYield s i))
2340+ Skip s -> return $ Skip (InterposeSuffixFirst s)
2341+ Stop -> return Stop
2342+
2343+ {-
2344+ step _ (InterposeSuffixFirstYield s1 i1) = do
2345+ r <- istep1 i1
2346+ return $ case r of
2347+ Yield x i' -> Yield x (InterposeSuffixFirstInner s1 i')
2348+ Skip i' -> Skip (InterposeSuffixFirstYield s1 i')
2349+ Stop -> Skip (InterposeSuffixFirst s1)
2350+ -}
2351+
2352+ step _ (InterposeSuffixFirstInner s1 i1) = do
2353+ r <- istep1 i1
2354+ return $ case r of
2355+ Yield x i' -> Yield x (InterposeSuffixFirstInner s1 i')
2356+ Skip i' -> Skip (InterposeSuffixFirstInner s1 i')
2357+ Stop -> Skip (InterposeSuffixSecond s1)
2358+
2359+ step _ (InterposeSuffixSecond s1) = do
2360+ r <- action
2361+ return $ Yield r (InterposeSuffixFirst s1)
2362+
2363+data ICALState s1 s2 i1 i2 a =
2364+ ICALFirst s1 s2
2365+ -- | ICALFirstYield s1 s2 i1
2366+ | ICALFirstInner s1 s2 i1
2367+ | ICALFirstOnly s1
2368+ | ICALFirstOnlyInner s1 i1
2369+ | ICALSecondInject s1 s2
2370+ | ICALFirstInject s1 s2 i2
2371+ -- | ICALFirstBuf s1 s2 i1 i2
2372+ | ICALSecondInner s1 s2 i1 i2
2373+ -- -- | ICALSecondInner s1 s2 i1 i2 a
2374+ -- -- | ICALFirstResume s1 s2 i1 i2 a
2375+
2376+-- | Interleave streams (full streams, not the elements) unfolded from two
2377+-- input streams and concat. Stop when the first stream stops. If the second
2378+-- stream ends before the first one then first stream still keeps running alone
2379+-- without any interleaving with the second stream.
2380+--
2381+-- [a1, a2, ... an] [b1, b2 ...]
2382+-- => [streamA1, streamA2, ... streamAn] [streamB1, streamB2, ...]
2383+-- => [streamA1, streamB1, streamA2...StreamAn, streamBn]
2384+-- => [a11, a12, ...a1j, b11, b12, ...b1k, a21, a22, ...]
2385+--
2386+{-# INLINE_NORMAL gintercalate #-}
2387+gintercalate
2388+ :: Monad m
2389+ => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
2390+gintercalate
2391+ (Unfold istep1 inject1) (Stream step1 state1)
2392+ (Unfold istep2 inject2) (Stream step2 state2) =
2393+ Stream step (ICALFirst state1 state2)
2394+
2395+ where
2396+
2397+ {-# INLINE_LATE step #-}
2398+ step gst (ICALFirst s1 s2) = do
2399+ r <- step1 (adaptState gst) s1
2400+ case r of
2401+ Yield a s -> do
2402+ i <- inject1 a
2403+ i `seq` return (Skip (ICALFirstInner s s2 i))
2404+ -- i `seq` return (Skip (ICALFirstYield s s2 i))
2405+ Skip s -> return $ Skip (ICALFirst s s2)
2406+ Stop -> return Stop
2407+
2408+ {-
2409+ step _ (ICALFirstYield s1 s2 i1) = do
2410+ r <- istep1 i1
2411+ return $ case r of
2412+ Yield x i' -> Yield x (ICALFirstInner s1 s2 i')
2413+ Skip i' -> Skip (ICALFirstYield s1 s2 i')
2414+ Stop -> Skip (ICALFirst s1 s2)
2415+ -}
2416+
2417+ step _ (ICALFirstInner s1 s2 i1) = do
2418+ r <- istep1 i1
2419+ return $ case r of
2420+ Yield x i' -> Yield x (ICALFirstInner s1 s2 i')
2421+ Skip i' -> Skip (ICALFirstInner s1 s2 i')
2422+ Stop -> Skip (ICALSecondInject s1 s2)
2423+
2424+ step gst (ICALFirstOnly s1) = do
2425+ r <- step1 (adaptState gst) s1
2426+ case r of
2427+ Yield a s -> do
2428+ i <- inject1 a
2429+ i `seq` return (Skip (ICALFirstOnlyInner s i))
2430+ Skip s -> return $ Skip (ICALFirstOnly s)
2431+ Stop -> return Stop
2432+
2433+ step _ (ICALFirstOnlyInner s1 i1) = do
2434+ r <- istep1 i1
2435+ return $ case r of
2436+ Yield x i' -> Yield x (ICALFirstOnlyInner s1 i')
2437+ Skip i' -> Skip (ICALFirstOnlyInner s1 i')
2438+ Stop -> Skip (ICALFirstOnly s1)
2439+
2440+ -- We inject the second stream even before checking if the first stream
2441+ -- would yield any more elements. There is no clear choice whether we
2442+ -- should do this before or after that. Doing it after may make the state
2443+ -- machine a bit simpler though.
2444+ step gst (ICALSecondInject s1 s2) = do
2445+ r <- step2 (adaptState gst) s2
2446+ case r of
2447+ Yield a s -> do
2448+ i <- inject2 a
2449+ i `seq` return (Skip (ICALFirstInject s1 s i))
2450+ Skip s -> return $ Skip (ICALSecondInject s1 s)
2451+ Stop -> return $ Skip (ICALFirstOnly s1)
2452+
2453+ step gst (ICALFirstInject s1 s2 i2) = do
2454+ r <- step1 (adaptState gst) s1
2455+ case r of
2456+ Yield a s -> do
2457+ i <- inject1 a
2458+ i `seq` return (Skip (ICALSecondInner s s2 i i2))
2459+ -- i `seq` return (Skip (ICALFirstBuf s s2 i i2))
2460+ Skip s -> return $ Skip (ICALFirstInject s s2 i2)
2461+ Stop -> return Stop
2462+
2463+ {-
2464+ step _ (ICALFirstBuf s1 s2 i1 i2) = do
2465+ r <- istep1 i1
2466+ return $ case r of
2467+ Yield x i' -> Skip (ICALSecondInner s1 s2 i' i2 x)
2468+ Skip i' -> Skip (ICALFirstBuf s1 s2 i' i2)
2469+ Stop -> Stop
2470+
2471+ step _ (ICALSecondInner s1 s2 i1 i2 v) = do
2472+ r <- istep2 i2
2473+ return $ case r of
2474+ Yield x i' -> Yield x (ICALSecondInner s1 s2 i1 i' v)
2475+ Skip i' -> Skip (ICALSecondInner s1 s2 i1 i' v)
2476+ Stop -> Skip (ICALFirstResume s1 s2 i1 i2 v)
2477+ -}
2478+
2479+ step _ (ICALSecondInner s1 s2 i1 i2) = do
2480+ r <- istep2 i2
2481+ return $ case r of
2482+ Yield x i' -> Yield x (ICALSecondInner s1 s2 i1 i')
2483+ Skip i' -> Skip (ICALSecondInner s1 s2 i1 i')
2484+ Stop -> Skip (ICALFirstInner s1 s2 i1)
2485+ -- Stop -> Skip (ICALFirstResume s1 s2 i1 i2)
2486+
2487+ {-
2488+ step _ (ICALFirstResume s1 s2 i1 i2 x) = do
2489+ return $ Yield x (ICALFirstInner s1 s2 i1 i2)
2490+ -}
2491+
2492+data InterposeState s1 i1 a =
2493+ InterposeFirst s1
2494+ -- | InterposeFirstYield s1 i1
2495+ | InterposeFirstInner s1 i1
2496+ | InterposeFirstInject s1
2497+ -- | InterposeFirstBuf s1 i1
2498+ | InterposeSecondYield s1 i1
2499+ -- -- | InterposeSecondYield s1 i1 a
2500+ -- -- | InterposeFirstResume s1 i1 a
2501+
2502+-- Note that this only interposes the pure values, we may run many effects to
2503+-- generate those values as some effects may not generate anything (Skip).
2504+{-# INLINE_NORMAL interpose #-}
2505+interpose :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c
2506+interpose
2507+ action
2508+ (Unfold istep1 inject1) (Stream step1 state1) =
2509+ Stream step (InterposeFirst state1)
2510+
2511+ where
2512+
2513+ {-# INLINE_LATE step #-}
2514+ step gst (InterposeFirst s1) = do
2515+ r <- step1 (adaptState gst) s1
2516+ case r of
2517+ Yield a s -> do
2518+ i <- inject1 a
2519+ i `seq` return (Skip (InterposeFirstInner s i))
2520+ -- i `seq` return (Skip (InterposeFirstYield s i))
2521+ Skip s -> return $ Skip (InterposeFirst s)
2522+ Stop -> return Stop
2523+
2524+ {-
2525+ step _ (InterposeFirstYield s1 i1) = do
2526+ r <- istep1 i1
2527+ return $ case r of
2528+ Yield x i' -> Yield x (InterposeFirstInner s1 i')
2529+ Skip i' -> Skip (InterposeFirstYield s1 i')
2530+ Stop -> Skip (InterposeFirst s1)
2531+ -}
2532+
2533+ step _ (InterposeFirstInner s1 i1) = do
2534+ r <- istep1 i1
2535+ return $ case r of
2536+ Yield x i' -> Yield x (InterposeFirstInner s1 i')
2537+ Skip i' -> Skip (InterposeFirstInner s1 i')
2538+ Stop -> Skip (InterposeFirstInject s1)
2539+
2540+ step gst (InterposeFirstInject s1) = do
2541+ r <- step1 (adaptState gst) s1
2542+ case r of
2543+ Yield a s -> do
2544+ i <- inject1 a
2545+ -- i `seq` return (Skip (InterposeFirstBuf s i))
2546+ i `seq` return (Skip (InterposeSecondYield s i))
2547+ Skip s -> return $ Skip (InterposeFirstInject s)
2548+ Stop -> return Stop
2549+
2550+ {-
2551+ step _ (InterposeFirstBuf s1 i1) = do
2552+ r <- istep1 i1
2553+ return $ case r of
2554+ Yield x i' -> Skip (InterposeSecondYield s1 i' x)
2555+ Skip i' -> Skip (InterposeFirstBuf s1 i')
2556+ Stop -> Stop
2557+ -}
2558+
2559+ {-
2560+ step _ (InterposeSecondYield s1 i1 v) = do
2561+ r <- action
2562+ return $ Yield r (InterposeFirstResume s1 i1 v)
2563+ -}
2564+ step _ (InterposeSecondYield s1 i1) = do
2565+ r <- action
2566+ return $ Yield r (InterposeFirstInner s1 i1)
2567+
2568+ {-
2569+ step _ (InterposeFirstResume s1 i1 v) = do
2570+ return $ Yield v (InterposeFirstInner s1 i1)
2571+ -}
2572+
22562573------------------------------------------------------------------------------
22572574-- Exceptions
22582575------------------------------------------------------------------------------
@@ -2853,9 +3170,9 @@ data SuffixState s a
28533170 | SuffixSuffix s
28543171 | SuffixYield a (SuffixState s a)
28553172
2856−{-# INLINE_NORMAL insertAfterEach #-}
2857−insertAfterEach :: forall m a. Monad m => m a -> Stream m a -> Stream m a
2858−insertAfterEach action (Stream step state) = Stream step' (SuffixElem state)
3173+{-# INLINE_NORMAL intersperseSuffix #-}
3174+intersperseSuffix :: forall m a. Monad m => m a -> Stream m a -> Stream m a
3175+intersperseSuffix action (Stream step state) = Stream step' (SuffixElem state)
28593176 where
28603177 {-# INLINE_LATE step' #-}
28613178 step' gst (SuffixElem st) = do
src/Streamly/Streams/StreamK.hs+9−1
@@ -62,6 +62,7 @@ module Streamly.Streams.StreamK
6262
6363 -- ** Specialized Generation
6464 , repeat
65+ , repeatM
6566 , replicate
6667 , replicateM
6768 , fromIndices
@@ -268,9 +269,16 @@ once = yieldM
268269-- repeatM = cycle1 . yield
269270-- @
270271--
272+-- Generate an infinite stream by repeating a monadic value.
273+--
274+-- /Internal/
275+repeatM :: (IsStream t, MonadAsync m) => m a -> t m a
276+repeatM = go
277+ where go m = m |: go m
278+
271279-- Generate an infinite stream by repeating a pure value.
272280--
273−-- @since 0.4.0
281+-- /Internal/
274282{-# INLINE repeat #-}
275283repeat :: IsStream t => a -> t m a
276284repeat a = let x = cons a x in x