@@ -24,18 +24,17 @@
24module Streamly.Internal.Data.Fold
24module Streamly.Internal.Data.Fold
26 -- * Fold Type
26 -- * Fold Type
28− -- A 'Fold' can be run over a stream using the 'runFold' combinator in
29− -- "Streamly.Prelude":
31− -- >>> S.runFold FL.sum (S.enumerateFromTo 1 100)
32+ -- * Fold Creation Utilities
39 -- ** Full Folds
38 -- ** Full Folds
@@ -62,9 +61,6 @@ module Streamly.Internal.Data.Fold
62 , foldMapM
61 , foldMapM
64 -- ** Full Folds (To Containers)
63 -- ** Full Folds (To Containers)
65− -- | Avoid using these folds in scalable or performance critical
66− -- applications, they buffer all the input in GC memory which can be
67− -- detrimental to performance if the input is large.
70 , toListRevF -- experimental
66 , toListRevF -- experimental
@@ -92,22 +88,6 @@ module Streamly.Internal.Data.Fold
94 -- * Transformations
90 -- * Transformations
95− -- | Unlike stream producer types (e.g. @SerialT m a@) which have only
96− -- output side, folds have an input side as well as an output side. In the
97− -- type @Fold m a b@, the input type is @a@ and the output type is @b@.
98− -- Transformations can be applied either on the input side or on the output
99− -- side. The 'Functor' instance of a fold maps on the output of the fold:
101− -- >>> S.runFold (fmap show FL.sum) (S.enumerateFromTo 1 100)
104− -- However, the input side or contravariant transformations are more
105− -- interesting for folds. The following sections describe the input
106− -- transformation operations on a fold. The names of the operations are
107− -- consistent with their covariant counterparts in "Streamly.Prelude", the
108− -- only difference is that they are prefixed with 'l' which stands for
109− -- 'left' assuming left side is the input side, notice that in @Fold m a b@
110− -- the type variable @a@ is on the left side.
112 -- ** Covariant Operations
92 -- ** Covariant Operations
113 , sequence
93 , sequence
@@ -160,51 +140,17 @@ module Streamly.Internal.Data.Fold
160 , lchunksOf
140 , lchunksOf
162 -- * Distributing
142 -- * Distributing
164− -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
165− -- of the stream to each fold and combines the results using a function.
169− -- |-------Fold m a b--------|
170− -- ---stream m a---| |---m (b,c,...)
171− -- |-------Fold m a c--------|
176− -- To compute the average of numbers in a stream without going throught he
179− -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
180− -- >>> S.runFold avg (S.enumerateFromTo 1.0 100.0)
183− -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
184− -- the input to both the folds and combines the outputs using Monoid or
185− -- Semigroup instances of the output types:
187− -- >>> import Data.Monoid (Sum)
188− -- >>> S.runFold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
189− -- Just (Sum {getSum = 101.0})
191− -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.
194 , distribute
145 , distribute
196 -- * Partitioning
147 -- * Partitioning
198− -- Direct items in the input stream to different folds using a binary
201 -- , partitionByM
149 -- , partitionByM
202 -- , partitionBy
150 -- , partitionBy
203 , partition
151 , partition
205 -- * Demultiplexing
153 -- * Demultiplexing
206− -- | Direct values in the input stream to different folds using an n-ary
210 -- , demuxWith
156 -- , demuxWith
@@ -212,8 +158,6 @@ module Streamly.Internal.Data.Fold
212 -- , demuxWith_
158 -- , demuxWith_
214 -- * Classifying
160 -- * Classifying
215− -- | In an input stream of key value pairs fold values for different keys
216− -- in individual output buckets using the given fold.
218 , classify
162 , classify
219 -- , classifyWith
163 -- , classifyWith
@@ -255,6 +199,51 @@ import Streamly.Internal.Data.Strict
256import qualified Streamly.Internal.Data.Pipe.Types as Pipe
200import qualified Streamly.Internal.Data.Pipe.Types as Pipe
202+------------------------------------------------------------------------------
203+-- Smart constructors
204+------------------------------------------------------------------------------
206+-- | Make a fold using a pure step function, a pure initial state and
207+-- a pure state extraction function.
211+{-# INLINE mkPure #-}
212+mkPure :: Monad m => (s -> a -> s) -> s -> (s -> b) -> Fold m a b
213+mkPure step initial extract =
214+ Fold (\s a -> return $ step s a) (return initial) (return . extract)
216+-- | Make a fold using a pure step function and a pure initial state. The
217+-- final state extracted is identical to the intermediate state.
221+{-# INLINE mkPureId #-}
222+mkPureId :: Monad m => (b -> a -> b) -> b -> Fold m a b
223+mkPureId step initial = mkPure step initial id
225+-- | Make a fold with an effectful step function and initial state, and a state
226+-- extraction function.
230+-- We can just use 'Fold' but it is provided for completeness.
234+{-# INLINE mkFold #-}
235+mkFold :: (s -> a -> m s) -> m s -> (s -> m b) -> Fold m a b
238+-- | Make a fold with an effectful step function and initial state. The final
239+-- state extracted is identical to the intermediate state.
243+{-# INLINE mkFoldId #-}
244+mkFoldId :: Monad m => (b -> a -> m b) -> m b -> Fold m a b
245+mkFoldId step initial = Fold step initial return
258------------------------------------------------------------------------------
247------------------------------------------------------------------------------
259-- Transformations on fold inputs
248-- Transformations on fold inputs
260------------------------------------------------------------------------------
249------------------------------------------------------------------------------
@@ -545,7 +534,7 @@ rollingHash = rollingHashWithSalt defaultSalt
545-- | Fold an input stream consisting of monoidal elements using 'mappend'
534-- | Fold an input stream consisting of monoidal elements using 'mappend'
546-- and 'mempty'.
535-- and 'mempty'.
548−-- > S.runFold FL.mconcat (S.map Sum $ S.enumerateFromTo 1 10)
537+-- > S.fold FL.mconcat (S.map Sum $ S.enumerateFromTo 1 10)
550-- @since 0.7.0
539-- @since 0.7.0
551{-# INLINABLE mconcat #-}
540{-# INLINABLE mconcat #-}
@@ -558,7 +547,7 @@ mconcat = Fold (\x a -> return $ mappend x a) (return mempty) return
558-- Make a fold from a pure function that folds the output of the function
547-- Make a fold from a pure function that folds the output of the function
559-- using 'mappend' and 'mempty'.
548-- using 'mappend' and 'mempty'.
561−-- > S.runFold (FL.foldMap Sum) $ S.enumerateFromTo 1 10
550+-- > S.fold (FL.foldMap Sum) $ S.enumerateFromTo 1 10
563-- @since 0.7.0
552-- @since 0.7.0
564{-# INLINABLE foldMap #-}
553{-# INLINABLE foldMap #-}
@@ -571,7 +560,7 @@ foldMap f = lmap f mconcat
571-- Make a fold from a monadic function that folds the output of the function
560-- Make a fold from a monadic function that folds the output of the function
572-- using 'mappend' and 'mempty'.
561-- using 'mappend' and 'mempty'.
574−-- > S.runFold (FL.foldMapM (return . Sum)) $ S.enumerateFromTo 1 10
563+-- > S.fold (FL.foldMapM (return . Sum)) $ S.enumerateFromTo 1 10
576-- @since 0.7.0
565-- @since 0.7.0
577{-# INLINABLE foldMapM #-}
566{-# INLINABLE foldMapM #-}
@@ -769,7 +758,7 @@ or = Fold (\x a -> return $ x || a) (return False) return
769-- ---stream m a---| |---m (b,c)
758-- ---stream m a---| |---m (b,c)
770-- |-------Fold m a c--------|
759-- |-------Fold m a c--------|
772−-- >>> S.runFold (FL.tee FL.sum FL.length) (S.enumerateFromTo 1.0 100.0)
761+-- >>> S.fold (FL.tee FL.sum FL.length) (S.enumerateFromTo 1.0 100.0)
773-- (5050.0,100)
762-- (5050.0,100)
775-- @since 0.7.0
764-- @since 0.7.0
@@ -811,7 +800,7 @@ foldCons (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
814−-- >>> S.runFold (FL.distribute [FL.sum, FL.length]) (S.enumerateFromTo 1 5)
803+-- >>> S.fold (FL.distribute [FL.sum, FL.length]) (S.enumerateFromTo 1 5)
817-- This is the consumer side dual of the producer side 'sequence' operation.
806-- This is the consumer side dual of the producer side 'sequence' operation.
@@ -840,7 +829,7 @@ distribute (x:xs) = foldCons x (distribute xs)
841-- >>> import System.Random (randomIO)
830-- >>> import System.Random (randomIO)
842-- >>> randomly a = randomIO >>= \x -> return $ if x then Left a else Right a
831-- >>> randomly a = randomIO >>= \x -> return $ if x then Left a else Right a
843−-- >>> S.runFold (FL.partitionByM randomly FL.length FL.length) (S.enumerateFromTo 1 100)
832+-- >>> S.fold (FL.partitionByM randomly FL.length FL.length) (S.enumerateFromTo 1 100)
844-- (59,41)
833-- (59,41)
846-- Send input to the two folds in a proportion of 2:1:
835-- Send input to the two folds in a proportion of 2:1:
@@ -856,7 +845,7 @@ distribute (x:xs) = foldCons x (distribute xs)
857-- main = do
846-- main = do
858-- f <- proportionately 2 1
847-- f <- proportionately 2 1
859−-- r <- S.runFold (FL.partitionByM f FL.length FL.length) (S.enumerateFromTo (1 :: Int) 100)
848+-- r <- S.fold (FL.partitionByM f FL.length FL.length) (S.enumerateFromTo (1 :: Int) 100)
860-- print r
849-- print r
@@ -895,7 +884,7 @@ partitionByM f (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
895-- >>> let f = FL.partitionBy (\\n -> if even n then Left n else Right n)
884-- >>> let f = FL.partitionBy (\\n -> if even n then Left n else Right n)
896-- (fmap (("Even " ++) . show) FL.length)
885-- (fmap (("Even " ++) . show) FL.length)
897-- (fmap (("Odd " ++) . show) FL.length)
886-- (fmap (("Odd " ++) . show) FL.length)
898−-- in S.runFold f (S.enumerateFromTo 1 100)
887+-- in S.fold f (S.enumerateFromTo 1 100)
899-- ("Even 50","Odd 50")
888-- ("Even 50","Odd 50")
@@ -982,7 +971,7 @@ demuxWith f kv = Fold step initial extract
983-- > let table = Data.Map.fromList [(\"SUM", FL.sum), (\"PRODUCT", FL.product)]
972-- > let table = Data.Map.fromList [(\"SUM", FL.sum), (\"PRODUCT", FL.product)]
984-- input = S.fromList [(\"SUM",1),(\"PRODUCT",2),(\"SUM",3),(\"PRODUCT",4)]
973-- input = S.fromList [(\"SUM",1),(\"PRODUCT",2),(\"SUM",3),(\"PRODUCT",4)]
985−-- in S.runFold (FL.demux table) input
974+-- in S.fold (FL.demux table) input
@@ -1041,7 +1030,7 @@ demuxWith_ f kv = Fold step initial extract
1041-- > let prn = FL.drainBy print
1030-- > let prn = FL.drainBy print
1042-- > let table = Data.Map.fromList [(\"ONE", prn), (\"TWO", prn)]
1031-- > let table = Data.Map.fromList [(\"ONE", prn), (\"TWO", prn)]
1043-- input = S.fromList [(\"ONE",1),(\"TWO",2)]
1032-- input = S.fromList [(\"ONE",1),(\"TWO",2)]
1044−-- in S.runFold (FL.demux_ table) input
1033+-- in S.fold (FL.demux_ table) input
@@ -1061,7 +1050,7 @@ demux_ = demuxWith_ id
1063-- > let input = S.fromList [(\"ONE",1),(\"ONE",1.1),(\"TWO",2), (\"TWO",2.2)]
1052-- > let input = S.fromList [(\"ONE",1),(\"ONE",1.1),(\"TWO",2), (\"TWO",2.2)]
1064−-- in S.runFold (FL.classify FL.toList) input
1053+-- in S.fold (FL.classify FL.toList) input
1065-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
1054-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
@@ -1091,7 +1080,7 @@ classifyWith f (Fold step initial extract) = Fold step' initial' extract'
1093-- > let input = S.fromList [(\"ONE",1),(\"ONE",1.1),(\"TWO",2), (\"TWO",2.2)]
1082-- > let input = S.fromList [(\"ONE",1),(\"ONE",1.1),(\"TWO",2), (\"TWO",2.2)]
1094−-- in S.runFold (FL.classify FL.toList) input
1083+-- in S.fold (FL.classify FL.toList) input
1095-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
1084-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]