on e3e60ca help sign in
289 lines 8.5 kB history raw
1-- |
2-- Module : Streamly.Data.Fold
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : released
7-- Portability : GHC
8--
9-- A 'Fold' is a sink or a consumer of a stream of values. The 'Fold' type
10-- consists of an accumulator and an effectful action that absorbs a value into
11-- the accumulator.
12--
13-- >>> import Data.Function ((&))
14-- >>> import qualified Streamly.Data.Fold as Fold
15-- >>> import qualified Streamly.Data.Stream as Stream
16--
17-- For example, a 'sum' Fold represents adding the input to the accumulated
18-- sum. A fold driver e.g. 'Streamly.Prelude.fold' pushes values from a stream
19-- to the 'Fold' one at a time, reducing the stream to a single value.
20--
21-- >>> Stream.fold Fold.sum $ Stream.fromList [1..100]
22-- 5050
23--
24-- Conceptually, a 'Fold' is a data type that can mimic a strict left fold
25-- ('Data.List.foldl') as well as lazy right fold ('Prelude.foldr'). The above
26-- example is similar to a left fold using @(+)@ as the step and @0@ as the
27-- initial value of the accumulator:
28--
29-- >>> Data.List.foldl' (+) 0 [1..100]
30-- 5050
31--
32-- 'Fold's have an early termination capability e.g. the 'head' fold would
33-- terminate on an infinite stream:
34--
35-- >>> Stream.fold Fold.head $ Stream.fromList [1..]
36-- Just 1
37--
38-- The above example is similar to the following right fold:
39--
40-- >>> Prelude.foldr (\x _ -> Just x) Nothing [1..]
41-- Just 1
42--
43-- 'Fold's can be combined together using combinators. For example, to create a
44-- fold that sums first two elements in a stream:
45--
46-- >>> sumTwo = Fold.take 2 Fold.sum
47-- >>> Stream.fold sumTwo $ Stream.fromList [1..100]
48-- 3
49--
50-- Folds can be combined to run in parallel on the same input. For example, to
51-- compute the average of numbers in a stream without going through the stream
52-- twice:
53--
54-- >>> avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
55-- >>> Stream.fold avg $ Stream.fromList [1.0..100.0]
56-- 50.5
57--
58-- Folds can be combined so as to partition the input stream over multiple
59-- folds. For example, to count even and odd numbers in a stream:
60--
61-- >>> split n = if even n then Left n else Right n
62-- >>> stream = fmap split $ Stream.fromList [1..100]
63-- >>> countEven = fmap (("Even " ++) . show) Fold.length
64-- >>> countOdd = fmap (("Odd " ++) . show) Fold.length
65-- >>> f = Fold.partition countEven countOdd
66-- >>> Stream.fold f stream
67-- ("Even 50","Odd 50")
68--
69-- Terminating folds can be combined to parse the stream serially such that the
70-- first fold consumes the input until it terminates and the second fold
71-- consumes the rest of the input until it terminates:
72--
73-- >>> f = Fold.serialWith (,) (Fold.take 8 Fold.toList) (Fold.takeEndBy (== '\n') Fold.toList)
74-- >>> Stream.fold f $ Stream.fromList "header: hello\n"
75-- ("header: ","hello\n")
76--
77-- A 'Fold' can be applied repeatedly on a stream to transform it to a stream
78-- of fold results. To split a stream on newlines:
79--
80-- >>> f = Fold.takeEndBy (== '\n') Fold.toList
81-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.fromList "Hello there!\nHow are you\n"
82-- ["Hello there!\n","How are you\n"]
83--
84-- Similarly, we can split the input of a fold too:
85--
86-- >>> Stream.fold (Fold.many f Fold.toList) $ Stream.fromList "Hello there!\nHow are you\n"
87-- ["Hello there!\n","How are you\n"]
88--
89-- Please see "Streamly.Internal.Data.Fold" for additional @Pre-release@
90-- functions.
91--
92-- = Folds vs. Streams
93--
94-- We can often use streams or folds to achieve the same goal. However, streams
95-- are more efficient in composition of producers (e.g.
96-- 'Streamly.Prelude.serial' or 'Streamly.Prelude.mergeBy') whereas folds are
97-- more efficient in composition of consumers (e.g. 'serialWith', 'partition'
98-- or 'teeWith').
99--
100-- Streams are producers, transformations on streams happen on the output side:
101--
102-- >>> :{
103-- f stream =
104-- Stream.filter odd stream
105-- & fmap (+1)
106-- & Stream.fold Fold.sum
107-- :}
108--
109-- >>> f $ Stream.fromList [1..100 :: Int]
110-- 2550
111--
112-- Folds are stream consumers with an input stream and an output value, stream
113-- transformations on folds happen on the input side:
114--
115-- >>> :{
116-- f =
117-- Fold.filter odd
118-- $ Fold.lmap (+1)
119-- $ Fold.sum
120-- :}
121--
122-- >>> Stream.fold f $ Stream.fromList [1..100 :: Int]
123-- 2550
124--
125-- Notice the similiarity in the definition of @f@ in both cases, the only
126-- difference is the composition by @&@ vs @$@ and the use @lmap@ vs @map@, the
127-- difference is due to output vs input side transformations.
129module Streamly.Data.Fold
130 (
131 -- * Fold Type
133 Fold -- (..)
135 -- * Constructors
136 , foldl'
137 , foldlM'
138 , foldr
140 -- * Folds
141 -- ** Accumulators
142 -- | Folds that never terminate, these folds are much like strict left
143 -- folds. 'mconcat' is the fundamental accumulator. All other accumulators
144 -- can be expressed in terms of 'mconcat' using a suitable Monoid. Instead
145 -- of writing folds we could write Monoids and turn them into folds.
147 -- Monoids
148 , sconcat
149 , mconcat
150 , foldMap
151 , foldMapM
153 -- Reducers
154 , drain
155 , drainBy
156 , last
157 , length
158 , sum
159 , product
160 , maximumBy
161 , maximum
162 , minimumBy
163 , minimum
164 , mean
165 , rollingHash
166 , rollingHashWithSalt
168 -- Collectors
169 , toList
170 , toListRev
172 -- ** Terminating Folds
173 -- | These are much like lazy right folds.
175 , index
176 , head
177 , find
178 , lookup
179 , findIndex
180 , elemIndex
181 , null
182 , elem
183 , notElem
184 , all
185 , any
186 , and
187 , or
189 -- * Combinators
190 -- | Combinators are modifiers of folds. In the type @Fold m a b@, @a@ is
191 -- the input type and @b@ is the output type. Transformations can be
192 -- applied either on the input side or on the output side. Therefore,
193 -- combinators are of one of the following general shapes:
194 --
195 -- * @... -> Fold m a b -> Fold m c b@ (input transformation)
196 -- * @... -> Fold m a b -> Fold m a c@ (output transformation)
197 --
198 -- Output transformations are also known as covariant transformations, and
199 -- input transformations are also known as contravariant transformations.
200 -- The input side transformations are more interesting for folds. Most of
201 -- the following sections describe the input transformation operations on a
202 -- fold. The names and signatures of the operations are consistent with
203 -- corresponding operations in "Streamly.Prelude". When an operation makes
204 -- sense on both input and output side we use the prefix @l@ (for left) for
205 -- input side operations and the prefix @r@ (for right) for output side
206 -- operations.
208 -- ** Mapping on output
209 -- | The 'Functor' instance of a fold maps on the output of the fold:
210 --
211 -- >>> Stream.fold (fmap show Fold.sum) (Stream.enumerateFromTo 1 100)
212 -- "5050"
213 --
214 , rmapM
216 -- ** Mapping on Input
217 , lmap
218 , lmapM
220 -- ** Filtering
221 , filter
222 , filterM
224 -- -- ** Mapping Filters
225 , catMaybes
226 , mapMaybe
228 -- ** Trimming
229 , take
230 -- , takeInterval
231 , takeEndBy_
232 , takeEndBy
234 -- ** Serial Append
235 , serialWith
237 -- ** Parallel Distribution
238 -- | For applicative composition using distribution see
239 -- "Streamly.Internal.Data.Fold.Tee".
241 , teeWith
242 --, teeWithFst
243 --, teeWithMin
244 , tee
245 , distribute
247 -- ** Partitioning
248 -- | Direct items in the input stream to different folds using a binary
249 -- fold selector.
251 , partition
252 --, partitionByM
253 --, partitionByFstM
254 --, partitionByMinM
255 --, partitionBy
257 -- ** Unzipping
258 , unzip
260 -- ** Splitting
261 , many
262 , chunksOf
263 -- , intervalsOf
265 -- ** Nesting
266 , concatMap
268 -- * Deprecated
269 , sequence
270 , mapM
271 , variance
272 , stdDev
273 )
274where
276import Prelude
277 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
278 foldl, map, mapM_, sequence, all, any, sum, product, elem,
279 notElem, maximum, minimum, head, last, tail, length, null,
280 reverse, iterate, init, and, or, lookup, foldr1, (!!),
281 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
282 span, splitAt, break, mapM)
284import Streamly.Internal.Data.Fold
286-- $setup
287-- >>> import Data.Function ((&))
288-- >>> import qualified Streamly.Data.Fold as Fold
289-- >>> import qualified Streamly.Data.Stream as Stream