on master help sign in
rename/reorg/remove some fold APIs

rename groupsOf to chunksOf remove grouped as it is just another version of splitSuffixBy' reorganize sections Add some docs/notes

commit50232af0002fef5f5e6af786295f35fd52b6c987 authorHarendra Kumar <harendra.kumar@gmail.com> dateMon 20 May 2019, 00:46 (7 years ago) parent5798787
6 files changed+111−166
benchmark/FileIO.hs+8−8 benchmark/NanoBenchmarks.hs+1−1 src/Streamly/Fold.hs+87−88 src/Streamly/Prelude.hs+1 src/Streamly/Streams/StreamD.hs+9−13 src/Streamly/Time.hs+5−56
benchmark/FileIO.hs+8−8
@@ -134,20 +134,20 @@ main = do
134134 File.write outh (File.read inh)
135135 ]
136136 , bgroup "grouping"
137− [ mkBench "groupsOf 1 (toArray)" href $ do
137+ [ mkBench "chunksOf 1 (toArray)" href $ do
138138 Handles inh _ <- readIORef href
139− S.length $ FL.groupsOf fileSize (A.toArrayN fileSize)
139+ S.length $ FL.chunksOf fileSize (A.toArrayN fileSize)
140140 (File.read inh)
141141
142− , mkBench "groupsOf 1" href $ do
142+ , mkBench "chunksOf 1" href $ do
143143 Handles inh _ <- readIORef href
144− S.length $ FL.groupsOf 1 FL.drain (File.read inh)
145− , mkBench "groupsOf 10" href $ do
144+ S.length $ FL.chunksOf 1 FL.drain (File.read inh)
145+ , mkBench "chunksOf 10" href $ do
146146 Handles inh _ <- readIORef href
147− S.length $ FL.groupsOf 10 FL.drain (File.read inh)
148− , mkBench "groupsOf 1000" href $ do
147+ S.length $ FL.chunksOf 10 FL.drain (File.read inh)
148+ , mkBench "chunksOf 1000" href $ do
149149 Handles inh _ <- readIORef href
150− S.length $ FL.groupsOf 1000 FL.drain (File.read inh)
150+ S.length $ FL.chunksOf 1000 FL.drain (File.read inh)
151151 ]
152152
153153 , let lf = fromIntegral (ord '\n')
benchmark/NanoBenchmarks.hs+1−1
@@ -9,7 +9,7 @@
99import Streamly (SerialT)
1010import Streamly.SVar (MonadAsync)
1111
12−import qualified Streamly.Array as A
12+import qualified Streamly.Mem.Array as A
1313import qualified Streamly.FileSystem.File as File
1414import qualified Streamly.Fold as FL
1515import qualified Streamly.Prelude as S
src/Streamly/Fold.hs+87−88
@@ -146,16 +146,10 @@ module Streamly.Fold
146146 -- ** Filtering
147147 , lfilter
148148 , lfilterM
149− , ltake
150− , ltakeWhile
151− {-
152− , ltakeWhileM
153− , ldrop
154− , ldropWhile
155− , ldropWhileM
156− , ldeleteBy
157− , luniq
149+ -- , ldeleteBy
150+ -- , luniq
158151
152+ {-
159153 -- ** Mapping Filters
160154 , lmapMaybe
161155 , lmapMaybeM
@@ -174,23 +168,36 @@ module Streamly.Fold
174168 , lreverse
175169 -}
176170
177− -- * Stream Splitting Operations
178− -- ** Spanning
179− -- | Spanning splits the input into two groups and applies two different
180− -- folds on each group.
171+ -- * Parsing
172+ -- ** Trimming
173+ , ltake
174+ -- , lrunFor -- time
175+ , ltakeWhile
176+ {-
177+ , ltakeWhileM
178+ , ldrop
179+ , ldropWhile
180+ , ldropWhileM
181+ -}
181182
182− -- Element unaware spanning
183+ -- ** Breaking
184+
185+ -- By chunks
183186 , splitAt
187+ -- , splitIn -- time
184188
185− -- Element aware spanning
189+ -- By elements
186190 , span
187− , break
191+ , break -- breakPre
192+ -- , breakPost
193+ -- , breakOn
188194 -- , spanBy
189195 -- , spanRollingBy
190196
191− -- ** Grouping
192− -- | Grouping splits the stream into groups and applies the supplied fold
193− -- on each group.
197+ -- By sequences
198+ -- breakOnSeq
199+
200+ -- ** Splitting
194201
195202 -- In imperative terms grouped folding can be considered as a nested loop
196203 -- where we loop over the stream to group elements and then loop over
@@ -205,23 +212,17 @@ module Streamly.Fold
205212 -- groups in memory and then map a fold on it to fold the groups. This kind
206213 -- of grouping and folding would not work well when the group size is big.
207214
208− -- Element unaware grouping
209− , groupsOf
210− , intervalsOf
215+ -- *** By Chunks
216+ , chunksOf
217+ , spellsOf
211218
212− -- Element aware grouping
213− , groups
214− , groupsBy
215− , groupsRollingBy
216− , grouped -- XXX experimental
217−
218− -- ** Splitting by an Element
219+ -- *** By Elements
219220 , splitBy
220221 , splitSuffixBy
221222 -- , splitPrefixBy
222223 , wordsBy
223224
224− -- ** Splitting on a Sequence
225+ -- *** By Sequences
225226 , splitOn
226227 , splitSuffixOn
227228 -- , splitPrefixOn
@@ -237,7 +238,12 @@ module Streamly.Fold
237238 -- , splitSuffixOnAny
238239 -- , splitPrefixOnAny
239240
240− -- ** Distributing
241+ -- ** Grouping
242+ , groups
243+ , groupsBy
244+ , groupsRollingBy
245+
246+ -- * Distributing
241247 -- |
242248 -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
243249 -- of the stream to each fold and combines the results using a function.
@@ -271,7 +277,7 @@ module Streamly.Fold
271277 , tee
272278 , distribute
273279
274− -- ** Partitioning
280+ -- * Partitioning
275281 -- |
276282 -- Direct items in the input stream to different folds using a function to
277283 -- select the fold. This is useful to demultiplex the input stream.
@@ -280,21 +286,15 @@ module Streamly.Fold
280286 , demux_
281287 , classify
282288
283− -- ** Unzipping
289+ -- * Unzipping
284290 , unzip
285291 , unzipWith
286292 , unzipWithM
287293
288− -- ** Nested Folds
294+ -- * Nested Folds
289295 -- , concatMap
290− -- , groupsOf
296+ -- , chunksOf
291297 , duplicate -- experimental
292−
293− {-
294− -- * Splitter scans
295− -- | Scans that can be used to split and fold a stream using 'grouped'.
296− , _newline
297− -}
298298 )
299299where
300300
@@ -303,7 +303,7 @@ import Control.Monad (void)
303303import Control.Monad.IO.Class (MonadIO(..))
304304import Data.Functor.Identity (Identity)
305305import Data.Map.Strict (Map)
306−import Data.Maybe (fromJust, isJust)
306+import Data.Maybe (fromJust, isJust, isNothing)
307307
308308import Foreign.Storable (Storable(..))
309309import Prelude
@@ -970,6 +970,9 @@ ltakeWhile predicate (Fold step initial done) = Fold step' initial' done'
970970-- >>> splitAt_ 4 [1,2,3]
971971-- > ([1,2,3],[])
972972--
973+-- This can be considered as a two-fold version of 'ltake' where we take both
974+-- the segments instead of discarding the leftover.
975+--
973976-- @since 0.7.0
974977{-# INLINE splitAt #-}
975978splitAt
@@ -997,39 +1000,43 @@ splitAt n (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
9971000-- | Group the input stream into groups of @n@ elements each and then fold each
9981001-- group using the provided fold function.
9991002--
1000−-- >> S.toList $ FL.groupsOf 2 FL.sum (S.enumerateFromTo 1 10)
1003+-- >> S.toList $ FL.chunksOf 2 FL.sum (S.enumerateFromTo 1 10)
10011004-- > [3,7,11,15,19]
10021005--
1006+-- This can be considered as an n-fold version of 'ltake' where we apply
1007+-- 'ltake' repeatedly on the leftover stream until the stream exhausts.
1008+--
10031009-- @since 0.7.0
1004−{-# INLINE groupsOf #-}
1005−groupsOf
1010+{-# INLINE chunksOf #-}
1011+chunksOf
10061012 :: (IsStream t, Monad m)
10071013 => Int -> Fold m a b -> t m a -> t m b
1008−groupsOf n f m = D.fromStreamD $ D.groupsOf n f (D.toStreamD m)
1014+chunksOf n f m = D.fromStreamD $ D.groupsOf n f (D.toStreamD m)
10091015
10101016-- | Transform a fold from a pure input to a 'Maybe' input, consuming only
10111017-- 'Just' values.
10121018lcatMaybes :: Monad m => Fold m a b -> Fold m (Maybe a) b
10131019lcatMaybes = lfilter isJust . lmap fromJust
10141020
1021+-- XXX we can implement this by repeatedly applying the 'lrunFor' fold.
10151022-- XXX add this example after fixing the serial stream rate control
1016−-- >>> S.toList $ S.take 5 $ intervalsOf 1 FL.sum $ constRate 2 $ S.enumerateFrom 1
1023+-- >>> S.toList $ S.take 5 $ spellsOf 1 FL.sum $ constRate 2 $ S.enumerateFrom 1
10171024-- > [3,7,11,15,19]
10181025--
10191026-- | Group the input stream into windows of @n@ second each and then fold each
10201027-- group using the provided fold function.
10211028--
10221029-- @since 0.7.0
1023−{-# INLINE intervalsOf #-}
1024−intervalsOf
1030+{-# INLINE spellsOf #-}
1031+spellsOf
10251032 :: (IsStream t, MonadAsync m)
10261033 => Double -> Fold m a b -> t m a -> t m b
1027−intervalsOf n f m = grouped (lcatMaybes f) s
1034+spellsOf n f m = splitSuffixBy' isNothing (lcatMaybes f) s
10281035 where
1029− s = S.map (\x -> (Just x, False)) m `parallel` S.repeatM timeout
1036+ s = S.map Just m `parallel` S.repeatM timeout
10301037 timeout = do
10311038 liftIO $ threadDelay (round $ n * 1000000)
1032− return (Nothing, True)
1039+ return Nothing
10331040
10341041------------------------------------------------------------------------------
10351042-- Element Aware APIs
@@ -1039,18 +1046,17 @@ intervalsOf n f m = grouped (lcatMaybes f) s
10391046-- Binary APIs
10401047------------------------------------------------------------------------------
10411048
1042−{-
10431049-- | Break the input stream into two groups, the first group takes the input as
10441050-- long as the predicate applied to the first element of the stream and next
10451051-- input element holds 'True', the second group takes the rest of the input.
10461052--
1047−spanBy
1053+_spanBy
10481054 :: Monad m
10491055 => (a -> a -> Bool)
10501056 -> Fold m a b
10511057 -> Fold m a c
10521058 -> Fold m a (b, c)
1053−spanBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
1059+_spanBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
10541060 Fold step init extract
10551061
10561062 where
@@ -1071,7 +1077,6 @@ spanBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
10711077 >>= (\a' -> return (Tuple3' a a' (Tuple' Nothing False)))
10721078
10731079 extract (Tuple3' a b _) = (,) <$> extractL a <*> extractR b
1074−-}
10751080
10761081-- | Span as long as the predicate is 'True'. @span p f1 f2@ composes folds
10771082-- @f1@ and @f2@ such that the composed fold continues sending the input to
@@ -1089,6 +1094,9 @@ spanBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
10891094-- >>> span_ (< 4) [1,2,3]
10901095-- > ([1,2,3],[])
10911096--
1097+-- This can be considered as a two-fold version of 'ltakeWhile' where we take
1098+-- both the segments instead of discarding the leftover.
1099+--
10921100-- @since 0.7.0
10931101{-# INLINE span #-}
10941102span
@@ -1117,6 +1125,8 @@ span p (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
11171125-- folds @f1@ and @f2@ such that @f1@ stops receiving input as soon as the
11181126-- predicate @p@ becomes 'True'. The rest of the input is sent to @f2@.
11191127--
1128+-- This is the binary version of 'splitBy'.
1129+--
11201130-- > let break_ p xs = FL.foldl' (FL.break p FL.toList FL.toList) $ S.fromList xs
11211131--
11221132-- >>> break_ (< 1) [3,2,1]
@@ -1138,17 +1148,16 @@ break
11381148 -> Fold m a (b, c)
11391149break p = span (not . p)
11401150
1141−{-
11421151-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.
11431152-- predicate is applied to the previous and the next input elements.
1144−{-# INLINE spanRollingBy #-}
1145−spanRollingBy
1153+{-# INLINE _spanRollingBy #-}
1154+_spanRollingBy
11461155 :: Monad m
11471156 => (a -> a -> Bool)
11481157 -> Fold m a b
11491158 -> Fold m a c
11501159 -> Fold m a (b, c)
1151−spanRollingBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
1160+_spanRollingBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
11521161 Fold step init extract
11531162
11541163 where
@@ -1163,33 +1172,11 @@ spanRollingBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR)
11631172 stepL a input >>= (\a' -> return (Tuple3' a' b (Just input)))
11641173
11651174 extract (Tuple3' a b _) = (,) <$> extractL a <*> extractR b
1166−-}
11671175
11681176------------------------------------------------------------------------------
11691177-- N-ary APIs
11701178------------------------------------------------------------------------------
11711179--
1172−-- XXX should we use a strict pair?
1173−--
1174−-- | The splitter returns True if the current element is the last element of
1175−-- the group, otherwise returns false.
1176−--
1177−-- @since 0.7.0
1178−{-# INLINE grouped #-}
1179−grouped
1180− :: (IsStream t, Monad m)
1181− => Fold m a b
1182− -> t m (a, Bool)
1183− -> t m b
1184−grouped f m = D.fromStreamD $ D.grouped f (D.toStreamD m)
1185−
1186−-- An example of a splitter to be used with grouped.
1187−_newline :: IsStream t => t m Char -> t m (Char,Bool)
1188−_newline m = S.foldrS (\x xs ->
1189− if x == '\n'
1190− then (x,True) `K.cons` xs
1191− else (x,False) `K.cons` xs) K.nil m
1192−
11931180-- | @groupsBy cmp f $ S.fromList [a,b,c,...]@ assigns the element @a@ to the
11941181-- first group, if @a \`cmp` b@ is 'True' then @b@ is also assigned to the same
11951182-- group. If @a \`cmp` c@ is 'True' then @c@ is also assigned to the same
@@ -1292,6 +1279,10 @@ breakOn pat f m = undefined
12921279-- >>> splitBy_ (== '.') "a..b"
12931280-- > ["a","","b"]
12941281--
1282+-- This can be considered as an n-fold version of 'break' where we apply
1283+-- 'break' successively on the input stream, dropping the first element
1284+-- of the second segment after each break.
1285+--
12951286-- @since 0.7.0
12961287{-# INLINE splitBy #-}
12971288splitBy
@@ -1334,6 +1325,10 @@ splitBy predicate f m =
13341325--
13351326-- > lines = splitSuffixBy (== '\n')
13361327--
1328+-- This can be considered as an n-fold version of 'breakPost' where we apply
1329+-- 'breakPost' successively on the input stream, dropping the first element
1330+-- of the second segment after each break.
1331+--
13371332-- @since 0.7.0
13381333{-# INLINE splitSuffixBy #-}
13391334splitSuffixBy
@@ -1402,15 +1397,19 @@ wordsBy predicate f m =
14021397-- >>> splitSuffixBy'_ (== '.') "a..b.."
14031398-- > ["a.",".","b.","."]
14041399--
1400+-- This can be considered as an n-fold version of 'breakPost' where we apply
1401+-- 'breakPost' successively on the input stream.
1402+--
14051403-- @since 0.7.0
1406−{-# INLINE _splitSuffixBy' #-}
1407−_splitSuffixBy'
1404+{-# INLINE splitSuffixBy' #-}
1405+splitSuffixBy'
14081406 :: (IsStream t, Monad m)
14091407 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1410−_splitSuffixBy' predicate f m = grouped f (S.map (\a -> (a, predicate a)) m)
1408+splitSuffixBy' predicate f m =
1409+ D.fromStreamD $ D.splitSuffixBy' predicate f (D.toStreamD m)
14111410
14121411------------------------------------------------------------------------------
1413−-- Split on a delimiter
1412+-- Split on a delimiter sequence
14141413------------------------------------------------------------------------------
14151414
14161415-- Int list examples for splitOn:
@@ -1986,6 +1985,6 @@ lconcatMap s f1 f2 = undefined
19861985-- -----Fold m a b----|-Fold n a c-|-Fold n a c-|-...-|----Fold m a c
19871986--
19881987-- @
1989−lgroupsOf :: Int -> Fold m a b -> Fold m b c -> Fold m a c
1990−lgroupsOf n f1 f2 = undefined
1988+lchunksOf :: Int -> Fold m a b -> Fold m b c -> Fold m a c
1989+lchunksOf n f1 f2 = undefined
19911990-}
src/Streamly/Prelude.hs+1
@@ -358,6 +358,7 @@ module Streamly.Prelude
358358
359359 , findIndices
360360 , elemIndices
361+ -- , seqIndices -- search a sequence in the stream
361362
362363 -- ** Insertion
363364 -- | Insertion adds more elements to the stream.
src/Streamly/Streams/StreamD.hs+9−13
@@ -131,7 +131,6 @@ module Streamly.Streams.StreamD
131131
132132 -- ** Grouping
133133 , groupsOf
134− , grouped
135134 , groupsBy
136135 , groupsRollingBy
137136
@@ -922,9 +921,11 @@ concatMap f = concatMapM (return . f)
922921-- Grouping/Splitting
923922------------------------------------------------------------------------------
924923
925−{-# INLINE_NORMAL grouped #-}
926−grouped :: Monad m => Fold m a b -> Stream m (a, Bool) -> Stream m b
927−grouped f (Stream step state) = Stream (stepOuter f) (Just state)
924+{-# INLINE_NORMAL splitSuffixBy' #-}
925+splitSuffixBy' :: Monad m
926+ => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
927+splitSuffixBy' predicate f (Stream step state) =
928+ Stream (stepOuter f) (Just state)
928929
929930 where
930931
@@ -932,10 +933,10 @@ grouped f (Stream step state) = Stream (stepOuter f) (Just state)
932933 stepOuter (Fold fstep initial done) gst (Just st) = do
933934 res <- step (adaptState gst) st
934935 case res of
935− Yield (x,r) s -> do
936+ Yield x s -> do
936937 acc <- initial
937938 acc' <- fstep acc x
938− if r
939+ if (predicate x)
939940 then done acc' >>= \val -> return $ Yield val (Just s)
940941 else go SPEC s acc'
941942
@@ -947,9 +948,9 @@ grouped f (Stream step state) = Stream (stepOuter f) (Just state)
947948 go !_ stt !acc = do
948949 res <- step (adaptState gst) stt
949950 case res of
950− Yield (x,r) s -> do
951+ Yield x s -> do
951952 acc' <- fstep acc x
952− if r
953+ if (predicate x)
953954 then done acc' >>= \val -> return $ Yield val (Just s)
954955 else go SPEC s acc'
955956 Skip s -> go SPEC s acc
@@ -1071,11 +1072,6 @@ groupsRollingBy cmp f (Stream step state) =
10711072
10721073 stepOuter _ _ (Nothing, _) = return Stop
10731074
1074−{-# INLINE_EARLY splitSuffixBy' #-}
1075−splitSuffixBy' :: Monad m
1076− => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
1077−splitSuffixBy' predicate f m = grouped f (map (\a -> (a, predicate a)) m)
1078−
10791075{-# INLINE_NORMAL splitBy #-}
10801076splitBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
10811077splitBy predicate f (Stream step state) = Stream (step' f) (Just state)
src/Streamly/Time.hs+5−56
@@ -1,5 +1,3 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−
31-- |
42-- Module : Streamly.Time
53-- Copyright : (c) 2017 Harendra Kumar
@@ -12,26 +10,16 @@
1210-- Time utilities for reactive programming.
1311
1412module Streamly.Time
15− ( intervalsOf
16−
17− -- * Deprecated
18− , periodic
13+{-# DEPRECATED
14+ "Please use the \"rate\" combinator instead of the functions in this module"
15+ #-}
16+ ( periodic
1917 , withClock
2018 )
2119where
2220
23−import Control.Concurrent (threadDelay)
2421import Control.Monad (when)
25−import Control.Monad.IO.Class (MonadIO(liftIO))
26−import Data.Maybe (fromJust, isJust)
27−
28−import Streamly (parallel)
29−import Streamly.SVar (MonadAsync)
30−import Streamly.Streams.StreamK (IsStream(..))
31−import Streamly.Fold (Fold)
32−
33−import qualified Streamly.Fold as FL
34−import qualified Streamly.Prelude as S
22+import Control.Concurrent (threadDelay)
3523
3624-- | Run an action forever periodically at the given frequency specified in per
3725-- second (Hz).
@@ -84,42 +72,3 @@ withClock clock freq action = do
8472 lastPeriod = (baseTime - lastAdj) `div` freq
8573 newDelay = max 0 (delay + period - lastPeriod)
8674 return (baseTime, newTick, newDelay)
87−
88−-------------------------------------------------------------------------------
89−-- Stream element unaware: Time based buffering
90−-------------------------------------------------------------------------------
91−
92−-- There are two ways in which time based buffering can be implemented.
93−--
94−-- 1) One is to use an API like groupsOf to group elements by time and then
95−-- flush when the group is complete. This will require checking time on each
96−-- element yield.
97−--
98−-- 2) Use an async thread to write. The producer thread would just queue the
99−-- elements to be written on a queue associated with the writer thread. When
100−-- the write thread is scheduled elements that have been buffered will be
101−-- written based on a buffering policy. This will require the producer to
102−-- synchronize on the queue, send a doorbell when the queue has items in it.
103−-- The queue can be an array which can be written directly to the IO device.
104−--
105−-- We can try both and see which one performs better.
106−
107−-- XXX add this example after fixing the serial stream rate control
108−-- >>> S.toList $ S.take 5 $ intervalsOf 1 FL.sum $ constRate 2 $ S.enumerateFrom 1
109−-- > [3,7,11,15,19]
110−--
111−-- | Group the input stream into windows of @n@ second each and then fold each
112−-- group using the provided fold function.
113−--
114−-- @since 0.7.0
115−{-# INLINE intervalsOf #-}
116−intervalsOf
117− :: (IsStream t, MonadAsync m)
118− => Double -> Fold m a b -> t m a -> t m b
119−intervalsOf n f m = FL.grouped (catMaybes f) s
120− where
121− s = S.map (\x -> (Just x, False)) m `parallel` S.repeatM timeout
122− timeout = do
123− liftIO $ threadDelay (round $ n * 1000000)
124− return (Nothing, True)
125− catMaybes = FL.lfilter isJust . FL.lmap fromJust