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