on 8a93c0c help sign in
276 lines 8.4 kB history raw
1-- |
2-- Module : Streamly.Data.Fold
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD3
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
7-- Portability : GHC
8--
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
19-- combinators; a stream can then be supplied to the combined fold and it would
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.
24--
25-- = Monoids
26--
27-- Monoids allow modular folding in general. Most of the folds in this module
28-- can be expressed using 'mconcat' and a suitable 'Monoid'. Instead of writing
29-- folds we can write Monoids instead and turn them into folds.
30--
31-- = Performance Notes
32--
33-- 'Fold' representation is more efficient than using streams when splitting
34-- streams. @Fold m a b@ can be considered roughly equivalent to a fold action
35-- @m b -> t m a -> m b@ (where @t@ is a stream type and @m@ is a 'Monad').
36-- Instead of using a 'Fold' type one could just use a fold action of the shape
37-- @m b -> t m a -> m b@ for folding streams. However, multiple such actions
38-- cannot be composed into a single fold function in an efficient manner.
39-- Using the 'Fold' type we can efficiently split the stream across mutliple
40-- folds because it allows the compiler to perform stream fusion optimizations.
41--
42-- On the other hand, transformation operations (e.g. 'Streamly.Prelude.map')
43-- on stream types can be as efficient as transformations on 'Fold' (e.g.
44-- 'Streamly.Internal.Data.Fold.lmap').
45--
46-- = Left folds vs Right Folds
47--
48-- The folds in this module are left folds, therefore, even partial folds, e.g.
49-- @head@ in this module, would drain the whole stream. On the other hand, the
50-- partial folds in "Streamly.Prelude" module are lazy right folds and would
51-- terminate as soon as the result is determined. However, the folds in this
52-- module can be composed but the folds in "Streamly.Prelude" cannot be
53-- composed.
54--
55-- = Programmer Notes
56--
57-- > import qualified Streamly.Data.Fold as FL
58--
59-- More, not yet exposed, fold combinators can be found in
60-- "Streamly.Internal.Data.Fold".
62-- IMPORTANT: keep the signatures consistent with the folds in Streamly.Prelude
64module Streamly.Data.Fold
65 (
66 -- * Fold Type
67 -- |
68 -- A 'Fold' can be run over a stream using the 'Streamly.Prelude.fold'
69 -- combinator:
70 --
71 -- >>> S.fold FL.sum (S.enumerateFromTo 1 100)
72 -- 5050
74 Fold -- (..)
76 -- , tail
77 -- , init
79 -- ** Full Folds
80 , drain
81 , drainBy
82 , last
83 , length
84 , sum
85 , product
86 , maximumBy
87 , maximum
88 , minimumBy
89 , minimum
90 -- , the
91 , mean
92 , variance
93 , stdDev
95 -- ** Full Folds (Monoidal)
96 , mconcat
97 , foldMap
98 , foldMapM
100 -- ** Full Folds (To Containers)
101 -- | Avoid using these folds in scalable or performance critical
102 -- applications, they buffer all the input in GC memory which can be
103 -- detrimental to performance if the input is large.
105 , toList
107 -- ** Partial Folds
108 -- , drainN
109 -- , drainWhile
110 -- , lastN
111 -- , (!!)
112 -- , genericIndex
113 , index
114 , head
115 -- , findM
116 , find
117 , lookup
118 , findIndex
119 , elemIndex
120 , null
121 , elem
122 , notElem
123 -- XXX these are slower than right folds even when full input is used
124 , all
125 , any
126 , and
127 , or
129 -- * Transformations
130 -- | Unlike stream producer types (e.g. @SerialT m a@) which have only
131 -- output side, folds have an input side as well as an output side. In the
132 -- type @Fold m a b@, the input type is @a@ and the output type is @b@.
133 -- Transformations can be applied either on the input side or on the output
134 -- side. The 'Functor' instance of a fold maps on the output of the fold:
135 --
136 -- >>> S.fold (fmap show FL.sum) (S.enumerateFromTo 1 100)
137 -- "5050"
138 --
139 -- However, the input side or contravariant transformations are more
140 -- interesting for folds. The following sections describe the input
141 -- transformation operations on a fold. The names of the operations are
142 -- consistent with their covariant counterparts in "Streamly.Prelude", the
143 -- only difference is that they are prefixed with 'l' which stands for
144 -- 'left' assuming left side is the input side, notice that in @Fold m a b@
145 -- the type variable @a@ is on the left side.
147 -- ** Covariant Operations
148 , sequence
149 , mapM
151 {-
152 -- ** Mapping
153 --, transform
154 -- , lmap
155 --, lsequence
156 -- , lmapM
158 -- -- ** Filtering
159 -- , lfilter
160 -- , lfilterM
161 -- , ldeleteBy
162 -- , luniq
164 -- ** Mapping Filters
165 , lmapMaybe
166 , lmapMaybeM
168 -- ** Scanning Filters
169 , lfindIndices
170 , lelemIndices
172 -- ** Insertion
173 -- | Insertion adds more elements to the stream.
175 , linsertBy
176 , lintersperseM
178 -- ** Reordering
179 , lreverse
180 -}
182 {-
183 -- * Parsing
184 -- ** Trimming
185 , ltake
186 -- , lrunFor -- time
187 , ltakeWhile
188 , ltakeWhileM
189 , ldrop
190 , ldropWhile
191 , ldropWhileM
192 -}
194 -- * Distributing
195 -- |
196 -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
197 -- of the stream to each fold and combines the results using a function.
198 --
199 -- @
200 --
201 -- |-------Fold m a b--------|
202 -- ---stream m a---| |---m (b,c,...)
203 -- |-------Fold m a c--------|
204 -- | |
205 -- ...
206 -- @
207 --
208 -- To compute the average of numbers in a stream without going through the
209 -- stream twice:
210 --
211 -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
212 -- >>> S.fold avg (S.enumerateFromTo 1.0 100.0)
213 -- 50.5
214 --
215 -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
216 -- the input to both the folds and combines the outputs using Monoid or
217 -- Semigroup instances of the output types:
218 --
219 -- >>> import Data.Monoid (Sum)
220 -- >>> S.fold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
221 -- Just (Sum {getSum = 101.0})
222 --
223 -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.
225 , tee
226 , distribute
228 -- * Partitioning
229 -- |
230 -- Direct items in the input stream to different folds using a binary
231 -- fold selector.
233 -- , partitionByM
234 -- , partitionBy
235 , partition
237 {-
238 -- * Demultiplexing
239 -- | Direct values in the input stream to different folds using an n-ary
240 -- fold selector.
242 , demux
243 -- , demuxWith
244 , demux_
245 -- , demuxWith_
247 -- * Classifying
248 -- | In an input stream of key value pairs fold values for different keys
249 -- in individual output buckets using the given fold.
251 , classify
252 -- , classifyWith
253 -}
255 -- * Unzipping
256 , unzip
257 -- These can be expressed using lmap/lmapM and unzip
258 -- , unzipWith
259 -- , unzipWithM
261 -- -- * Nested Folds
262 -- , concatMap
263 -- , chunksOf
264 -- , duplicate -- experimental
265 )
266where
268import Prelude
269 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
270 foldl, map, mapM_, sequence, all, any, sum, product, elem,
271 notElem, maximum, minimum, head, last, tail, length, null,
272 reverse, iterate, init, and, or, lookup, foldr1, (!!),
273 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
274 span, splitAt, break, mapM)
276import Streamly.Internal.Data.Fold