on d73f65c help sign in
261 lines 7.7 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-- = Programmer Notes
41--
42-- > import qualified Streamly.Data.Fold as FL
43--
44-- More, not yet exposed, fold combinators can be found in
45-- "Streamly.Internal.Data.Fold".
47-- IMPORTANT: keep the signatures consistent with the folds in Streamly.Prelude
49module Streamly.Data.Fold
50 (
51 -- * Fold Type
52 -- |
53 -- A 'Fold' can be run over a stream using the 'Streamly.Prelude.fold'
54 -- combinator:
55 --
56 -- >>> S.fold FL.sum (S.enumerateFromTo 1 100)
57 -- 5050
59 Fold -- (..)
61 -- , tail
62 -- , init
64 -- ** Full Folds
65 , drain
66 , drainBy
67 , last
68 , length
69 , sum
70 , product
71 , maximumBy
72 , maximum
73 , minimumBy
74 , minimum
75 -- , the
76 , mean
77 , variance
78 , stdDev
80 -- ** Full Folds (Monoidal)
81 , mconcat
82 , foldMap
83 , foldMapM
85 -- ** Full Folds (To Containers)
86 -- | Avoid using these folds in scalable or performance critical
87 -- applications, they buffer all the input in GC memory which can be
88 -- detrimental to performance if the input is large.
90 , toList
92 -- ** Partial Folds
93 -- , drainN
94 -- , drainWhile
95 -- , lastN
96 -- , (!!)
97 -- , genericIndex
98 , index
99 , head
100 -- , findM
101 , find
102 , lookup
103 , findIndex
104 , elemIndex
105 , null
106 , elem
107 , notElem
108 -- XXX these are slower than right folds even when full input is used
109 , all
110 , any
111 , and
112 , or
114 -- * Transformations
115 -- | Unlike stream producer types (e.g. @SerialT m a@) which have only
116 -- output side, folds have an input side as well as an output side. In the
117 -- type @Fold m a b@, the input type is @a@ and the output type is @b@.
118 -- Transformations can be applied either on the input side or on the output
119 -- side. The 'Functor' instance of a fold maps on the output of the fold:
120 --
121 -- >>> S.fold (fmap show FL.sum) (S.enumerateFromTo 1 100)
122 -- "5050"
123 --
124 -- However, the input side or contravariant transformations are more
125 -- interesting for folds. The following sections describe the input
126 -- transformation operations on a fold. The names of the operations are
127 -- consistent with their covariant counterparts in "Streamly.Prelude", the
128 -- only difference is that they are prefixed with 'l' which stands for
129 -- 'left' assuming left side is the input side, notice that in @Fold m a b@
130 -- the type variable @a@ is on the left side.
132 -- ** Covariant Operations
133 , sequence
134 , mapM
136 {-
137 -- ** Mapping
138 --, transform
139 -- , lmap
140 --, lsequence
141 -- , lmapM
143 -- -- ** Filtering
144 -- , lfilter
145 -- , lfilterM
146 -- , ldeleteBy
147 -- , luniq
149 -- ** Mapping Filters
150 , lmapMaybe
151 , lmapMaybeM
153 -- ** Scanning Filters
154 , lfindIndices
155 , lelemIndices
157 -- ** Insertion
158 -- | Insertion adds more elements to the stream.
160 , linsertBy
161 , lintersperseM
163 -- ** Reordering
164 , lreverse
165 -}
167 {-
168 -- * Parsing
169 -- ** Trimming
170 , ltake
171 -- , lrunFor -- time
172 , ltakeWhile
173 , ltakeWhileM
174 , ldrop
175 , ldropWhile
176 , ldropWhileM
177 -}
179 -- * Distributing
180 -- |
181 -- The 'Applicative' instance of a distributing 'Fold' distributes one copy
182 -- of the stream to each fold and combines the results using a function.
183 --
184 -- @
185 --
186 -- |-------Fold m a b--------|
187 -- ---stream m a---| |---m (b,c,...)
188 -- |-------Fold m a c--------|
189 -- | |
190 -- ...
191 -- @
192 --
193 -- To compute the average of numbers in a stream without going through the
194 -- stream twice:
195 --
196 -- >>> let avg = (/) <$> FL.sum <*> fmap fromIntegral FL.length
197 -- >>> S.fold avg (S.enumerateFromTo 1.0 100.0)
198 -- 50.5
199 --
200 -- The 'Semigroup' and 'Monoid' instances of a distributing fold distribute
201 -- the input to both the folds and combines the outputs using Monoid or
202 -- Semigroup instances of the output types:
203 --
204 -- >>> import Data.Monoid (Sum)
205 -- >>> S.fold (FL.head <> FL.last) (fmap Sum $ S.enumerateFromTo 1.0 100.0)
206 -- Just (Sum {getSum = 101.0})
207 --
208 -- The 'Num', 'Floating', and 'Fractional' instances work in the same way.
210 , tee
211 , distribute
213 -- * Partitioning
214 -- |
215 -- Direct items in the input stream to different folds using a binary
216 -- fold selector.
218 -- , partitionByM
219 -- , partitionBy
220 , partition
222 {-
223 -- * Demultiplexing
224 -- | Direct values in the input stream to different folds using an n-ary
225 -- fold selector.
227 , demux
228 -- , demuxWith
229 , demux_
230 -- , demuxWith_
232 -- * Classifying
233 -- | In an input stream of key value pairs fold values for different keys
234 -- in individual output buckets using the given fold.
236 , classify
237 -- , classifyWith
238 -}
240 -- * Unzipping
241 , unzip
242 -- These can be expressed using lmap/lmapM and unzip
243 -- , unzipWith
244 -- , unzipWithM
246 -- -- * Nested Folds
247 -- , concatMap
248 -- , chunksOf
249 -- , duplicate -- experimental
250 )
251where
253import Prelude
254 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
255 foldl, map, mapM_, sequence, all, any, sum, product, elem,
256 notElem, maximum, minimum, head, last, tail, length, null,
257 reverse, iterate, init, and, or, lookup, foldr1, (!!),
258 scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
259 span, splitAt, break, mapM)
261import Streamly.Internal.Data.Fold