on 9ad7fca help sign in
1204 lines 35 kB history raw
1{-# OPTIONS_HADDOCK hide #-}
2{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE CPP #-}
4{-# LANGUAGE ExistentialQuantification #-}
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RecordWildCards #-}
8{-# LANGUAGE ScopedTypeVariables #-}
10-- |
11-- Module : Streamly.Internal.Data.Fold
12-- Copyright : (c) 2019 Composewell Technologies
13-- (c) 2013 Gabriel Gonzalez
14-- License : BSD3
15-- Maintainer : streamly@composewell.com
16-- Stability : experimental
17-- Portability : GHC
19-- Also see the "Streamly.Internal.Data.Sink" module that provides specialized left folds
20-- that discard the outputs.
21--
22-- IMPORTANT: keep the signatures consistent with the folds in Streamly.Prelude
24module Streamly.Internal.Data.Fold
25 (
26 -- * Fold Type
27 Fold (..)
29 , hoist
30 , generally
32 -- , tail
33 -- , init
35 -- * Fold Creation Utilities
36 , mkPure
37 , mkPureId
38 , mkFold
39 , mkFoldId
41 -- ** Full Folds
42 , drain
43 , drainBy
44 , last
45 , length
46 , sum
47 , product
48 , maximumBy
49 , maximum
50 , minimumBy
51 , minimum
52 -- , the
53 , mean
54 , variance
55 , stdDev
56 , rollingHash
57 , rollingHashWithSalt
58 -- , rollingHashFirstN
59 -- , rollingHashLastN
61 -- ** Full Folds (Monoidal)
62 , mconcat
63 , foldMap
64 , foldMapM
66 -- ** Full Folds (To Containers)
68 , toList
69 , toListRevF -- experimental
71 -- ** Partial Folds
72 -- , drainN
73 -- , drainWhile
74 -- , lastN
75 -- , (!!)
76 -- , genericIndex
77 , index
78 , head
79 -- , findM
80 , find
81 , lookup
82 , findIndex
83 , elemIndex
84 , null
85 , elem
86 , notElem
87 -- XXX these are slower than right folds even when full input is used
88 , all
89 , any
90 , and
91 , or
93 -- * Transformations
95 -- ** Covariant Operations
96 , sequence
97 , mapM
99 -- ** Mapping
100 , transform
101 , lmap
102 --, lsequence
103 , lmapM
104 -- ** Filtering
105 , lfilter
106 , lfilterM
107 -- , ldeleteBy
108 -- , luniq
109 , lcatMaybes
111 {-
112 -- ** Mapping Filters
113 , lmapMaybe
114 , lmapMaybeM
116 -- ** Scanning Filters
117 , lfindIndices
118 , lelemIndices
120 -- ** Insertion
121 -- | Insertion adds more elements to the stream.
123 , linsertBy
124 , lintersperseM
126 -- ** Reordering
127 , lreverse
128 -}
130 -- * Parsing
131 -- ** Trimming
132 , ltake
133 -- , lrunFor -- time
134 , ltakeWhile
135 {-
136 , ltakeWhileM
137 , ldrop
138 , ldropWhile
139 , ldropWhileM
140 -}
142 , lsessionsOf
143 , lchunksOf
145 -- * Distributing
147 , tee
148 , distribute
150 -- * Partitioning
152 -- , partitionByM
153 -- , partitionBy
154 , partition
156 -- * Demultiplexing
158 , demux
159 -- , demuxWith
160 , demux_
161 -- , demuxWith_
163 -- * Classifying
165 , classify
166 -- , classifyWith
168 -- * Unzipping
169 , unzip
170 -- These can be expressed using lmap/lmapM and unzip
171 -- , unzipWith
172 -- , unzipWithM
174 -- * Running Folds
175 , initialize
176 , runStep
178 -- * Nested Folds
179 -- , concatMap
180 -- , chunksOf
181 , duplicate -- experimental
182 )
183where
185import Control.Monad (void)
186import Data.Functor.Identity (Identity(..))
187import Data.Map.Strict (Map)
189import Prelude
190 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
191 foldl, map, mapM_, sequence, all, any, sum, product, elem,
192 notElem, maximum, minimum, head, last, tail, length, null,
193 reverse, iterate, init, and, or, lookup, foldr1, (!!),
194 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
195 span, splitAt, break, mapM)
197import qualified Data.Map.Strict as Map
198import qualified Prelude
200import Streamly.Internal.Data.Pipe.Types (Pipe (..), PipeState(..))
201import Streamly.Internal.Data.Fold.Types
202import Streamly.Internal.Data.Strict
204import qualified Streamly.Internal.Data.Pipe.Types as Pipe
206------------------------------------------------------------------------------
207-- Smart constructors
208------------------------------------------------------------------------------
210-- | Make a fold using a pure step function, a pure initial state and
211-- a pure state extraction function.
212--
213-- /Internal/
214--
215{-# INLINE mkPure #-}
216mkPure :: Monad m => (s -> a -> s) -> s -> (s -> b) -> Fold m a b
217mkPure step initial extract =
218 Fold (\s a -> return $ step s a) (return initial) (return . extract)
220-- | Make a fold using a pure step function and a pure initial state. The
221-- final state extracted is identical to the intermediate state.
222--
223-- /Internal/
224--
225{-# INLINE mkPureId #-}
226mkPureId :: Monad m => (b -> a -> b) -> b -> Fold m a b
227mkPureId step initial = mkPure step initial id
229-- | Make a fold with an effectful step function and initial state, and a state
230-- extraction function.
231--
232-- > mkFold = Fold
233--
234-- We can just use 'Fold' but it is provided for completeness.
235--
236-- /Internal/
237--
238{-# INLINE mkFold #-}
239mkFold :: (s -> a -> m s) -> m s -> (s -> m b) -> Fold m a b
240mkFold = Fold
242-- | Make a fold with an effectful step function and initial state. The final
243-- state extracted is identical to the intermediate state.
244--
245-- /Internal/
246--
247{-# INLINE mkFoldId #-}
248mkFoldId :: Monad m => (b -> a -> m b) -> m b -> Fold m a b
249mkFoldId step initial = Fold step initial return
251------------------------------------------------------------------------------
252-- hoist
253------------------------------------------------------------------------------
255-- | Change the underlying monad of a fold
256--
257-- /Internal/
258hoist :: (forall x. m x -> n x) -> Fold m a b -> Fold n a b
259hoist f (Fold step initial extract) =
260 Fold (\x a -> f $ step x a) (f initial) (f . extract)
262-- | Adapt a pure fold to any monad
263--
264-- > generally = hoist (return . runIdentity)
265--
266-- /Internal/
267generally :: Monad m => Fold Identity a b -> Fold m a b
268generally = hoist (return . runIdentity)
270------------------------------------------------------------------------------
271-- Transformations on fold inputs
272------------------------------------------------------------------------------
274-- | Flatten the monadic output of a fold to pure output.
275--
276-- @since 0.7.0
277{-# INLINE sequence #-}
278sequence :: Monad m => Fold m a (m b) -> Fold m a b
279sequence (Fold step initial extract) = Fold step initial extract'
280 where
281 extract' x = do
282 act <- extract x
283 act >>= return
285-- | Map a monadic function on the output of a fold.
286--
287-- @since 0.7.0
288{-# INLINE mapM #-}
289mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c
290mapM f = sequence . fmap f
292------------------------------------------------------------------------------
293-- Transformations on fold inputs
294------------------------------------------------------------------------------
296-- rename to lpipe?
297--
298-- | Apply a transformation on a 'Fold' using a 'Pipe'.
299--
300-- @since 0.7.0
301{-# INLINE transform #-}
302transform :: Monad m => Pipe m a b -> Fold m b c -> Fold m a c
303transform (Pipe pstep1 pstep2 pinitial) (Fold fstep finitial fextract) =
304 Fold step initial extract
306 where
308 initial = Tuple' <$> return pinitial <*> finitial
309 step (Tuple' ps fs) x = do
310 r <- pstep1 ps x
311 go fs r
313 where
314 -- XXX use SPEC?
315 go acc (Pipe.Yield b (Consume ps')) = do
316 acc' <- fstep acc b
317 return (Tuple' ps' acc')
319 go acc (Pipe.Yield b (Produce ps')) = do
320 acc' <- fstep acc b
321 r <- pstep2 ps'
322 go acc' r
324 go acc (Pipe.Continue (Consume ps')) = return (Tuple' ps' acc)
326 go acc (Pipe.Continue (Produce ps')) = do
327 r <- pstep2 ps'
328 go acc r
330 extract (Tuple' _ fs) = fextract fs
332------------------------------------------------------------------------------
333-- Utilities
334------------------------------------------------------------------------------
336-- | @_Fold1 step@ returns a new 'Fold' using just a step function that has the
337-- same type for the accumulator and the element. The result type is the
338-- accumulator type wrapped in 'Maybe'. The initial accumulator is retrieved
339-- from the 'Foldable', the result is 'None' for empty containers.
340{-# INLINABLE _Fold1 #-}
341_Fold1 :: Monad m => (a -> a -> a) -> Fold m a (Maybe a)
342_Fold1 step = Fold step_ (return Nothing') fromStrictMaybe
343 where
344 step_ mx a = return $ Just' $
345 case mx of
346 Nothing' -> a
347 Just' x -> step x a
349------------------------------------------------------------------------------
350-- Left folds
351------------------------------------------------------------------------------
353------------------------------------------------------------------------------
354-- Run Effects
355------------------------------------------------------------------------------
357-- | A fold that drains all its input, running the effects and discarding the
358-- results.
359--
360-- @since 0.7.0
361{-# INLINABLE drain #-}
362drain :: Monad m => Fold m a ()
363drain = Fold step begin done
364 where
365 begin = return ()
366 step _ _ = return ()
367 done = return
369-- |
370-- > drainBy f = lmapM f drain
371--
372-- Drain all input after passing it through a monadic function. This is the
373-- dual of mapM_ on stream producers.
374--
375-- @since 0.7.0
376{-# INLINABLE drainBy #-}
377drainBy :: Monad m => (a -> m b) -> Fold m a ()
378drainBy f = Fold (const (void . f)) (return ()) return
380-- | Extract the last element of the input stream, if any.
381--
382-- @since 0.7.0
383{-# INLINABLE last #-}
384last :: Monad m => Fold m a (Maybe a)
385last = _Fold1 (flip const)
387------------------------------------------------------------------------------
388-- To Summary
389------------------------------------------------------------------------------
391-- | Like 'length', except with a more general 'Num' return value
392--
393-- @since 0.7.0
394{-# INLINABLE genericLength #-}
395genericLength :: (Monad m, Num b) => Fold m a b
396genericLength = Fold (\n _ -> return $ n + 1) (return 0) return
398-- | Determine the length of the input stream.
399--
400-- @since 0.7.0
401{-# INLINABLE length #-}
402length :: Monad m => Fold m a Int
403length = genericLength
405-- | Determine the sum of all elements of a stream of numbers. Returns additive
406-- identity (@0@) when the stream is empty. Note that this is not numerically
407-- stable for floating point numbers.
408--
409-- @since 0.7.0
410{-# INLINABLE sum #-}
411sum :: (Monad m, Num a) => Fold m a a
412sum = Fold (\x a -> return $ x + a) (return 0) return
414-- | Determine the product of all elements of a stream of numbers. Returns
415-- multiplicative identity (@1@) when the stream is empty.
416--
417-- @since 0.7.0
418{-# INLINABLE product #-}
419product :: (Monad m, Num a) => Fold m a a
420product = Fold (\x a -> return $ x * a) (return 1) return
422------------------------------------------------------------------------------
423-- To Summary (Maybe)
424------------------------------------------------------------------------------
426-- | Determine the maximum element in a stream using the supplied comparison
427-- function.
428--
429-- @since 0.7.0
430{-# INLINABLE maximumBy #-}
431maximumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)
432maximumBy cmp = _Fold1 max'
433 where
434 max' x y = case cmp x y of
435 GT -> x
436 _ -> y
438-- |
439-- @
440-- maximum = 'maximumBy' compare
441-- @
442--
443-- Determine the maximum element in a stream.
444--
445-- @since 0.7.0
446{-# INLINABLE maximum #-}
447maximum :: (Monad m, Ord a) => Fold m a (Maybe a)
448maximum = _Fold1 max
450-- | Computes the minimum element with respect to the given comparison function
451--
452-- @since 0.7.0
453{-# INLINABLE minimumBy #-}
454minimumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)
455minimumBy cmp = _Fold1 min'
456 where
457 min' x y = case cmp x y of
458 GT -> y
459 _ -> x
461-- | Determine the minimum element in a stream using the supplied comparison
462-- function.
463--
464-- @since 0.7.0
465{-# INLINABLE minimum #-}
466minimum :: (Monad m, Ord a) => Fold m a (Maybe a)
467minimum = _Fold1 min
469------------------------------------------------------------------------------
470-- To Summary (Statistical)
471------------------------------------------------------------------------------
473-- | Compute a numerically stable arithmetic mean of all elements in the input
474-- stream.
475--
476-- @since 0.7.0
477{-# INLINABLE mean #-}
478mean :: (Monad m, Fractional a) => Fold m a a
479mean = Fold step (return begin) (return . done)
480 where
481 begin = Tuple' 0 0
482 step (Tuple' x n) y = return $
483 let n' = n + 1
484 in Tuple' (x + (y - x) / n') n'
485 done (Tuple' x _) = x
487-- | Compute a numerically stable (population) variance over all elements in
488-- the input stream.
489--
490-- @since 0.7.0
491{-# INLINABLE variance #-}
492variance :: (Monad m, Fractional a) => Fold m a a
493variance = Fold step (return begin) (return . done)
494 where
495 begin = Tuple3' 0 0 0
497 step (Tuple3' n mean_ m2) x = return $ Tuple3' n' mean' m2'
498 where
499 n' = n + 1
500 mean' = (n * mean_ + x) / (n + 1)
501 delta = x - mean_
502 m2' = m2 + delta * delta * n / (n + 1)
504 done (Tuple3' n _ m2) = m2 / n
506-- | Compute a numerically stable (population) standard deviation over all
507-- elements in the input stream.
508--
509-- @since 0.7.0
510{-# INLINABLE stdDev #-}
511stdDev :: (Monad m, Floating a) => Fold m a a
512stdDev = sqrt variance
514-- | Compute an 'Int' sized polynomial rolling hash
515--
516-- > H = salt * k ^ n + c1 * k ^ (n - 1) + c2 * k ^ (n - 2) + ... + cn * k ^ 0
517--
518-- Where @c1@, @c2@, @cn@ are the elements in the input stream and @k@ is a
519-- constant.
520--
521-- This hash is often used in Rabin-Karp string search algorithm.
522--
523-- See https://en.wikipedia.org/wiki/Rolling_hash
524--
525-- @since 0.7.0
526{-# INLINABLE rollingHashWithSalt #-}
527rollingHashWithSalt :: (Monad m, Enum a) => Int -> Fold m a Int
528rollingHashWithSalt salt = Fold step initial extract
529 where
530 k = 2891336453
531 initial = return salt
532 step cksum a = return $ cksum * k + fromEnum a
533 extract = return
535-- | A default salt used in the implementation of 'rollingHash'.
536{-# INLINE defaultSalt #-}
537defaultSalt :: Int
538#if WORD_SIZE_IN_BITS == 64
539defaultSalt = 0xdc36d1615b7400a4
540#else
541defaultSalt = 0x087fc72c
542#endif
544-- | Compute an 'Int' sized polynomial rolling hash of a stream.
545--
546-- > rollingHash = rollingHashWithSalt defaultSalt
547--
548-- @since 0.7.0
549{-# INLINABLE rollingHash #-}
550rollingHash :: (Monad m, Enum a) => Fold m a Int
551rollingHash = rollingHashWithSalt defaultSalt
553------------------------------------------------------------------------------
554-- Monoidal left folds
555------------------------------------------------------------------------------
557-- | Fold an input stream consisting of monoidal elements using 'mappend'
558-- and 'mempty'.
559--
560-- > S.fold FL.mconcat (S.map Sum $ S.enumerateFromTo 1 10)
561--
562-- @since 0.7.0
563{-# INLINABLE mconcat #-}
564mconcat :: (Monad m, Monoid a) => Fold m a a
565mconcat = Fold (\x a -> return $ mappend x a) (return mempty) return
567-- |
568-- > foldMap f = map f mconcat
569--
570-- Make a fold from a pure function that folds the output of the function
571-- using 'mappend' and 'mempty'.
572--
573-- > S.fold (FL.foldMap Sum) $ S.enumerateFromTo 1 10
574--
575-- @since 0.7.0
576{-# INLINABLE foldMap #-}
577foldMap :: (Monad m, Monoid b) => (a -> b) -> Fold m a b
578foldMap f = lmap f mconcat
580-- |
581-- > foldMapM f = mapM f mconcat
582--
583-- Make a fold from a monadic function that folds the output of the function
584-- using 'mappend' and 'mempty'.
585--
586-- > S.fold (FL.foldMapM (return . Sum)) $ S.enumerateFromTo 1 10
587--
588-- @since 0.7.0
589{-# INLINABLE foldMapM #-}
590foldMapM :: (Monad m, Monoid b) => (a -> m b) -> Fold m a b
591foldMapM act = Fold step begin done
592 where
593 done = return
594 begin = return mempty
595 step m a = do
596 m' <- act a
597 return $! mappend m m'
599------------------------------------------------------------------------------
600-- To Containers
601------------------------------------------------------------------------------
603-- | Folds the input stream to a list.
604--
605-- /Warning!/ working on large lists accumulated as buffers in memory could be
606-- very inefficient, consider using "Streamly.Array" instead.
607--
608-- @since 0.7.0
610-- id . (x1 :) . (x2 :) . (x3 :) . ... . (xn :) $ []
611{-# INLINABLE toList #-}
612toList :: Monad m => Fold m a [a]
613toList = Fold (\f x -> return $ f . (x :))
614 (return id)
615 (return . ($ []))
617------------------------------------------------------------------------------
618-- Partial Folds
619------------------------------------------------------------------------------
621------------------------------------------------------------------------------
622-- To Elements
623------------------------------------------------------------------------------
625-- | Like 'index', except with a more general 'Integral' argument
626--
627-- @since 0.7.0
628{-# INLINABLE genericIndex #-}
629genericIndex :: (Integral i, Monad m) => i -> Fold m a (Maybe a)
630genericIndex i = Fold step (return $ Left' 0) done
631 where
632 step x a = return $
633 case x of
634 Left' j -> if i == j
635 then Right' a
636 else Left' (j + 1)
637 _ -> x
638 done x = return $
639 case x of
640 Left' _ -> Nothing
641 Right' a -> Just a
643-- | Lookup the element at the given index.
644--
645-- @since 0.7.0
646{-# INLINABLE index #-}
647index :: Monad m => Int -> Fold m a (Maybe a)
648index = genericIndex
650-- | Extract the first element of the stream, if any.
651--
652-- @since 0.7.0
653{-# INLINABLE head #-}
654head :: Monad m => Fold m a (Maybe a)
655head = _Fold1 const
657-- | Returns the first element that satisfies the given predicate.
658--
659-- @since 0.7.0
660{-# INLINABLE find #-}
661find :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
662find predicate = Fold step (return Nothing') fromStrictMaybe
663 where
664 step x a = return $
665 case x of
666 Nothing' -> if predicate a
667 then Just' a
668 else Nothing'
669 _ -> x
671-- | In a stream of (key-value) pairs @(a, b)@, return the value @b@ of the
672-- first pair where the key equals the given value @a@.
673--
674-- @since 0.7.0
675{-# INLINABLE lookup #-}
676lookup :: (Eq a, Monad m) => a -> Fold m (a,b) (Maybe b)
677lookup a0 = Fold step (return Nothing') fromStrictMaybe
678 where
679 step x (a,b) = return $
680 case x of
681 Nothing' -> if a == a0
682 then Just' b
683 else Nothing'
684 _ -> x
686-- | Convert strict 'Either'' to lazy 'Maybe'
687{-# INLINABLE hush #-}
688hush :: Either' a b -> Maybe b
689hush (Left' _) = Nothing
690hush (Right' b) = Just b
692-- | Returns the first index that satisfies the given predicate.
693--
694-- @since 0.7.0
695{-# INLINABLE findIndex #-}
696findIndex :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)
697findIndex predicate = Fold step (return $ Left' 0) (return . hush)
698 where
699 step x a = return $
700 case x of
701 Left' i ->
702 if predicate a
703 then Right' i
704 else Left' (i + 1)
705 _ -> x
707-- | Returns the first index where a given value is found in the stream.
708--
709-- @since 0.7.0
710{-# INLINABLE elemIndex #-}
711elemIndex :: (Eq a, Monad m) => a -> Fold m a (Maybe Int)
712elemIndex a = findIndex (a ==)
714------------------------------------------------------------------------------
715-- To Boolean
716------------------------------------------------------------------------------
718-- | Return 'True' if the input stream is empty.
719--
720-- @since 0.7.0
721{-# INLINABLE null #-}
722null :: Monad m => Fold m a Bool
723null = Fold (\_ _ -> return False) (return True) return
725-- |
726-- > any p = lmap p or
727--
728-- | Returns 'True' if any of the elements of a stream satisfies a predicate.
729--
730-- @since 0.7.0
731{-# INLINABLE any #-}
732any :: Monad m => (a -> Bool) -> Fold m a Bool
733any predicate = Fold (\x a -> return $ x || predicate a) (return False) return
735-- | Return 'True' if the given element is present in the stream.
736--
737-- @since 0.7.0
738{-# INLINABLE elem #-}
739elem :: (Eq a, Monad m) => a -> Fold m a Bool
740elem a = any (a ==)
742-- |
743-- > all p = lmap p and
744--
745-- | Returns 'True' if all elements of a stream satisfy a predicate.
746--
747-- @since 0.7.0
748{-# INLINABLE all #-}
749all :: Monad m => (a -> Bool) -> Fold m a Bool
750all predicate = Fold (\x a -> return $ x && predicate a) (return True) return
752-- | Returns 'True' if the given element is not present in the stream.
753--
754-- @since 0.7.0
755{-# INLINABLE notElem #-}
756notElem :: (Eq a, Monad m) => a -> Fold m a Bool
757notElem a = all (a /=)
759-- | Returns 'True' if all elements are 'True', 'False' otherwise
760--
761-- @since 0.7.0
762{-# INLINABLE and #-}
763and :: Monad m => Fold m Bool Bool
764and = Fold (\x a -> return $ x && a) (return True) return
766-- | Returns 'True' if any element is 'True', 'False' otherwise
767--
768-- @since 0.7.0
769{-# INLINABLE or #-}
770or :: Monad m => Fold m Bool Bool
771or = Fold (\x a -> return $ x || a) (return False) return
773------------------------------------------------------------------------------
774-- Distributing
775------------------------------------------------------------------------------
776--
777-- | Distribute one copy of the stream to each fold and zip the results.
778--
779-- @
780-- |-------Fold m a b--------|
781-- ---stream m a---| |---m (b,c)
782-- |-------Fold m a c--------|
783-- @
784-- >>> S.fold (FL.tee FL.sum FL.length) (S.enumerateFromTo 1.0 100.0)
785-- (5050.0,100)
786--
787-- @since 0.7.0
788{-# INLINE tee #-}
789tee :: Monad m => Fold m a b -> Fold m a c -> Fold m a (b,c)
790tee f1 f2 = (,) <$> f1 <*> f2
792{-# INLINE foldNil #-}
793foldNil :: Monad m => Fold m a [b]
794foldNil = Fold step begin done where
795 begin = return []
796 step _ _ = return []
797 done = return
799{-# INLINE foldCons #-}
800foldCons :: Monad m => Fold m a b -> Fold m a [b] -> Fold m a [b]
801foldCons (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
802 Fold step begin done
804 where
806 begin = Tuple' <$> beginL <*> beginR
807 step (Tuple' xL xR) a = Tuple' <$> stepL xL a <*> stepR xR a
808 done (Tuple' xL xR) = (:) <$> (doneL xL) <*> (doneR xR)
810-- XXX use "List" instead of "[]"?, use Array for output to scale it to a large
811-- number of consumers? For polymorphic case a vector could be helpful. For
812-- Storables we can use arrays. Will need separate APIs for those.
813--
814-- | Distribute one copy of the stream to each fold and collect the results in
815-- a container.
816--
817-- @
818--
819-- |-------Fold m a b--------|
820-- ---stream m a---| |---m [b]
821-- |-------Fold m a b--------|
822-- | |
823-- ...
824-- @
825--
826-- >>> S.fold (FL.distribute [FL.sum, FL.length]) (S.enumerateFromTo 1 5)
827-- [15,5]
828--
829-- This is the consumer side dual of the producer side 'sequence' operation.
830--
831-- @since 0.7.0
832{-# INLINE distribute #-}
833distribute :: Monad m => [Fold m a b] -> Fold m a [b]
834distribute [] = foldNil
835distribute (x:xs) = foldCons x (distribute xs)
837------------------------------------------------------------------------------
838-- Partitioning
839------------------------------------------------------------------------------
840--
841-- | Partition the input over two folds using an 'Either' partitioning
842-- predicate.
843--
844-- @
845--
846-- |-------Fold b x--------|
847-- -----stream m a --> (Either b c)----| |----(x,y)
848-- |-------Fold c y--------|
849-- @
850--
851-- Send input to either fold randomly:
852--
853-- >>> import System.Random (randomIO)
854-- >>> randomly a = randomIO >>= \x -> return $ if x then Left a else Right a
855-- >>> S.fold (FL.partitionByM randomly FL.length FL.length) (S.enumerateFromTo 1 100)
856-- (59,41)
857--
858-- Send input to the two folds in a proportion of 2:1:
859--
860-- @
861-- import Data.IORef (newIORef, readIORef, writeIORef)
862-- proportionately m n = do
863-- ref <- newIORef $ cycle $ concat [replicate m Left, replicate n Right]
864-- return $ \\a -> do
865-- r <- readIORef ref
866-- writeIORef ref $ tail r
867-- return $ head r a
868--
869-- main = do
870-- f <- proportionately 2 1
871-- r <- S.fold (FL.partitionByM f FL.length FL.length) (S.enumerateFromTo (1 :: Int) 100)
872-- print r
873-- @
874-- @
875-- (67,33)
876-- @
877--
878-- This is the consumer side dual of the producer side 'mergeBy' operation.
879--
880-- @since 0.7.0
881{-# INLINE partitionByM #-}
882partitionByM :: Monad m
883 => (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)
884partitionByM f (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
886 Fold step begin done
888 where
890 begin = Tuple' <$> beginL <*> beginR
891 step (Tuple' xL xR) a = do
892 r <- f a
893 case r of
894 Left b -> Tuple' <$> stepL xL b <*> return xR
895 Right c -> Tuple' <$> return xL <*> stepR xR c
896 done (Tuple' xL xR) = (,) <$> doneL xL <*> doneR xR
898-- Note: we could use (a -> Bool) instead of (a -> Either b c), but the latter
899-- makes the signature clearer as to which case belongs to which fold.
900-- XXX need to check the performance in both cases.
902-- | Same as 'partitionByM' but with a pure partition function.
903--
904-- Count even and odd numbers in a stream:
905--
906-- @
907-- >>> let f = FL.partitionBy (\\n -> if even n then Left n else Right n)
908-- (fmap (("Even " ++) . show) FL.length)
909-- (fmap (("Odd " ++) . show) FL.length)
910-- in S.fold f (S.enumerateFromTo 1 100)
911-- ("Even 50","Odd 50")
912-- @
913--
914-- @since 0.7.0
915{-# INLINE partitionBy #-}
916partitionBy :: Monad m
917 => (a -> Either b c) -> Fold m b x -> Fold m c y -> Fold m a (x, y)
918partitionBy f = partitionByM (return . f)
920-- | Compose two folds such that the combined fold accepts a stream of 'Either'
921-- and routes the 'Left' values to the first fold and 'Right' values to the
922-- second fold.
923--
924-- > partition = partitionBy id
925--
926-- @since 0.7.0
927{-# INLINE partition #-}
928partition :: Monad m
929 => Fold m b x -> Fold m c y -> Fold m (Either b c) (x, y)
930partition = partitionBy id
932{-
933-- | Send one item to each fold in a round-robin fashion. This is the consumer
934-- side dual of producer side 'mergeN' operation.
935--
936-- partitionN :: Monad m => [Fold m a b] -> Fold m a [b]
937-- partitionN fs = Fold step begin done
938-}
940-- TODO Demultiplex an input element into a number of typed variants. We want
941-- to statically restrict the target values within a set of predefined types,
942-- an enumeration of a GADT. We also want to make sure that the Map contains
943-- only those types and the full set of those types.
944--
945-- TODO Instead of the input Map it should probably be a lookup-table using an
946-- array and not in GC memory. The same applies to the output Map as well.
947-- However, that would only be helpful if we have a very large data structure,
948-- need to measure and see how it scales.
949--
950-- This is the consumer side dual of the producer side 'mux' operation (XXX to
951-- be implemented).
953-- | Split the input stream based on a key field and fold each split using a
954-- specific fold collecting the results in a map from the keys to the results.
955-- Useful for cases like protocol handlers to handle different type of packets
956-- using different handlers.
957--
958-- @
959--
960-- |-------Fold m a b
961-- -----stream m a-----Map-----|
962-- |-------Fold m a b
963-- |
964-- ...
965-- @
966--
967-- @since 0.7.0
968{-# INLINE demuxWith #-}
969demuxWith :: (Monad m, Ord k)
970 => (a -> (k, a')) -> Map k (Fold m a' b) -> Fold m a (Map k b)
971demuxWith f kv = Fold step initial extract
973 where
975 initial = return kv
976 step mp a = case f a of
977 (k, a') -> Map.alterF twiddle k mp
978 -- XXX should we raise an exception in Nothing case?
979 -- Ideally we should enforce that it is a total map over k so that look
980 -- up never fails
981 -- XXX we could use a monadic update function for a single lookup and
982 -- update in the map.
983 where
984 twiddle Nothing = pure Nothing
985 twiddle (Just (Fold step' acc extract')) = do
986 !r <- acc >>= \x -> step' x a'
987 pure . Just $ Fold step' (return r) extract'
988 extract = Prelude.mapM (\(Fold _ acc e) -> acc >>= e)
990-- | Fold a stream of key value pairs using a map of specific folds for each
991-- key into a map from keys to the results of fold outputs of the corresponding
992-- values.
993--
994-- @
995-- > let table = Data.Map.fromList [(\"SUM", FL.sum), (\"PRODUCT", FL.product)]
996-- input = S.fromList [(\"SUM",1),(\"PRODUCT",2),(\"SUM",3),(\"PRODUCT",4)]
997-- in S.fold (FL.demux table) input
998-- One 1
999-- Two 2
1000-- @
1002-- @since 0.7.0
1003{-# INLINE demux #-}
1004demux :: (Monad m, Ord k)
1005 => Map k (Fold m a b) -> Fold m (k, a) (Map k b)
1006demux = demuxWith id
1008-- | Split the input stream based on a key field and fold each split using a
1009-- specific fold without collecting the results. Useful for cases like protocol
1010-- handlers to handle different type of packets.
1012-- @
1014-- |-------Fold m a ()
1015-- -----stream m a-----Map-----|
1016-- |-------Fold m a ()
1017-- |
1018-- ...
1019-- @
1022-- @since 0.7.0
1024-- demuxWith_ can be slightly faster than demuxWith because we do not need to
1025-- update the Map in this case. This may be significant only if the map is
1026-- large.
1027{-# INLINE demuxWith_ #-}
1028demuxWith_ :: (Monad m, Ord k)
1029 => (a -> (k, a')) -> Map k (Fold m a' b) -> Fold m a ()
1030demuxWith_ f kv = Fold step initial extract
1032 where
1034 initial = do
1035 Prelude.mapM (\(Fold s i e) ->
1036 i >>= \r -> return (Fold s (return r) e)) kv
1037 step mp a
1038 -- XXX should we raise an exception in Nothing case?
1039 -- Ideally we should enforce that it is a total map over k so that look
1040 -- up never fails
1041 | (k, a') <- f a
1042 = case Map.lookup k mp of
1043 Nothing -> return mp
1044 Just (Fold step' acc _) -> do
1045 _ <- acc >>= \x -> step' x a'
1046 return mp
1047 extract mp = Prelude.mapM_ (\(Fold _ acc e) -> acc >>= e) mp
1049-- | Given a stream of key value pairs and a map from keys to folds, fold the
1050-- values for each key using the corresponding folds, discarding the outputs.
1052-- @
1053-- > let prn = FL.drainBy print
1054-- > let table = Data.Map.fromList [(\"ONE", prn), (\"TWO", prn)]
1055-- input = S.fromList [(\"ONE",1),(\"TWO",2)]
1056-- in S.fold (FL.demux_ table) input
1057-- One 1
1058-- Two 2
1059-- @
1061-- @since 0.7.0
1062{-# INLINE demux_ #-}
1063demux_ :: (Monad m, Ord k) => Map k (Fold m a ()) -> Fold m (k, a) ()
1064demux_ = demuxWith_ id
1066-- TODO If the data is large we may need a map/hashmap in pinned memory instead
1067-- of a regular Map. That may require a serializable constraint though. We can
1068-- have another API for that.
1070-- | Split the input stream based on a key field and fold each split using the
1071-- given fold. Useful for map/reduce, bucketizing the input in different bins
1072-- or for generating histograms.
1074-- @
1075-- > let input = S.fromList [(\"ONE",1),(\"ONE",1.1),(\"TWO",2), (\"TWO",2.2)]
1076-- in S.fold (FL.classify FL.toList) input
1077-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
1078-- @
1080-- @since 0.7.0
1081{-# INLINE classifyWith #-}
1082classifyWith :: (Monad m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (Map k b)
1083classifyWith f (Fold step initial extract) = Fold step' initial' extract'
1085 where
1087 initial' = return Map.empty
1088 step' kv a =
1089 let k = f a
1090 in case Map.lookup k kv of
1091 Nothing -> do
1092 x <- initial
1093 r <- step x a
1094 return $ Map.insert k r kv
1095 Just x -> do
1096 r <- step x a
1097 return $ Map.insert k r kv
1098 extract' = Prelude.mapM extract
1100-- | Given an input stream of key value pairs and a fold for values, fold all
1101-- the values belonging to each key. Useful for map/reduce, bucketizing the
1102-- input in different bins or for generating histograms.
1104-- @
1105-- > let input = S.fromList [(\"ONE",1),(\"ONE",1.1),(\"TWO",2), (\"TWO",2.2)]
1106-- in S.fold (FL.classify FL.toList) input
1107-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
1108-- @
1110-- @since 0.7.0
1112-- Same as:
1114-- > classify fld = classifyWith fst (lmap snd fld)
1116{-# INLINE classify #-}
1117classify :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)
1118classify fld = classifyWith fst (lmap snd fld)
1120------------------------------------------------------------------------------
1121-- Unzipping
1122------------------------------------------------------------------------------
1124-- | Like 'unzipWith' but with a monadic splitter function.
1126-- @since 0.7.0
1127{-# INLINE unzipWithM #-}
1128unzipWithM :: Monad m
1129 => (a -> m (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)
1130unzipWithM f (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
1131 Fold step begin done
1133 where
1135 step (Tuple' xL xR) a = do
1136 (b,c) <- f a
1137 Tuple' <$> stepL xL b <*> stepR xR c
1138 begin = Tuple' <$> beginL <*> beginR
1139 done (Tuple' xL xR) = (,) <$> doneL xL <*> doneR xR
1141-- | Split elements in the input stream into two parts using a pure splitter
1142-- function, direct each part to a different fold and zip the results.
1144-- @since 0.7.0
1145{-# INLINE unzipWith #-}
1146unzipWith :: Monad m
1147 => (a -> (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)
1148unzipWith f = unzipWithM (return . f)
1150-- | Send the elements of tuples in a stream of tuples through two different
1151-- folds.
1153-- @
1155-- |-------Fold m a x--------|
1156-- ---------stream of (a,b)--| |----m (x,y)
1157-- |-------Fold m b y--------|
1159-- @
1161-- This is the consumer side dual of the producer side 'zip' operation.
1163-- @since 0.7.0
1164{-# INLINE unzip #-}
1165unzip :: Monad m => Fold m a x -> Fold m b y -> Fold m (a,b) (x,y)
1166unzip = unzipWith id
1168------------------------------------------------------------------------------
1169-- Nesting
1170------------------------------------------------------------------------------
1173-- All the stream flattening transformations can also be applied to a fold
1174-- input stream.
1176-- | This can be used to apply all the stream generation operations on folds.
1177lconcatMap ::(IsStream t, Monad m) => (a -> t m b)
1178 -> Fold m b c
1179 -> Fold m a c
1180lconcatMap s f1 f2 = undefined
1183-- All the grouping transformation that we apply to a stream can also be
1184-- applied to a fold input stream.
1187-- | Group the input stream into groups of elements between @low@ and @high@.
1188-- Collection starts in chunks of @low@ and then keeps doubling until we reach
1189-- @high@. Each chunk is folded using the provided fold function.
1191-- This could be useful, for example, when we are folding a stream of unknown
1192-- size to a stream of arrays and we want to minimize the number of
1193-- allocations.
1195-- @
1197-- XXX we should be able to implement it with parsers/terminating folds.
1199{-# INLINE lchunksInRange #-}
1200lchunksInRange :: Monad m
1201 => Int -> Int -> Fold m a b -> Fold m b c -> Fold m a c
1202lchunksInRange low high (Fold step1 initial1 extract1)
1203 (Fold step2 initial2 extract2) = undefined