on master help sign in
Add intersperse to Streamly.Prelude.
d4bef2ePranay Sashank
Changelog.md+2
@@ -66,6 +66,8 @@ example, concat streams concurrently using this.
66 * `reverse'` a several times faster reverse using Storable instance
66 * `reverse'` a several times faster reverse using Storable instance
67 * `intersperseByTime` intersperse a mondadic action in a stream
67 * `intersperseByTime` intersperse a mondadic action in a stream
68 periodically
68 periodically
69+ * `intersperse` intersperse an element in between consecutive elements in
70+ stream
69
71
70### Deprecations
72### Deprecations
71
73
benchmark/BaseStreams.hs+6
@@ -101,6 +101,8 @@ main =
101 , benchIO "mapMaybeM" (D.mapMaybeM 1) D.sourceUnfoldrM
101 , benchIO "mapMaybeM" (D.mapMaybeM 1) D.sourceUnfoldrM
102 , benchIO "concatMap" (D.concatMap 1) (D.sourceUnfoldrMN D.value2)
102 , benchIO "concatMap" (D.concatMap 1) (D.sourceUnfoldrMN D.value2)
103 , benchIO "concatMapPure" (D.concatMap 1) (D.sourceUnfoldrN D.value2)
103 , benchIO "concatMapPure" (D.concatMap 1) (D.sourceUnfoldrN D.value2)
104+ , benchIO "intersperse" (D.intersperse 1) (D.sourceUnfoldrMN D.value2)
105+ , benchIO "interspersePure" (D.intersperse 1) (D.sourceUnfoldrN D.value2)
104 -- , benchIO "foldrS" (D.foldrS 1) D.sourceUnfoldrM
106 -- , benchIO "foldrS" (D.foldrS 1) D.sourceUnfoldrM
105 -- This has horrible performance, never finishes
107 -- This has horrible performance, never finishes
106 -- , benchIO "foldlS" (D.foldlS 1) D.sourceUnfoldrM
108 -- , benchIO "foldlS" (D.foldlS 1) D.sourceUnfoldrM
@@ -113,6 +115,7 @@ main =
113 , benchIO "mapMaybe" (D.mapMaybe 4) D.sourceUnfoldrM
115 , benchIO "mapMaybe" (D.mapMaybe 4) D.sourceUnfoldrM
114 , benchIO "mapMaybeM" (D.mapMaybeM 4) D.sourceUnfoldrM
116 , benchIO "mapMaybeM" (D.mapMaybeM 4) D.sourceUnfoldrM
115 -- , benchIO "concatMap" (D.concatMap 4) (D.sourceUnfoldrMN D.value16)
117 -- , benchIO "concatMap" (D.concatMap 4) (D.sourceUnfoldrMN D.value16)
118+ , benchIO "intersperse" (D.intersperse 4) (D.sourceUnfoldrMN D.value16)
116 ]
119 ]
117 , bgroup "filtering"
120 , bgroup "filtering"
118 [ benchIO "filter-even" (D.filterEven 1) D.sourceUnfoldrM
121 [ benchIO "filter-even" (D.filterEven 1) D.sourceUnfoldrM
@@ -242,6 +245,8 @@ main =
242 -- , benchIOSrcK "concatMap" K.concatMap
245 -- , benchIOSrcK "concatMap" K.concatMap
243 , benchIO "concatMap" (K.concatMap 1) (K.sourceUnfoldrMN K.value2)
246 , benchIO "concatMap" (K.concatMap 1) (K.sourceUnfoldrMN K.value2)
244 , benchIO "concatMapPure" (K.concatMap 1) (K.sourceUnfoldrN K.value2)
247 , benchIO "concatMapPure" (K.concatMap 1) (K.sourceUnfoldrN K.value2)
248+ , benchIO "intersperse" (K.intersperse 1) (K.sourceUnfoldrMN K.value2)
249+ , benchIO "interspersePure" (K.intersperse 1) (K.sourceUnfoldrN K.value2)
245 , benchIO "foldlS" (K.foldlS 1) K.sourceUnfoldrM
250 , benchIO "foldlS" (K.foldlS 1) K.sourceUnfoldrM
246 ]
251 ]
247 , bgroup "transformationX4"
252 , bgroup "transformationX4"
@@ -251,6 +256,7 @@ main =
251 , benchIO "mapM" (K.mapM 4) K.sourceUnfoldrM
256 , benchIO "mapM" (K.mapM 4) K.sourceUnfoldrM
252 , benchIO "mapMSerial" (K.mapMSerial 4) K.sourceUnfoldrM
257 , benchIO "mapMSerial" (K.mapMSerial 4) K.sourceUnfoldrM
253 -- , benchIO "concatMap" (K.concatMap 4) (K.sourceUnfoldrMN K.value16)
258 -- , benchIO "concatMap" (K.concatMap 4) (K.sourceUnfoldrMN K.value16)
259+ , benchIO "intersperse" (K.intersperse 4) (K.sourceUnfoldrMN K.value16)
254 ]
260 ]
255 , bgroup "filtering"
261 , bgroup "filtering"
256 [ benchIO "filter-even" (K.filterEven 1) K.sourceUnfoldrM
262 [ benchIO "filter-even" (K.filterEven 1) K.sourceUnfoldrM
benchmark/Linear.hs+2
@@ -300,6 +300,7 @@ main =
300 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 1)
300 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 1)
301 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 1)
301 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 1)
302 , benchIOSink "deleteBy" (Ops.deleteBy 1)
302 , benchIOSink "deleteBy" (Ops.deleteBy 1)
303+ , benchIOSink "intersperse" (Ops.intersperse 1)
303 , benchIOSink "insertBy" (Ops.insertBy 1)
304 , benchIOSink "insertBy" (Ops.insertBy 1)
304 ]
305 ]
305 , bgroup "filteringX4"
306 , bgroup "filteringX4"
@@ -315,6 +316,7 @@ main =
315 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 4)
316 --, benchIOSink "dropWhileM-true" (Ops.dropWhileMTrue 4)
316 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 4)
317 , benchIOSink "dropWhile-false" (Ops.dropWhileFalse 4)
317 , benchIOSink "deleteBy" (Ops.deleteBy 4)
318 , benchIOSink "deleteBy" (Ops.deleteBy 4)
319+ , benchIOSink "intersperse" (Ops.intersperse 4)
318 , benchIOSink "insertBy" (Ops.insertBy 4)
320 , benchIOSink "insertBy" (Ops.insertBy 4)
319 ]
321 ]
320 , bgroup "multi-stream"
322 , bgroup "multi-stream"
benchmark/LinearOps.hs+3−1
@@ -357,7 +357,8 @@ scan, scanl1', map, fmap, mapMaybe, filterEven, filterAllOut,
357 => Int -> Stream m Int -> m ()
357 => Int -> Stream m Int -> m ()
358
358
359{-# INLINE mapMaybeM #-}
359{-# INLINE mapMaybeM #-}
360+{-# INLINE intersperse #-}
360−mapMaybeM :: S.MonadAsync m => Int -> Stream m Int -> m ()
361+mapMaybeM, intersperse :: S.MonadAsync m => Int -> Stream m Int -> m ()
361
362
362{-# INLINE mapM #-}
363{-# INLINE mapM #-}
363{-# INLINE transformMapM #-}
364{-# INLINE transformMapM #-}
@@ -415,6 +416,7 @@ dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
415dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
416dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
416findIndices n = composeN n $ S.findIndices (== maxValue)
417findIndices n = composeN n $ S.findIndices (== maxValue)
417elemIndices n = composeN n $ S.elemIndices maxValue
418elemIndices n = composeN n $ S.elemIndices maxValue
419+intersperse n = composeN n $ S.intersperse maxValue
418insertBy n = composeN n $ S.insertBy compare maxValue
420insertBy n = composeN n $ S.insertBy compare maxValue
419deleteBy n = composeN n $ S.deleteBy (>=) maxValue
421deleteBy n = composeN n $ S.deleteBy (>=) maxValue
420reverse n = composeN n $ S.reverse
422reverse n = composeN n $ S.reverse
benchmark/StreamDOps.hs+3−1
@@ -177,10 +177,11 @@ composeN n f =
177{-# INLINE foldrS #-}
177{-# INLINE foldrS #-}
178{-# INLINE foldlS #-}
178{-# INLINE foldlS #-}
179{-# INLINE concatMap #-}
179{-# INLINE concatMap #-}
180+{-# INLINE intersperse #-}
180scan, map, fmap, mapM, mapMaybe, mapMaybeM, filterEven, filterAllOut,
181scan, map, fmap, mapM, mapMaybe, mapMaybeM, filterEven, filterAllOut,
181 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
182 filterAllIn, takeOne, takeAll, takeWhileTrue, takeWhileMTrue, dropOne,
182 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse, foldrS, foldlS,
183 dropAll, dropWhileTrue, dropWhileMTrue, dropWhileFalse, foldrS, foldlS,
183− concatMap
184+ concatMap, intersperse
184 :: Monad m
185 :: Monad m
185 => Int -> Stream m Int -> m ()
186 => Int -> Stream m Int -> m ()
186
187
@@ -207,6 +208,7 @@ dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
207foldrS n = composeN n $ S.foldrS S.cons S.nil
208foldrS n = composeN n $ S.foldrS S.cons S.nil
208foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
209foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
209concatMap n = composeN n $ (\s -> S.concatMap (\_ -> s) s)
210concatMap n = composeN n $ (\s -> S.concatMap (\_ -> s) s)
211+intersperse n = composeN n $ S.intersperse maxValue
210
212
211-------------------------------------------------------------------------------
213-------------------------------------------------------------------------------
212-- Iteration
214-- Iteration
benchmark/StreamKOps.hs+4−1
@@ -205,7 +205,9 @@ scan, map, fmap, filterEven, filterAllOut,
205
205
206{-# INLINE mapM #-}
206{-# INLINE mapM #-}
207{-# INLINE mapMSerial #-}
207{-# INLINE mapMSerial #-}
208+{-# INLINE intersperse #-}
209+mapM, mapMSerial, intersperse
208−mapM, mapMSerial :: S.MonadAsync m => Int -> Stream m Int -> m ()
210+ :: S.MonadAsync m => Int -> Stream m Int -> m ()
209
211
210scan n = composeN n $ S.scanl' (+) 0
212scan n = composeN n $ S.scanl' (+) 0
211map n = composeN n $ P.fmap (+1)
213map n = composeN n $ P.fmap (+1)
@@ -224,6 +226,7 @@ dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
224dropWhileFalse n = composeN n $ S.dropWhile (<= 1)
226dropWhileFalse n = composeN n $ S.dropWhile (<= 1)
225foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
227foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
226concatMap n = composeN n $ (\s -> S.concatMap (\_ -> s) s)
228concatMap n = composeN n $ (\s -> S.concatMap (\_ -> s) s)
229+intersperse n = composeN n $ S.intersperse maxValue
227
230
228-------------------------------------------------------------------------------
231-------------------------------------------------------------------------------
229-- Iteration
232-- Iteration
src/Streamly/Prelude.hs+15−1
@@ -369,6 +369,7 @@ module Streamly.Prelude
369
369
370 , insertBy
370 , insertBy
371 , intersperseM
371 , intersperseM
372+ , intersperse
372 -- , intersperseBySpan
373 -- , intersperseBySpan
373 , intersperseByTime
374 , intersperseByTime
374
375
@@ -1933,7 +1934,20 @@ reverse' s = fromStreamD $ D.reverse' $ toStreamD s
1933-- @since 0.5.0
1934-- @since 0.5.0
1934{-# INLINE intersperseM #-}
1935{-# INLINE intersperseM #-}
1935intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1936intersperseM :: (IsStream t, MonadAsync m) => m a -> t m a -> t m a
1936−intersperseM = K.intersperseM
1937+intersperseM m = fromStreamS . S.intersperseM m . toStreamS
1938+
1939+-- | Generate a stream by inserting a given element between consecutive
1940+-- elements of the given stream.
1941+--
1942+-- @
1943+-- > S.toList $ S.intersperse ',' $ S.fromList "hello"
1944+-- "h,e,l,l,o"
1945+-- @
1946+--
1947+-- @since 0.7.0
1948+{-# INLINE intersperse #-}
1949+intersperse :: (IsStream t, MonadAsync m) => a -> t m a -> t m a
1950+intersperse a = fromStreamS . S.intersperse a . toStreamS
1937
1951
1938{-
1952{-
1939-- | Intersperse a monadic action into the input stream after every @n@
1953-- | Intersperse a monadic action into the input stream after every @n@
src/Streamly/Streams/StreamD.hs+34
@@ -203,6 +203,8 @@ module Streamly.Streams.StreamD
203 , sequence
203 , sequence
204
204
205 -- * Inserting
205 -- * Inserting
206+ , intersperseM
207+ , intersperse
206 , insertBy
208 , insertBy
207
209
208 -- * Deleting
210 -- * Deleting
@@ -2174,6 +2176,38 @@ sequence (Stream step state) = Stream step' state
2174-- Inserting
2176-- Inserting
2175------------------------------------------------------------------------------
2177------------------------------------------------------------------------------
2176
2178
2179+data LoopState x s = FirstYield s
2180+ | InterspersingYield s
2181+ | YieldAndCarry x s
2182+
2183+{-# INLINE_NORMAL intersperseM #-}
2184+intersperseM :: Monad m => m a -> Stream m a -> Stream m a
2185+intersperseM m (Stream step state) = Stream step' (FirstYield state)
2186+ where
2187+ {-# INLINE_LATE step' #-}
2188+ step' gst (FirstYield st) = do
2189+ r <- step gst st
2190+ return $
2191+ case r of
2192+ Yield x s -> Skip (YieldAndCarry x s)
2193+ Skip s -> Skip (FirstYield s)
2194+ Stop -> Stop
2195+
2196+ step' gst (InterspersingYield st) = do
2197+ r <- step gst st
2198+ case r of
2199+ Yield x s -> do
2200+ a <- m
2201+ return $ Yield a (YieldAndCarry x s)
2202+ Skip s -> return $ Skip $ InterspersingYield s
2203+ Stop -> return Stop
2204+
2205+ step' _ (YieldAndCarry x st) = return $ Yield x (InterspersingYield st)
2206+
2207+{-# INLINE intersperse #-}
2208+intersperse :: Monad m => a -> Stream m a -> Stream m a
2209+intersperse a = intersperseM (return a)
2210+
2177{-# INLINE_NORMAL insertBy #-}
2211{-# INLINE_NORMAL insertBy #-}
2178insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
2212insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
2179insertBy cmp a (Stream step state) = Stream step' (state, False, Nothing)
2213insertBy cmp a (Stream step state) = Stream step' (state, False, Nothing)
src/Streamly/Streams/StreamK.hs+5
@@ -143,6 +143,7 @@ module Streamly.Streams.StreamK
143
143
144 -- ** Inserting
144 -- ** Inserting
145 , intersperseM
145 , intersperseM
146+ , intersperse
146 , insertBy
147 , insertBy
147
148
148 -- ** Deleting
149 -- ** Deleting
@@ -826,6 +827,10 @@ intersperseM a m = prependingStart m
826 yieldk i x = foldStreamShared st yld sng stp $ a |: return i |: go x
827 yieldk i x = foldStreamShared st yld sng stp $ a |: return i |: go x
827 in foldStream st yieldk single stp m2
828 in foldStream st yieldk single stp m2
828
829
830+{-# INLINE intersperse #-}
831+intersperse :: (IsStream t, MonadAsync m) => a -> t m a -> t m a
832+intersperse a = intersperseM (return a)
833+
829{-# INLINE insertBy #-}
834{-# INLINE insertBy #-}
830insertBy :: IsStream t => (a -> a -> Ordering) -> a -> t m a -> t m a
835insertBy :: IsStream t => (a -> a -> Ordering) -> a -> t m a -> t m a
831insertBy cmp x m = go m
836insertBy cmp x m = go m
src/Streamly/String.hs+1−1
@@ -191,4 +191,4 @@ unlines = D.fromStreamD . A.unlines . D.toStreamD
191
191
192{-# INLINE unwords #-}
192{-# INLINE unwords #-}
193unwords :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
193unwords :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
194−unwords = A.flattenArrays . (S.intersperseM (return $ A.fromList " "))
194+unwords = A.flattenArrays . (S.intersperse (A.fromList " "))