@@ -2,45 +2,58 @@
22-- Module : Streamly.Data.Fold
33-- Copyright : (c) 2019 Composewell Technologies
44-- License : BSD3
5−-- Maintainer : harendra@composewell.com
5+-- Maintainer : streamly@composewell.com
66-- Stability : experimental
77-- Portability : GHC
88--
9−-- A left fold consumes a stream and reduces it to a single value. The fold
10−-- operations in "Streamly.Prelude" cannot be combined such that multiple of
11−-- them can run on the same stream. This module provides a 'Fold' type that
12−-- represents a left fold. Multiple such folds can be combined using
9+-- 'Fold' type represents an effectful action that consumes a value from an
10+-- input stream and combines it with a single final value often called an
11+-- accumulator, returning the resulting output accumulator. Values from a
12+-- stream can be /pushed/ to the fold and consumed one at a time. It can also
13+-- be called a consumer of stream or a sink. It is a data representation of
14+-- the standard 'Streamly.Prelude.foldl'' function. A 'Fold' can be turned
15+-- into an effect (@m b@) using 'Streamly.Prelude.fold' by supplying it the
16+-- input stream.
17+--
18+-- Using this representation multiple folds can be combined efficiently using
1319-- combinators; a stream can then be supplied to the combined fold and it would
14−-- distribute the input to constituent folds according to the composition.
15−-- For example, an applicative composition distributes the same input to
16−-- the constituent folds and then combines the fold outputs. Similarly, a
17−-- partitioning combinator can divide the input among constituent folds.
18−-- All the combinators in this module are of true streaming nature, stream
19−-- elements are not unnecessarily buffered in memory, guaranteeing a constant
20−-- memory consumption.
20+-- distribute the input to constituent folds according to the composition. For
21+-- example, an applicative composition distributes the same input to the
22+-- constituent folds and then combines the resulting fold outputs. Similarly,
23+-- a partitioning combinator divides the input among constituent folds.
2124--
22−-- Consider this module as the consumer side dual of the "Streamly.Prelude"
23−-- module. "Streamly.Prelude" provides combinators that can combine stream
24−-- sources in interesting ways whereas this module provides combinators that
25−-- combine stream consumers in interesting ways. In other words,
26−-- "Streamly.Prelude" provides stream merging capabilities while
27−-- "Streamly.Internal.Data.Fold" provides stream splitting capabilities.
25+-- = Performance Notes
2826--
29−-- > import qualified Streamly.Internal.Data.Fold as FL
30−
31−-- Also see the "Streamly.Internal.Data.Sink" module that provides specialized left folds
32−-- that discard the outputs.
27+-- 'Fold' representation is more efficient than using streams when splitting
28+-- streams. @Fold m a b@ can be considered roughly equivalent to a fold action
29+-- @m b -> t m a -> m b@ (where @t@ is a stream type and @m@ is a 'Monad').
30+-- Instead of using a 'Fold' type one could just use a fold action of the shape
31+-- @m b -> t m a -> m b@ for folding streams. However, multiple such actions
32+-- cannot be composed into a single fold function in an efficient manner.
33+-- Using the 'Fold' type we can efficiently split the stream across mutliple
34+-- folds because it allows the compiler to perform stream fusion optimizations.
35+--
36+-- On the other hand, transformation operations (e.g. 'Streamly.Prelude.map')
37+-- on stream types can be as efficient as transformations on 'Fold' (e.g.
38+-- 'Streamly.Internal.Data.Fold.lmap').
3339--
40+-- = Programmer Notes
41+--
42+-- > import qualified Streamly.Data.Fold as FL
43+--
44+-- More, not yet exposed, fold combinators can be found in
45+-- "Streamly.Internal.Data.Fold".
46+
3447-- IMPORTANT: keep the signatures consistent with the folds in Streamly.Prelude
3548
3649module Streamly.Data.Fold
3750 (
3851 -- * Fold Type
3952 -- |
40− -- A 'Fold' can be run over a stream using the 'runFold' combinator in
41− -- "Streamly.Prelude":
53+ -- A 'Fold' can be run over a stream using the 'Streamly.Prelude.fold'
54+ -- combinator:
4255 --
43− -- >>> S.runFold FL.sum (S.enumerateFromTo 1 100)
56+ -- >>> S.fold FL.sum (S.enumerateFromTo 1 100)
4457 -- 5050
4558
4659 Fold -- (..)
@@ -105,7 +118,7 @@ module Streamly.Data.Fold
105118 -- Transformations can be applied either on the input side or on the output
106119 -- side. The 'Functor' instance of a fold maps on the output of the fold:
107120 --
108− -- >>> S.runFold (fmap show FL.sum) (S.enumerateFromTo 1 100)
121+ -- >>> S.fold (fmap show FL.sum) (S.enumerateFromTo 1 100)
109122 -- "5050"
110123 --
111124 -- However, the input side or contravariant transformations are more
@@ -181,7 +194,7 @@ module Streamly.Data.Fold
181194 -- stream twice:
182195 --
183196 -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
184− -- >>> S.runFold avg (S.enumerateFromTo 1.0 100.0)
197+ -- >>> S.fold avg (S.enumerateFromTo 1.0 100.0)
185198 -- 50.5
186199 --
187200 -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
@@ -189,7 +202,7 @@ module Streamly.Data.Fold
189202 -- Semigroup instances of the output types:
190203 --
191204 -- >>> import Data.Monoid (Sum)
192− -- >>> S.runFold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
205+ -- >>> S.fold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
193206 -- Just (Sum {getSum = 101.0})
194207 --
195208 -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.