@@ -12,27 +12,38 @@ module Streamly.Internal.Data.Fold.Container
1212 -- * Imports
1313 -- $setup
1414
15− countDistinct
15+ -- * Set operations
16+ toSet
17+ , toIntSet
18+ , countDistinct
1619 , countDistinctInt
17− , frequency
18− , toMap
1920 , nub
2021 , nubInt
2122
23+ -- * Map operations
24+ , frequency
25+
2226 -- ** Demultiplexing
2327 -- | Direct values in the input stream to different folds using an n-ary
2428 -- fold selector. 'demux' is a generalization of 'classify' (and
2529 -- 'partition') where each key of the classifier can use a different fold.
30+ , demuxKvToContainer
31+ , demuxKvToMap
32+
33+ , demuxToContainer
34+ , demuxToContainerIO
35+ , demuxToMap
36+ , demuxToMapIO
37+
38+ , demuxGeneric
2639 , demux
27− , demuxWith
28− , demuxScanWith
29− , demuxScanMutWith
30− , demuxMutWith
40+ , demuxGenericIO
41+ , demuxIO
3142
3243 -- TODO: These can be implemented using the above operations
33− -- , demuxWithSel -- Stop when the fold for the specified key stops
34− -- , demuxWithMin -- Stop when any of the folds stop
35− -- , demuxWithAll -- Stop when all the folds stop (run once)
44+ -- , demuxSel -- Stop when the fold for the specified key stops
45+ -- , demuxMin -- Stop when any of the folds stop
46+ -- , demuxAll -- Stop when all the folds stop (run once)
3647
3748 -- ** Classifying
3849 -- | In an input stream of key value pairs fold values for different keys
@@ -47,12 +58,19 @@ module Streamly.Internal.Data.Fold.Container
4758 -- and their meanings then trie Map would be better if we also want to
4859 -- display them in sorted order.
4960
61+ , kvToMap
62+
63+ , toContainer
64+ , toContainerIO
65+ , toMap
66+ , toMapIO
67+
68+ , classifyGeneric
5069 , classify
51− , classifyWith
52− , classifyMutWith
53− , classifyScanWith
54− -- , classifyWithSel
55− -- , classifyWithMin
70+ , classifyGenericIO
71+ , classifyIO
72+ -- , toContainerSel
73+ -- , toContainerMin
5674 )
5775where
5876
@@ -62,29 +80,48 @@ where
6280import Control.Monad.IO.Class (MonadIO(..))
6381import Data.IORef (newIORef, readIORef, writeIORef)
6482import Data.Map.Strict (Map)
83+import Data.IntSet (IntSet)
84+import Data.Set (Set)
6585import Streamly.Internal.Data.IsMap (IsMap(..))
6686import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
6787
6888import qualified Data.IntSet as IntSet
6989import qualified Data.Set as Set
70−import qualified Prelude
7190import qualified Streamly.Internal.Data.IsMap as IsMap
7291
73−import Prelude hiding
74− ( filter, foldl1, drop, dropWhile, take, takeWhile, zipWith
75− , foldl, foldr, map, mapM_, sequence, all, any, sum, product, elem
76− , notElem, maximum, minimum, head, last, tail, length, null
77− , reverse, iterate, init, and, or, lookup, (!!)
78− , scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip
79− , span, splitAt, break, mapM, zip, maybe)
92+import Prelude hiding (length)
8093import Streamly.Internal.Data.Fold
8194
8295-- $setup
8396-- >>> :m
8497-- >>> :set -XFlexibleContexts
98+-- >>> import qualified Data.Map as Map
99+-- >>> import qualified Data.Set as Set
100+-- >>> import qualified Data.IntSet as IntSet
85101-- >>> import qualified Streamly.Data.Fold as Fold
86102-- >>> import qualified Streamly.Data.Stream as Stream
87−-- >>> import qualified Streamly.Internal.Data.Fold.Extra as Fold
103+-- >>> import qualified Streamly.Internal.Data.Fold.Container as Fold
104+
105+-- | Fold the input to a set.
106+--
107+-- Definition:
108+--
109+-- >>> toSet = Fold.foldl' (flip Set.insert) Set.empty
110+--
111+{-# INLINE toSet #-}
112+toSet :: (Monad m, Ord a) => Fold m a (Set a)
113+toSet = foldl' (flip Set.insert) Set.empty
114+
115+-- | Fold the input to an int set. For integer inputs this performs better than
116+-- 'toSet'.
117+--
118+-- Definition:
119+--
120+-- >>> toIntSet = Fold.foldl' (flip IntSet.insert) IntSet.empty
121+--
122+{-# INLINE toIntSet #-}
123+toIntSet :: Monad m => Fold m Int IntSet
124+toIntSet = foldl' (flip IntSet.insert) IntSet.empty
88125
89126-- XXX Name as nubOrd? Or write a nubGeneric
90127
@@ -113,10 +150,6 @@ nub = fmap (\(Tuple' _ x) -> x) $ foldl' step initial
113150
114151-- | Like 'nub' but specialized to a stream of 'Int', for better performance.
115152--
116−-- Definition:
117−--
118−-- >>> nubInt = Fold.nub
119−--
120153-- /Pre-release/
121154{-# INLINE nubInt #-}
122155nubInt :: Monad m => Fold m Int (Maybe Int)
@@ -139,6 +172,7 @@ nubInt = fmap (\(Tuple' _ x) -> x) $ foldl' step initial
139172--
140173-- Definition:
141174--
175+-- >>> countDistinct = fmap Set.size Fold.toSet
142176-- >>> countDistinct = Fold.postscan Fold.nub $ Fold.catMaybes $ Fold.length
143177--
144178-- The memory used is proportional to the number of distinct elements in the
@@ -151,7 +185,8 @@ nubInt = fmap (\(Tuple' _ x) -> x) $ foldl' step initial
151185--
152186{-# INLINE countDistinct #-}
153187countDistinct :: (Monad m, Ord a) => Fold m a Int
154−countDistinct = postscan nub $ catMaybes length
188+-- countDistinct = postscan nub $ catMaybes length
189+countDistinct = fmap Set.size toSet
155190{-
156191countDistinct = fmap (\(Tuple' _ n) -> n) $ foldl' step initial
157192
@@ -173,12 +208,14 @@ countDistinct = fmap (\(Tuple' _ n) -> n) $ foldl' step initial
173208--
174209-- Definition:
175210--
211+-- >>> countDistinctInt = fmap IntSet.size Fold.toIntSet
176212-- >>> countDistinctInt = Fold.postscan Fold.nubInt $ Fold.catMaybes $ Fold.length
177213--
178214-- /Pre-release/
179215{-# INLINE countDistinctInt #-}
180216countDistinctInt :: Monad m => Fold m Int Int
181−countDistinctInt = postscan nubInt $ catMaybes length
217+-- countDistinctInt = postscan nubInt $ catMaybes length
218+countDistinctInt = fmap IntSet.size toIntSet
182219{-
183220countDistinctInt = fmap (\(Tuple' _ n) -> n) $ foldl' step initial
184221
@@ -214,23 +251,12 @@ countDistinctInt = fmap (\(Tuple' _ n) -> n) $ foldl' step initial
214251-- because key is to be determined on each input whereas fold is to be
215252-- determined only once for a key.
216253
217−-- | In a key value stream, fold values corresponding to each key with a key
218−-- specific fold. The fold returns the fold result as the second component of
219−-- the output tuple whenever a fold terminates. The first component of the
220−-- tuple is a container of in-progress folds. If a fold terminates, another
221−-- instance of the fold is started upon receiving an input with that key.
222−--
223−-- This can be used to scan a stream and collect the results from the scan
224−-- output.
225−--
226−-- /Pre-release/
227−--
228−{-# INLINE demuxScanWith #-}
229−demuxScanWith :: (Monad m, IsMap f, Traversable f) =>
254+{-# INLINE demuxGeneric #-}
255+demuxGeneric :: (Monad m, IsMap f, Traversable f) =>
230256 (a -> Key f)
231257 -> (a -> m (Fold m a b))
232258 -> Fold m a (m (f b), Maybe (Key f, b))
233−demuxScanWith getKey getFold = fmap extract $ foldlM' step initial
259+demuxGeneric getKey getFold = fmap extract $ foldlM' step initial
234260
235261 where
236262
@@ -268,19 +294,30 @@ demuxScanWith getKey getFold = fmap extract $ foldlM' step initial
268294 Partial s -> e s
269295 Done b -> return b
270296
271−-- | This is specialized version of 'demuxScanWith' that uses mutable cells as
272−-- fold accumulators for better performance.
297+-- | In a key value stream, fold values corresponding to each key with a key
298+-- specific fold. The fold returns the fold result as the second component of
299+-- the output tuple whenever a fold terminates. The first component of the
300+-- tuple is a Map of in-progress folds. If a fold terminates, another
301+-- instance of the fold is started upon receiving an input with that key.
273302--
274−-- Definition:
303+-- This can be used to scan a stream and collect the results from the scan
304+-- output.
275305--
276−-- >>> demuxScanMutWith = Fold.demuxScanWith
306+-- /Pre-release/
277307--
278−{-# INLINE demuxScanMutWith #-}
279−demuxScanMutWith :: (MonadIO m, IsMap f, Traversable f) =>
308+{-# INLINE demux #-}
309+demux :: (Monad m, Ord k) =>
310+ (a -> k)
311+ -> (a -> m (Fold m a b))
312+ -> Fold m a (m (Map k b), Maybe (k, b))
313+demux = demuxGeneric
314+
315+{-# INLINE demuxGenericIO #-}
316+demuxGenericIO :: (MonadIO m, IsMap f, Traversable f) =>
280317 (a -> Key f)
281318 -> (a -> m (Fold m a b))
282319 -> Fold m a (m (f b), Maybe (Key f, b))
283−demuxScanMutWith getKey getFold = fmap extract $ foldlM' step initial
320+demuxGenericIO getKey getFold = fmap extract $ foldlM' step initial
284321
285322 where
286323
@@ -314,7 +351,7 @@ demuxScanMutWith getKey getFold = fmap extract $ foldlM' step initial
314351 Done b ->
315352 let kv1 = IsMap.mapDelete k kv
316353 in return $ Tuple' kv1 (Just (k, b))
317− Done _ -> error "demuxScanMutWith: unreachable"
354+ Done _ -> error "demuxGenericIO: unreachable"
318355
319356 step (Tuple' kv _) a = do
320357 let k = getKey a
@@ -337,47 +374,68 @@ demuxScanMutWith getKey getFold = fmap extract $ foldlM' step initial
337374 Partial s -> e s
338375 Done b -> return b
339376
340−-- | This collects all the results of 'demuxScanWith' in a container.
377+-- | This is specialized version of 'demux' that uses mutable IO cells as
378+-- fold accumulators for better performance.
341379--
342−{-# INLINE demuxWith #-}
343−demuxWith :: (Monad m, IsMap f, Traversable f) =>
380+{-# INLINE demuxIO #-}
381+demuxIO :: (MonadIO m, Ord k) =>
382+ (a -> k)
383+ -> (a -> m (Fold m a b))
384+ -> Fold m a (m (Map k b), Maybe (k, b))
385+demuxIO = demuxGenericIO
386+
387+{-# INLINE demuxToContainer #-}
388+demuxToContainer :: (Monad m, IsMap f, Traversable f) =>
344389 (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)
345−demuxWith getKey getFold =
390+demuxToContainer getKey getFold =
346391 let
347− classifier = demuxScanWith getKey getFold
392+ classifier = demuxGeneric getKey getFold
348393 getMap Nothing = pure IsMap.mapEmpty
349394 getMap (Just action) = action
350395 aggregator =
351396 teeWith IsMap.mapUnion
352397 (rmapM getMap $ lmap fst latest)
353− (lmap snd $ catMaybes toMap)
398+ (lmap snd $ catMaybes kvToMapOverwriteGeneric)
354399 in postscan classifier aggregator
355400
356−-- | Same as 'demuxWith' but uses 'demuxScanMutWith' for better performance.
401+-- | This collects all the results of 'demux' in a Map.
357402--
358−-- Definition:
359−--
360−-- >>> demuxMutWith = Fold.demuxWith
361−--
362−{-# INLINE demuxMutWith #-}
363−demuxMutWith :: (MonadIO m, IsMap f, Traversable f) =>
403+{-# INLINE demuxToMap #-}
404+demuxToMap :: (Monad m, Ord k) =>
405+ (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (Map k b)
406+demuxToMap = demuxToContainer
407+
408+{-# INLINE demuxToContainerIO #-}
409+demuxToContainerIO :: (MonadIO m, IsMap f, Traversable f) =>
364410 (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)
365−demuxMutWith getKey getFold =
411+demuxToContainerIO getKey getFold =
366412 let
367− classifier = demuxScanMutWith getKey getFold
413+ classifier = demuxGenericIO getKey getFold
368414 getMap Nothing = pure IsMap.mapEmpty
369415 getMap (Just action) = action
370416 aggregator =
371417 teeWith IsMap.mapUnion
372418 (rmapM getMap $ lmap fst latest)
373− (lmap snd $ catMaybes toMap)
419+ (lmap snd $ catMaybes kvToMapOverwriteGeneric)
374420 in postscan classifier aggregator
375421
422+-- | Same as 'demuxToMap' but uses 'demuxIO' for better performance.
423+--
424+{-# INLINE demuxToMapIO #-}
425+demuxToMapIO :: (MonadIO m, Ord k) =>
426+ (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (Map k b)
427+demuxToMapIO = demuxToContainerIO
428+
429+{-# INLINE demuxKvToContainer #-}
430+demuxKvToContainer :: (Monad m, IsMap f, Traversable f) =>
431+ (Key f -> m (Fold m a b)) -> Fold m (Key f, a) (f b)
432+demuxKvToContainer f = demuxToContainer fst (\(k, _) -> fmap (lmap snd) (f k))
433+
376434-- | Fold a stream of key value pairs using a function that maps keys to folds.
377435--
378436-- Definition:
379437--
380−-- >>> demux f = Fold.demuxWith fst (Fold.lmap snd . f)
438+-- >>> demuxKvToMap f = Fold.demuxToContainer fst (Fold.lmap snd . f)
381439--
382440-- Example:
383441--
@@ -386,15 +444,15 @@ demuxMutWith getKey getFold =
386444-- let f "SUM" = return Fold.sum
387445-- f _ = return Fold.product
388446-- input = Stream.fromList [("SUM",1),("PRODUCT",2),("SUM",3),("PRODUCT",4)]
389−-- in Stream.fold (Fold.demux f) input :: IO (Map String Int)
447+-- in Stream.fold (Fold.demuxKvToMap f) input :: IO (Map String Int)
390448-- :}
391449-- fromList [("PRODUCT",8),("SUM",4)]
392450--
393451-- /Pre-release/
394−{-# INLINE demux #-}
395−demux :: (Monad m, IsMap f, Traversable f) =>
396− (Key f -> m (Fold m a b)) -> Fold m (Key f, a) (f b)
397−demux f = demuxWith fst (\(k, _) -> fmap (lmap snd) (f k))
452+{-# INLINE demuxKvToMap #-}
453+demuxKvToMap :: (Monad m, Ord k) =>
454+ (k -> m (Fold m a b)) -> Fold m (k, a) (Map k b)
455+demuxKvToMap = demuxKvToContainer
398456
399457------------------------------------------------------------------------------
400458-- Classify: Like demux but uses the same fold for all keys.
@@ -406,25 +464,14 @@ demux f = demuxWith fst (\(k, _) -> fmap (lmap snd) (f k))
406464-- done then initial would set a flag in the state to ignore the input or
407465-- return an error.
408466
409−-- | Folds the values for each key using the supplied fold. When scanning, as
410−-- soon as the fold is complete, its result is available in the second
411−-- component of the tuple. The first component of the tuple is a snapshot of
412−-- the in-progress folds.
413−--
414−-- Once the fold for a key is done, any future values of the key are ignored.
415−--
416−-- Definition:
417−--
418−-- >>> classifyScanWith f fld = Fold.demuxScanWith f (const fld)
419−--
420−{-# INLINE classifyScanWith #-}
421−classifyScanWith :: (Monad m, IsMap f, Traversable f, Ord (Key f)) =>
467+{-# INLINE classifyGeneric #-}
468+classifyGeneric :: (Monad m, IsMap f, Traversable f, Ord (Key f)) =>
422469 -- Note: we need to return the Map itself to display the in-progress values
423470 -- e.g. to implement top. We could possibly create a separate abstraction
424471 -- for that use case. We return an action because we want it to be lazy so
425472 -- that the downstream consumers can choose to process or discard it.
426473 (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))
427−classifyScanWith f (Fold step1 initial1 extract1) =
474+classifyGeneric f (Fold step1 initial1 extract1) =
428475 fmap extract $ foldlM' step initial
429476
430477 where
@@ -464,23 +511,30 @@ classifyScanWith f (Fold step1 initial1 extract1) =
464511
465512 extract (Tuple3' kv _ x) = (Prelude.mapM extract1 kv, x)
466513
467−-- XXX we can use a Prim IORef if we can constrain the state "s" to be Prim
514+-- | Folds the values for each key using the supplied fold. When scanning, as
515+-- soon as the fold is complete, its result is available in the second
516+-- component of the tuple. The first component of the tuple is a snapshot of
517+-- the in-progress folds.
468518--
469−-- The code is almost the same as classifyScanWith except the IORef operations.
470−
471−-- | Same as classifyScanWith except that it uses mutable IORef cells in the
472−-- Map providing better performance. Be aware that if this is used as a scan,
473−-- the values in the intermediate Maps would be mutable.
519+-- Once the fold for a key is done, any future values of the key are ignored.
474520--
475−-- Definitions:
521+-- Definition:
522+--
523+-- >>> classify f fld = Fold.demux f (const fld)
476524--
477−-- >>> classifyScanMutWith = Fold.classifyScanWith
478−-- >>> classifyScanMutWith f fld = Fold.demuxScanMutWith f (const fld)
525+{-# INLINE classify #-}
526+classify :: (Monad m, Ord k) =>
527+ (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b))
528+classify = classifyGeneric
529+
530+-- XXX we can use a Prim IORef if we can constrain the state "s" to be Prim
479531--
480−{-# INLINE classifyScanMutWith #-}
481−classifyScanMutWith :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) =>
532+-- The code is almost the same as classifyGeneric except the IORef operations.
533+
534+{-# INLINE classifyGenericIO #-}
535+classifyGenericIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) =>
482536 (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))
483−classifyScanMutWith f (Fold step1 initial1 extract1) =
537+classifyGenericIO f (Fold step1 initial1 extract1) =
484538 fmap extract $ foldlM' step initial
485539
486540 where
@@ -523,11 +577,39 @@ classifyScanMutWith f (Fold step1 initial1 extract1) =
523577 extract (Tuple3' kv _ x) =
524578 (Prelude.mapM (\ref -> liftIO (readIORef ref) >>= extract1) kv, x)
525579
526−-- | Fold a key value stream to a key-value container. If the same key appears
580+-- | Same as classify except that it uses mutable IORef cells in the
581+-- Map providing better performance. Be aware that if this is used as a scan,
582+-- the values in the intermediate Maps would be mutable.
583+--
584+-- Definitions:
585+--
586+-- >>> classifyIO f fld = Fold.demuxIO f (const fld)
587+--
588+{-# INLINE classifyIO #-}
589+classifyIO :: (MonadIO m, Ord k) =>
590+ (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b))
591+classifyIO = classifyGenericIO
592+
593+-- | Fold a key value stream to a key-value Map. If the same key appears
527594-- multiple times, only the last value is retained.
528−{-# INLINE toMap #-}
529−toMap :: (Monad m, IsMap f) => Fold m (Key f, a) (f a)
530−toMap = foldl' (\kv (k, v) -> IsMap.mapInsert k v kv) IsMap.mapEmpty
595+{-# INLINE kvToMapOverwriteGeneric #-}
596+kvToMapOverwriteGeneric :: (Monad m, IsMap f) => Fold m (Key f, a) (f a)
597+kvToMapOverwriteGeneric =
598+ foldl' (\kv (k, v) -> IsMap.mapInsert k v kv) IsMap.mapEmpty
599+
600+{-# INLINE toContainer #-}
601+toContainer :: (Monad m, IsMap f, Traversable f, Ord (Key f)) =>
602+ (a -> Key f) -> Fold m a b -> Fold m a (f b)
603+toContainer f fld =
604+ let
605+ classifier = classifyGeneric f fld
606+ getMap Nothing = pure IsMap.mapEmpty
607+ getMap (Just action) = action
608+ aggregator =
609+ teeWith IsMap.mapUnion
610+ (rmapM getMap $ lmap fst latest)
611+ (lmap snd $ catMaybes kvToMapOverwriteGeneric)
612+ in postscan classifier aggregator
531613
532614-- | Split the input stream based on a key field and fold each split using the
533615-- given fold. Useful for map/reduce, bucketizing the input in different bins
@@ -538,7 +620,7 @@ toMap = foldl' (\kv (k, v) -> IsMap.mapInsert k v kv) IsMap.mapEmpty
538620-- >>> import Data.Map.Strict (Map)
539621-- >>> :{
540622-- let input = Stream.fromList [("ONE",1),("ONE",1.1),("TWO",2), ("TWO",2.2)]
541−-- classify = Fold.classifyWith fst (Fold.lmap snd Fold.toList)
623+-- classify = Fold.toMap fst (Fold.lmap snd Fold.toList)
542624-- in Stream.fold classify input :: IO (Map String [Double])
543625-- :}
544626-- fromList [("ONE",[1.0,1.1]),("TWO",[2.0,2.2])]
@@ -550,44 +632,42 @@ toMap = foldl' (\kv (k, v) -> IsMap.mapInsert k v kv) IsMap.mapEmpty
550632-- monotonically increases because it stores whether a key has been seen or
551633-- not.
552634--
635+-- See 'demuxToMap' for a more powerful version where you can use a different
636+-- fold for each key. A simpler version of 'toMap' retaining only the last
637+-- value for a key can be written as:
638+--
639+-- >>> toMap = Fold.foldl' (\kv (k, v) -> Map.insert k v kv) Map.empty
640+--
553641-- /Stops: never/
554642--
555643-- /Pre-release/
556644--
557−{-# INLINE classifyWith #-}
558−classifyWith :: (Monad m, IsMap f, Traversable f, Ord (Key f)) =>
645+{-# INLINE toMap #-}
646+toMap :: (Monad m, Ord k) =>
647+ (a -> k) -> Fold m a b -> Fold m a (Map k b)
648+toMap = toContainer
649+
650+{-# INLINE toContainerIO #-}
651+toContainerIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) =>
559652 (a -> Key f) -> Fold m a b -> Fold m a (f b)
560−classifyWith f fld =
653+toContainerIO f fld =
561654 let
562− classifier = classifyScanWith f fld
655+ classifier = classifyGenericIO f fld
563656 getMap Nothing = pure IsMap.mapEmpty
564657 getMap (Just action) = action
565658 aggregator =
566659 teeWith IsMap.mapUnion
567660 (rmapM getMap $ lmap fst latest)
568− (lmap snd $ catMaybes toMap)
661+ (lmap snd $ catMaybes kvToMapOverwriteGeneric)
569662 in postscan classifier aggregator
570663
571−-- | Same as 'classifyWith' but maybe faster because it uses mutable cells as
664+-- | Same as 'toMap' but maybe faster because it uses mutable cells as
572665-- fold accumulators in the Map.
573666--
574−-- Definition:
575−--
576−-- >>> classifyMutWith = Fold.classifyWith
577−--
578−{-# INLINE classifyMutWith #-}
579−classifyMutWith :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) =>
580− (a -> Key f) -> Fold m a b -> Fold m a (f b)
581−classifyMutWith f fld =
582− let
583− classifier = classifyScanMutWith f fld
584− getMap Nothing = pure IsMap.mapEmpty
585− getMap (Just action) = action
586− aggregator =
587− teeWith IsMap.mapUnion
588− (rmapM getMap $ lmap fst latest)
589− (lmap snd $ catMaybes toMap)
590− in postscan classifier aggregator
667+{-# INLINE toMapIO #-}
668+toMapIO :: (MonadIO m, Ord k) =>
669+ (a -> k) -> Fold m a b -> Fold m a (Map k b)
670+toMapIO = toContainerIO
591671
592672-- | Given an input stream of key value pairs and a fold for values, fold all
593673-- the values belonging to each key. Useful for map/reduce, bucketizing the
@@ -595,20 +675,20 @@ classifyMutWith f fld =
595675--
596676-- Definition:
597677--
598−-- >>> classify = Fold.classifyWith fst . Fold.lmap snd
678+-- >>> kvToMap = Fold.toMap fst . Fold.lmap snd
599679--
600680-- Example:
601681--
602682-- >>> :{
603683-- let input = Stream.fromList [("ONE",1),("ONE",1.1),("TWO",2), ("TWO",2.2)]
604−-- in Stream.fold (Fold.classify Fold.toList) input
684+-- in Stream.fold (Fold.kvToMap Fold.toList) input
605685-- :}
606686-- fromList [("ONE",[1.0,1.1]),("TWO",[2.0,2.2])]
607687--
608688-- /Pre-release/
609−{-# INLINE classify #-}
610−classify :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)
611−classify = classifyWith fst . lmap snd
689+{-# INLINE kvToMap #-}
690+kvToMap :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)
691+kvToMap = toMap fst . lmap snd
612692
613693-- | Determine the frequency of each element in the stream.
614694--
@@ -617,8 +697,8 @@ classify = classifyWith fst . lmap snd
617697--
618698-- Definition:
619699--
620−-- >>> frequency = Fold.classifyWith id Fold.length
700+-- >>> frequency = Fold.toMap id Fold.length
621701--
622702{-# INLINE frequency #-}
623703frequency :: (Monad m, Ord a) => Fold m a (Map a Int)
624−frequency = classifyWith id length
704+frequency = toMap id length