on 63c646a help sign in
270 lines 8.1 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-- = Performance Notes
26--
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').
39--
40-- = Left folds vs Right Folds
41--
42-- The folds in this module are left folds, therefore, even partial folds, e.g.
43-- @head@ in this module, would drain the whole stream. On the other hand, the
44-- partial folds in "Streamly.Prelude" module are lazy right folds and would
45-- terminate as soon as the result is determined. However, the folds in this
46-- module can be composed but the folds in "Streamly.Prelude" cannot be
47-- composed.
48--
49-- = Programmer Notes
50--
51-- > import qualified Streamly.Data.Fold as FL
52--
53-- More, not yet exposed, fold combinators can be found in
54-- "Streamly.Internal.Data.Fold".
56-- IMPORTANT: keep the signatures consistent with the folds in Streamly.Prelude
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 -- , tail
71 -- , init
73 -- ** Full Folds
74 , drain
75 , drainBy
76 , last
77 , length
78 , sum
79 , product
80 , maximumBy
81 , maximum
82 , minimumBy
83 , minimum
84 -- , the
85 , mean
86 , variance
87 , stdDev
89 -- ** Full Folds (Monoidal)
90 , mconcat
91 , foldMap
92 , foldMapM
94 -- ** Full Folds (To Containers)
95 -- | Avoid using these folds in scalable or performance critical
96 -- applications, they buffer all the input in GC memory which can be
97 -- detrimental to performance if the input is large.
99 , toList
101 -- ** Partial Folds
102 -- , drainN
103 -- , drainWhile
104 -- , lastN
105 -- , (!!)
106 -- , genericIndex
107 , index
108 , head
109 -- , findM
110 , find
111 , lookup
112 , findIndex
113 , elemIndex
114 , null
115 , elem
116 , notElem
117 -- XXX these are slower than right folds even when full input is used
118 , all
119 , any
120 , and
121 , or
123 -- * Transformations
124 -- | Unlike stream producer types (e.g. @SerialT m a@) which have only
125 -- output side, folds have an input side as well as an output side. In the
126 -- type @Fold m a b@, the input type is @a@ and the output type is @b@.
127 -- Transformations can be applied either on the input side or on the output
128 -- side. The 'Functor' instance of a fold maps on the output of the fold:
129 --
130 -- >>> S.fold (fmap show FL.sum) (S.enumerateFromTo 1 100)
131 -- "5050"
132 --
133 -- However, the input side or contravariant transformations are more
134 -- interesting for folds. The following sections describe the input
135 -- transformation operations on a fold. The names of the operations are
136 -- consistent with their covariant counterparts in "Streamly.Prelude", the
137 -- only difference is that they are prefixed with 'l' which stands for
138 -- 'left' assuming left side is the input side, notice that in @Fold m a b@
139 -- the type variable @a@ is on the left side.
141 -- ** Covariant Operations
142 , sequence
143 , mapM
145 {-
146 -- ** Mapping
147 --, transform
148 -- , lmap
149 --, lsequence
150 -- , lmapM
152 -- -- ** Filtering
153 -- , lfilter
154 -- , lfilterM
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 -- * Parsing
178 -- ** Trimming
179 , ltake
180 -- , lrunFor -- time
181 , ltakeWhile
182 , ltakeWhileM
183 , ldrop
184 , ldropWhile
185 , ldropWhileM
186 -}
188 -- * Distributing
189 -- |
190 -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
191 -- of the stream to each fold and combines the results using a function.
192 --
193 -- @
194 --
195 -- |-------Fold m a b--------|
196 -- ---stream m a---| |---m (b,c,...)
197 -- |-------Fold m a c--------|
198 -- | |
199 -- ...
200 -- @
201 --
202 -- To compute the average of numbers in a stream without going through the
203 -- stream twice:
204 --
205 -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
206 -- >>> S.fold avg (S.enumerateFromTo 1.0 100.0)
207 -- 50.5
208 --
209 -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
210 -- the input to both the folds and combines the outputs using Monoid or
211 -- Semigroup instances of the output types:
212 --
213 -- >>> import Data.Monoid (Sum)
214 -- >>> S.fold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
215 -- Just (Sum {getSum = 101.0})
216 --
217 -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.
219 , tee
220 , distribute
222 -- * Partitioning
223 -- |
224 -- Direct items in the input stream to different folds using a binary
225 -- fold selector.
227 -- , partitionByM
228 -- , partitionBy
229 , partition
231 {-
232 -- * Demultiplexing
233 -- | Direct values in the input stream to different folds using an n-ary
234 -- fold selector.
236 , demux
237 -- , demuxWith
238 , demux_
239 -- , demuxWith_
241 -- * Classifying
242 -- | In an input stream of key value pairs fold values for different keys
243 -- in individual output buckets using the given fold.
245 , classify
246 -- , classifyWith
247 -}
249 -- * Unzipping
250 , unzip
251 -- These can be expressed using lmap/lmapM and unzip
252 -- , unzipWith
253 -- , unzipWithM
255 -- -- * Nested Folds
256 -- , concatMap
257 -- , chunksOf
258 -- , duplicate -- experimental
259 )
260where
262import Prelude
263 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
264 foldl, map, mapM_, sequence, all, any, sum, product, elem,
265 notElem, maximum, minimum, head, last, tail, length, null,
266 reverse, iterate, init, and, or, lookup, foldr1, (!!),
267 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
268 span, splitAt, break, mapM)
270import Streamly.Internal.Data.Fold