on 30ea7f1 help sign in
267 lines 7.8 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-- The 'Fold' type represents an effectful action that absorbs a value into an
10-- accumulator. For example, a 'sum' 'Fold' represents adding the input to the
11-- accumulated sum. A fold driver e.g. 'Streamly.Prelude.fold' pushes values
12-- from a stream to the 'Fold' one at a time, reducing the stream to a single
13-- value. Therefore, a fold can also be thought of as a sink or a stream
14-- consumer.
15--
16-- A 'Fold' is in fact a data representation of a standard left fold ('foldl').
17-- Unlike the standard left folds, 'Fold's can terminate at any point e.g. the
18-- 'head' fold would terminate immediately after returning the head element.
19--
20-- 'Fold's can be combined efficiently using combinators provided in this
21-- module; a stream supplied to the combined fold is provided to the
22-- constituent folds according to the behavior of the combinator. For example,
23-- the 'tee' combinator distributes the input stream to two folds and then
24-- combines the resulting fold outputs. Similarly, a 'partition' combinator
25-- divides the input among constituent folds.
26--
27-- = Accumulators and Terminating Folds
28--
29-- Folds in this module can be classified in two categories viz. accumulators
30-- and terminating folds. Accumulators do not have a terminating condition,
31-- they run forever and consume the entire stream, for example the 'length'
32-- fold. Terminating folds have a terminating condition and can terminate
33-- without consuming the entire stream, for example, the 'head' fold.
34--
35-- = Monoids
36--
37-- Monoids allow generalized, modular folding. The accumulators in this module
38-- can be expressed using 'mconcat' and a suitable 'Monoid'. Instead of
39-- writing folds we can write Monoids and turn them into folds.
40--
41-- = Performance Notes
42--
43-- 'Streamly.Prelude' module provides fold functions to directly fold streams
44-- e.g. Streamly.Prelude/'Streamly.Prelude.sum' serves the same purpose as
45-- Fold/'sum'. However, the functions in Streamly.Prelude cannot be
46-- efficiently combined together e.g. we cannot drive the input stream through
47-- @sum@ and @length@ fold functions simultaneously. Using the 'Fold' type we
48-- can efficiently split the stream across mutliple folds because it allows the
49-- compiler to perform stream fusion optimizations.
50--
51-- = Programmer Notes
52--
53-- > import qualified Streamly.Data.Fold as Fold
54--
55-- More, not yet exposed, fold combinators can be found in
56-- "Streamly.Internal.Data.Fold".
58module Streamly.Data.Fold
59 (
60 -- * Fold Type
61 -- |
62 -- A 'Fold' can be run over a stream using the 'Streamly.Prelude.fold'
63 -- combinator:
64 --
65 -- >>> S.fold FL.sum (S.enumerateFromTo 1 100)
66 -- 5050
68 Fold -- (..)
70 -- * Accumulators
71 -- ** Monoids
72 , mconcat
73 , foldMap
74 , foldMapM
76 -- ** Reducers
77 , drain
78 , drainBy
79 , last
80 , length
81 , sum
82 , product
83 , maximumBy
84 , maximum
85 , minimumBy
86 , minimum
87 , mean
88 , variance
89 , stdDev
91 -- ** Collectors
92 -- | Avoid using these folds in scalable or performance critical
93 -- applications, they buffer all the input in GC memory which can be
94 -- detrimental to performance if the input is large.
96 , toList
98 -- * Terminating Folds
99 -- , drainN
100 -- , drainWhile
101 -- , lastN
102 -- , (!!)
103 -- , genericIndex
104 , index
105 , head
106 -- , findM
107 , find
108 , lookup
109 , findIndex
110 , elemIndex
111 , null
112 , elem
113 , notElem
114 , all
115 , any
116 , and
117 , or
119 -- * Output Transformations
120 -- | Unlike stream producer types (e.g. @SerialT m a@) which have only
121 -- output side, folds have an input side as well as an output side. In the
122 -- type @Fold m a b@, the input type is @a@ and the output type is @b@.
123 -- Transformations can be applied either on the input side or on the output
124 -- side. The 'Functor' instance of a fold maps on the output of the fold:
125 --
126 -- >>> S.fold (fmap show FL.sum) (S.enumerateFromTo 1 100)
127 -- "5050"
128 --
129 -- Note: Output transformations are also known as covariant
130 -- transformations.
131 , rsequence
132 , sequence
133 , rmapM
134 , mapM
136 {-
137 -- * Input Transformations
138 -- The input side transformations are more interesting for folds. The
139 -- following sections describe the input transformation operations on a
140 -- fold. The names and signatures of the operations are consistent with
141 -- corresponding operations in "Streamly.Prelude".
142 --
143 -- Note: Input transformations are also known as contravariant
144 -- transformations.
146 -- ** Mapping
147 --, transform
148 -- , lmap
149 --, lsequence
150 -- , lmapM
152 -- -- ** Filtering
153 -- , filter
154 -- , filterM
155 -- , ldeleteBy
156 -- , luniq
158 -- ** Mapping Filters
159 , lmapMaybe
160 , lmapMaybeM
162 -- ** Scanning Filters
163 , lfindIndices
164 , lelemIndices
166 -- ** Insertion
167 -- | Insertion adds more elements to the stream.
169 , linsertBy
170 , lintersperseM
172 -- ** Reordering
173 , lreverse
174 -}
176 {-
177 -- ** Trimming
178 , takeLE
179 -- takeByTime
180 , ldrop
181 , ldropWhile
182 , ldropWhileM
183 -}
185 -- * Distributing
186 -- |
187 -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
188 -- of the stream to each fold and combines the results using a function.
189 --
190 -- @
191 --
192 -- |-------Fold m a b--------|
193 -- ---stream m a---| |---m (b,c,...)
194 -- |-------Fold m a c--------|
195 -- | |
196 -- ...
197 -- @
198 --
199 -- To compute the average of numbers in a stream without going through the
200 -- stream twice:
201 --
202 -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
203 -- >>> S.fold avg (S.enumerateFromTo 1.0 100.0)
204 -- 50.5
205 --
206 -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
207 -- the input to both the folds and combines the outputs using Monoid or
208 -- Semigroup instances of the output types:
209 --
210 -- >>> import Data.Monoid (Sum)
211 -- >>> S.fold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
212 -- Just (Sum {getSum = 101.0})
213 --
214 -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.
216 , tee
217 , distribute
219 -- * Partitioning
220 -- |
221 -- Direct items in the input stream to different folds using a binary
222 -- fold selector.
224 -- , partitionByM
225 -- , partitionBy
226 , partition
228 {-
229 -- * Demultiplexing
230 -- | Direct values in the input stream to different folds using an n-ary
231 -- fold selector.
233 , demux
234 -- , demuxWith
235 , demux_
236 -- , demuxWith_
238 -- * Classifying
239 -- | In an input stream of key value pairs fold values for different keys
240 -- in individual output buckets using the given fold.
242 , classify
243 -- , classifyWith
244 -}
246 -- * Unzipping
247 , unzip
248 -- These can be expressed using lmap/lmapM and unzip
249 -- , unzipWith
250 -- , unzipWithM
252 -- -- * Nesting
253 -- , concatMap
254 -- , chunksOf
255 -- , duplicate -- experimental
256 )
257where
259import Prelude
260 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
261 foldl, map, mapM_, sequence, all, any, sum, product, elem,
262 notElem, maximum, minimum, head, last, tail, length, null,
263 reverse, iterate, init, and, or, lookup, foldr1, (!!),
264 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
265 span, splitAt, break, mapM)
267import Streamly.Internal.Data.Fold