on master help sign in
implement Array intercalate in terms of stream op
7a8227eHarendra Kumar
src/Streamly/Data/String.hs+16−3
@@ -70,6 +70,7 @@ module Streamly.Data.String
70 , stripStart
70 , stripStart
71 , foldLines
71 , foldLines
72 , foldWords
72 , foldWords
73+ , unfoldLines
73
74
74 -- * Streams of Strings
75 -- * Streams of Strings
75 , lines
76 , lines
@@ -87,10 +88,13 @@ import Streamly (IsStream, MonadAsync)
87import Prelude hiding (String, lines, words, unlines, unwords)
88import Prelude hiding (String, lines, words, unlines, unwords)
88import Streamly.Fold (Fold)
89import Streamly.Fold (Fold)
89import Streamly.Memory.Array (Array)
90import Streamly.Memory.Array (Array)
91+import Streamly.Unfold (Unfold)
90
92
91−import qualified Streamly.Prelude as S
93+import qualified Streamly.Prelude.Internal as S
92import qualified Streamly.Memory.Array as A
94import qualified Streamly.Memory.Array as A
95+import qualified Streamly.Memory.Array.Types as A
93import qualified Streamly.Streams.StreamD as D
96import qualified Streamly.Streams.StreamD as D
97+import qualified Streamly.Unfold as UF
94
98
95-- type String = List Char
99-- type String = List Char
96
100
@@ -242,6 +246,15 @@ lines = foldLines A.write
242words :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
246words :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
243words = foldWords A.write
247words = foldWords A.write
244
248
249+-- | Unfold a stream to character streams using the supplied 'Unfold'
250+-- and concat the results suffixing a newline character @\\n@ to each stream.
251+--
252+-- > unfoldLines = S.intercalateSuffix UF.singleton '\n'
253+--
254+{-# INLINE unfoldLines #-}
255+unfoldLines :: (MonadIO m, IsStream t) => Unfold m a Char -> t m a -> t m Char
256+unfoldLines unf = S.intercalateSuffix UF.singleton '\n' unf
257+
245-- | Flattens the stream of @Array Char@, after appending a terminating
258-- | Flattens the stream of @Array Char@, after appending a terminating
246-- newline to each string.
259-- newline to each string.
247--
260--
@@ -250,14 +263,14 @@ words = foldWords A.write
250-- >>> S.toList $ unlines $ S.fromList ["lines", "this", "string"]
263-- >>> S.toList $ unlines $ S.fromList ["lines", "this", "string"]
251-- "lines\nthis\nstring\n"
264-- "lines\nthis\nstring\n"
252--
265--
253−-- > unlines = A.concatWithSuffix '\n'
266+-- > unlines = unfoldLines A.readU
254--
267--
255-- Note that, in general
268-- Note that, in general
256--
269--
257-- > unlines . lines /= id
270-- > unlines . lines /= id
258{-# INLINE unlines #-}
271{-# INLINE unlines #-}
259unlines :: (MonadIO m, IsStream t) => t m (Array Char) -> t m Char
272unlines :: (MonadIO m, IsStream t) => t m (Array Char) -> t m Char
260−unlines = A.concatWithSuffix '\n'
273+unlines = unfoldLines A.readU
261
274
262-- | Flattens the stream of @Array Char@, after appending a separating
275-- | Flattens the stream of @Array Char@, after appending a separating
263-- space to each string.
276-- space to each string.
src/Streamly/Internal.hs+1
@@ -66,6 +66,7 @@ module Streamly.Internal
66 , concatMapU
66 , concatMapU
67 , concatUnfoldInterleave
67 , concatUnfoldInterleave
68 , concatUnfoldRoundrobin
68 , concatUnfoldRoundrobin
69+ , intercalateSuffix
69
70
70 -- * Streamly.Fold Experimental Exports
71 -- * Streamly.Fold Experimental Exports
71 , Fold (..)
72 , Fold (..)
src/Streamly/Memory/Array.hs−1
@@ -128,7 +128,6 @@ module Streamly.Memory.Array
128 -- ** Elimination
128 -- ** Elimination
129 , AS.concat
129 , AS.concat
130 -- , AS.concatRev
130 -- , AS.concatRev
131− , AS.concatWithSuffix
132 , AS.toArray
131 , AS.toArray
133
132
134 -- ** Transformation
133 -- ** Transformation
src/Streamly/Memory/ArrayStream.hs+13−8
@@ -26,7 +26,8 @@ module Streamly.Memory.ArrayStream
26 -- * Flattening to elements
26 -- * Flattening to elements
27 , concat
27 , concat
28 , concatRev
28 , concatRev
29− , concatWithSuffix
29+ , interposeSuffix
30+ , intercalateSuffix
30
31
31 -- * Transformation
32 -- * Transformation
32 , splitOn
33 , splitOn
@@ -49,8 +50,9 @@ import Streamly.Memory.Array.Types (Array(..), length)
49import Streamly.Streams.Serial (SerialT)
50import Streamly.Streams.Serial (SerialT)
50import Streamly.Streams.StreamK.Type (IsStream)
51import Streamly.Streams.StreamK.Type (IsStream)
51
52
53+import qualified Streamly.Unfold as UF
52import qualified Streamly.Memory.Array.Types as A
54import qualified Streamly.Memory.Array.Types as A
53−import qualified Streamly.Prelude as S
55+import qualified Streamly.Prelude.Internal as S
54import qualified Streamly.Streams.StreamD as D
56import qualified Streamly.Streams.StreamD as D
55import qualified Streamly.Streams.Prelude as P
57import qualified Streamly.Streams.Prelude as P
56
58
@@ -82,17 +84,20 @@ concat m = D.fromStreamD $ D.concatMapU A.readU (D.toStreamD m)
82concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
84concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
83concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
85concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
84
86
85−-- XXX use an Array instead as separator? Or use a separate unlinesArraysBySeq
87+{-# INLINE intercalateSuffix #-}
86−-- API for that?
88+intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
87−--
89+ => Array a -> t m (Array a) -> t m a
90+intercalateSuffix arr = S.intercalateSuffix A.readU arr A.readU
91+
88-- | Flatten a stream of arrays appending the given element after each
92-- | Flatten a stream of arrays appending the given element after each
89-- array.
93-- array.
90--
94--
91-- @since 0.7.0
95-- @since 0.7.0
92−{-# INLINE concatWithSuffix #-}
96+{-# INLINE interposeSuffix #-}
93−concatWithSuffix :: (MonadIO m, IsStream t, Storable a)
97+interposeSuffix :: (MonadIO m, IsStream t, Storable a)
94 => a -> t m (Array a) -> t m a
98 => a -> t m (Array a) -> t m a
95−concatWithSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
99+-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
100+interposeSuffix x = S.intercalateSuffix UF.singleton x A.readU
96
101
97-- | Split a stream of arrays on a given separator byte, dropping the separator
102-- | Split a stream of arrays on a given separator byte, dropping the separator
98-- and coalescing all the arrays between two separators into a single array.
103-- and coalescing all the arrays between two separators into a single array.
src/Streamly/Prelude/Internal.hs+18−11
@@ -245,8 +245,8 @@ module Streamly.Prelude.Internal
245 , concatUnfoldRoundrobin
245 , concatUnfoldRoundrobin
246 , concatMap
246 , concatMap
247 , concatMapWith
247 , concatMapWith
248− -- , interposeBy
249 -- , intercalate
248 -- , intercalate
249+ , intercalateSuffix
250
250
251 -- -- ** Breaking
251 -- -- ** Breaking
252
252
@@ -2210,22 +2210,29 @@ concatUnfoldRoundrobin u m =
2210 fromStreamD $ D.concatUnfoldRoundrobin u (toStreamD m)
2210 fromStreamD $ D.concatUnfoldRoundrobin u (toStreamD m)
2211
2211
2212{-
2212{-
2213−-- | A generalization of insertBy to inserting sequences.
2214−interposeBy :: (IsStream t, Monad m)
2215− => (a -> a -> Ordering) -> t m a -> t m a -> t m a
2216−interposeBy = undefined
2217−
2218-- | Insert a stream between segements of streams and flatten.
2213-- | Insert a stream between segements of streams and flatten.
2219intercalate :: (IsStream t, MonadAsync m)
2214intercalate :: (IsStream t, MonadAsync m)
2220 => SerialT Identity b -> (a -> t m b) -> t m a -> t m b
2215 => SerialT Identity b -> (a -> t m b) -> t m a -> t m b
2221intercalate = undefined
2216intercalate = undefined
2222−
2223−-- | Insert a stream after segements of streams and flatten.
2224−intercalatePost :: (IsStream t, MonadAsync m)
2225− => SerialT Identity b -> (a -> t m b) -> t m a -> t m b
2226−intercalatePost = undefined
2227-}
2217-}
2228
2218
2219+-- | @intercalateSuffix genSuffix seed gen stream@ generates streams from seeds
2220+-- in the input @stream@ using the generator @gen@ and concatenates them
2221+-- after suffixing the stream generated by @genSuffix@ using @seed@.
2222+--
2223+-- For example to insert CRLF in a stream of character strings:
2224+--
2225+-- > unlines = intercalateSuffix UF.fromList "\r\n" UF.fromList
2226+--
2227+{-# INLINE intercalateSuffix #-}
2228+intercalateSuffix
2229+ :: (IsStream t, Monad m)
2230+ => Unfold m a c -> a -> Unfold m b c -> t m b -> t m c
2231+intercalateSuffix suffix seed unf str =
2232+ D.fromStreamD $ D.interleaveFstThenConcat
2233+ unf (D.toStreamD str)
2234+ suffix (D.repeat seed)
2235+
2229------------------------------------------------------------------------------
2236------------------------------------------------------------------------------
2230-- Grouping/Splitting
2237-- Grouping/Splitting
2231------------------------------------------------------------------------------
2238------------------------------------------------------------------------------
src/Streamly/Streams/StreamD.hs+84
@@ -147,6 +147,7 @@ module Streamly.Streams.StreamD
147 , interleaveMin
147 , interleaveMin
148 , interleaveFst
148 , interleaveFst
149 , roundRobin -- interleaveFair?/ParallelFair
149 , roundRobin -- interleaveFair?/ParallelFair
150+ , interleaveFstThenConcat
150
151
151 -- ** Grouping
152 -- ** Grouping
152 , groupsOf
153 , groupsOf
@@ -2025,6 +2026,89 @@ roundRobin (Stream step1 state1) (Stream step2 state2) =
2025 Skip s -> Skip (InterleaveFirstOnly s)
2026 Skip s -> Skip (InterleaveFirstOnly s)
2026 Stop -> Stop
2027 Stop -> Stop
2027
2028
2029+data ICUState s1 s2 i1 i2 =
2030+ ICUFirst s1 s2
2031+ | ICUSecond s1 s2
2032+ | ICUSecondOnly s2
2033+ | ICUFirstOnly s1
2034+ | ICUFirstInner s1 s2 i1
2035+ | ICUSecondInner s1 s2 i2
2036+ | ICUFirstOnlyInner s1 i1
2037+ | ICUSecondOnlyInner s2 i2
2038+
2039+-- | Interleave streams (full streams, not the elements) unfolded from two
2040+-- input streams and concat. Stop when the first stream stops. If the second
2041+-- stream ends before the first one then first stream still keeps running alone
2042+-- without any interleaving with the second stream.
2043+--
2044+-- [a1, a2, ... an] [b1, b2 ...]
2045+-- => [streamA1, streamA2, ... streamAn] [streamB1, streamB2, ...]
2046+-- => [streamA1, streamB1, streamA2...StreamAn, streamBn]
2047+-- => [a11, a12, ...a1j, b11, b12, ...b1k, a21, a22, ...]
2048+--
2049+{-# INLINE_NORMAL interleaveFstThenConcat #-}
2050+interleaveFstThenConcat
2051+ :: Monad m
2052+ => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
2053+interleaveFstThenConcat
2054+ (Unfold istep1 inject1) (Stream step1 state1)
2055+ (Unfold istep2 inject2) (Stream step2 state2) =
2056+ Stream step (ICUFirst state1 state2)
2057+
2058+ where
2059+
2060+ {-# INLINE_LATE step #-}
2061+ step gst (ICUFirst s1 s2) = do
2062+ r <- step1 (adaptState gst) s1
2063+ case r of
2064+ Yield a s -> do
2065+ i <- inject1 a
2066+ i `seq` return (Skip (ICUFirstInner s s2 i))
2067+ Skip s -> return $ Skip (ICUFirst s s2)
2068+ Stop -> return Stop
2069+
2070+ step gst (ICUFirstOnly s1) = do
2071+ r <- step1 (adaptState gst) s1
2072+ case r of
2073+ Yield a s -> do
2074+ i <- inject1 a
2075+ i `seq` return (Skip (ICUFirstOnlyInner s i))
2076+ Skip s -> return $ Skip (ICUFirstOnly s)
2077+ Stop -> return Stop
2078+
2079+ step _ (ICUFirstInner s1 s2 i1) = do
2080+ r <- istep1 i1
2081+ return $ case r of
2082+ Yield x i' -> Yield x (ICUFirstInner s1 s2 i')
2083+ Skip i' -> Skip (ICUFirstInner s1 s2 i')
2084+ Stop -> Skip (ICUSecond s1 s2)
2085+
2086+ step _ (ICUFirstOnlyInner s1 i1) = do
2087+ r <- istep1 i1
2088+ return $ case r of
2089+ Yield x i' -> Yield x (ICUFirstOnlyInner s1 i')
2090+ Skip i' -> Skip (ICUFirstOnlyInner s1 i')
2091+ Stop -> Skip (ICUFirstOnly s1)
2092+
2093+ step gst (ICUSecond s1 s2) = do
2094+ r <- step2 (adaptState gst) s2
2095+ case r of
2096+ Yield a s -> do
2097+ i <- inject2 a
2098+ i `seq` return (Skip (ICUSecondInner s1 s i))
2099+ Skip s -> return $ Skip (ICUSecond s1 s)
2100+ Stop -> return $ Skip (ICUFirstOnly s1)
2101+
2102+ step _ (ICUSecondInner s1 s2 i2) = do
2103+ r <- istep2 i2
2104+ return $ case r of
2105+ Yield x i' -> Yield x (ICUSecondInner s1 s2 i')
2106+ Skip i' -> Skip (ICUSecondInner s1 s2 i')
2107+ Stop -> Skip (ICUFirst s1 s2)
2108+
2109+ step _ (ICUSecondOnly _s2) = undefined
2110+ step _ (ICUSecondOnlyInner _s2 _i2) = undefined
2111+
2028------------------------------------------------------------------------------
2112------------------------------------------------------------------------------
2029-- Exceptions
2113-- Exceptions
2030------------------------------------------------------------------------------
2114------------------------------------------------------------------------------
src/Streamly/Unfold.hs+31
@@ -57,7 +57,9 @@ module Streamly.Unfold
57 , unfold
57 , unfold
58
58
59 -- ** Unfolds
59 -- ** Unfolds
60+ , singleton
60 , replicateM
61 , replicateM
62+ , fromList
61 )
63 )
62where
64where
63
65
@@ -71,6 +73,8 @@ import Streamly.Unfold.Types (Unfold(..))
71-- Running unfolds
73-- Running unfolds
72-------------------------------------------------------------------------------
74-------------------------------------------------------------------------------
73
75
76+-- | Convert an 'Unfold' into a 'Stream' by supplying it a seed.
77+--
74{-# INLINE_NORMAL unfold #-}
78{-# INLINE_NORMAL unfold #-}
75unfold :: Monad m => a -> Unfold m a b -> Stream m b
79unfold :: Monad m => a -> Unfold m a b -> Stream m b
76unfold seed (Unfold ustep inject) = Stream step Nothing
80unfold seed (Unfold ustep inject) = Stream step Nothing
@@ -88,12 +92,39 @@ unfold seed (Unfold ustep inject) = Stream step Nothing
88-- Unfolds
92-- Unfolds
89-------------------------------------------------------------------------------
93-------------------------------------------------------------------------------
90
94
95+-- | Identity unfold. Generates a singleton stream with the seed as the only
96+-- element in the stream.
97+--
98+-- > singleton = replicateM 1
99+--
100+{-# INLINE singleton #-}
101+singleton :: Monad m => Unfold m a a
102+singleton = Unfold step inject
103+ where
104+ inject x = return $ Just x
105+ {-# INLINE_LATE step #-}
106+ step (Just x) = return $ Yield x Nothing
107+ step Nothing = return Stop
108+
109+-- | Generates a stream replicating the seed @n@ times.
110+--
91{-# INLINE replicateM #-}
111{-# INLINE replicateM #-}
92replicateM :: Monad m => Int -> Unfold m a a
112replicateM :: Monad m => Int -> Unfold m a a
93replicateM n = Unfold step inject
113replicateM n = Unfold step inject
94 where
114 where
95 inject x = return (x, n)
115 inject x = return (x, n)
116+ {-# INLINE_LATE step #-}
96 step (x, i) = return $
117 step (x, i) = return $
97 if i <= 0
118 if i <= 0
98 then Stop
119 then Stop
99 else Yield x (x, (i - 1))
120 else Yield x (x, (i - 1))
121+
122+-- | Convert a list of pure values to a 'Stream'
123+{-# INLINE_LATE fromList #-}
124+fromList :: Monad m => Unfold m [a] a
125+fromList = Unfold step inject
126+ where
127+ inject x = return x
128+ {-# INLINE_LATE step #-}
129+ step (x:xs) = return $ Yield x xs
130+ step [] = return Stop