streamly-0.8.0: src/Streamly/Data/Fold.hs
-- |
-- Module : Streamly.Data.Fold
-- Copyright : (c) 2019 Composewell Technologies
-- License : BSD-3-Clause
-- Maintainer : streamly@composewell.com
-- Stability : released
-- Portability : GHC
--
-- A 'Fold' is a sink or a consumer of a stream of values. The 'Fold' type
-- consists of an accumulator and an effectful action that absorbs a value into
-- the accumulator.
--
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Prelude as Stream
--
-- For example, a 'sum' Fold represents adding the input to the accumulated
-- sum. A fold driver e.g. 'Streamly.Prelude.fold' pushes values from a stream
-- to the 'Fold' one at a time, reducing the stream to a single value.
--
-- >>> Stream.fold Fold.sum $ Stream.fromList [1..100]
-- 5050
--
-- Conceptually, a 'Fold' is a data type that can mimic a strict left fold
-- ('Data.List.foldl') as well as lazy right fold ('Prelude.foldr'). The above
-- example is similar to a left fold using @(+)@ as the step and @0@ as the
-- initial value of the accumulator:
--
-- >>> Data.List.foldl' (+) 0 [1..100]
-- 5050
--
-- 'Fold's have an early termination capability e.g. the 'head' fold would
-- terminate on an infinite stream:
--
-- >>> Stream.fold Fold.head $ Stream.fromList [1..]
-- Just 1
--
-- The above example is similar to the following right fold:
--
-- >>> Prelude.foldr (\x _ -> Just x) Nothing [1..]
-- Just 1
--
-- 'Fold's can be combined together using combinators. For example, to create a
-- fold that sums first two elements in a stream:
--
-- >>> sumTwo = Fold.take 2 Fold.sum
-- >>> Stream.fold sumTwo $ Stream.fromList [1..100]
-- 3
--
-- Folds can be combined to run in parallel on the same input. For example, to
-- compute the average of numbers in a stream without going through the stream
-- twice:
--
-- >>> avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
-- >>> Stream.fold avg $ Stream.fromList [1.0..100.0]
-- 50.5
--
-- Folds can be combined so as to partition the input stream over multiple
-- folds. For example, to count even and odd numbers in a stream:
--
-- >>> split n = if even n then Left n else Right n
-- >>> stream = Stream.map split $ Stream.fromList [1..100]
-- >>> countEven = fmap (("Even " ++) . show) Fold.length
-- >>> countOdd = fmap (("Odd " ++) . show) Fold.length
-- >>> f = Fold.partition countEven countOdd
-- >>> Stream.fold f stream
-- ("Even 50","Odd 50")
--
-- Terminating folds can be combined to parse the stream serially such that the
-- first fold consumes the input until it terminates and the second fold
-- consumes the rest of the input until it terminates:
--
-- >>> f = Fold.serialWith (,) (Fold.take 8 Fold.toList) (Fold.takeEndBy (== '\n') Fold.toList)
-- >>> Stream.fold f $ Stream.fromList "header: hello\n"
-- ("header: ","hello\n")
--
-- A 'Fold' can be applied repeatedly on a stream to transform it to a stream
-- of fold results. To split a stream on newlines:
--
-- >>> f = Fold.takeEndBy (== '\n') Fold.toList
-- >>> Stream.toList $ Stream.foldMany f $ Stream.fromList "Hello there!\nHow are you\n"
-- ["Hello there!\n","How are you\n"]
--
-- Similarly, we can split the input of a fold too:
--
-- >>> Stream.fold (Fold.many f Fold.toList) $ Stream.fromList "Hello there!\nHow are you\n"
-- ["Hello there!\n","How are you\n"]
--
-- Please see "Streamly.Internal.Data.Fold" for additional @Pre-release@
-- functions.
--
-- = Folds vs. Streams
--
-- We can often use streams or folds to achieve the same goal. However, streams
-- allow efficient composition of producers (e.g. 'Streamly.Prelude.serial' or
-- 'Streamly.Prelude.mergeBy') whereas folds allow efficient composition of
-- consumers (e.g. 'serialWith', 'partition' or 'teeWith').
--
-- Streams are producers, transformations on streams happen on the output side:
--
-- >>> f = Stream.sum . Stream.map (+1) . Stream.filter odd
-- >>> f $ Stream.fromList [1..100]
-- 2550
--
-- Folds are stream consumers with an input stream and an output value, stream
-- transformations on folds happen on the input side:
--
-- >>> f = Fold.filter odd $ Fold.lmap (+1) $ Fold.sum
-- >>> Stream.fold f $ Stream.fromList [1..100]
-- 2550
--
-- Notice the composition by @.@ vs @$@ and the order of operations in the
-- above examples, the difference is due to output vs input side
-- transformations.
module Streamly.Data.Fold
(
-- * Fold Type
Fold -- (..)
-- * Constructors
, foldl'
, foldlM'
, foldr
-- * Folds
-- ** Accumulators
-- | Folds that never terminate, these folds are much like strict left
-- folds. 'mconcat' is the fundamental accumulator. All other accumulators
-- can be expressed in terms of 'mconcat' using a suitable Monoid. Instead
-- of writing folds we could write Monoids and turn them into folds.
-- Monoids
, sconcat
, mconcat
, foldMap
, foldMapM
-- Reducers
, drain
, drainBy
, last
, length
, sum
, product
, maximumBy
, maximum
, minimumBy
, minimum
, mean
, variance
, stdDev
, rollingHash
, rollingHashWithSalt
-- Collectors
, toList
, toListRev
-- ** Terminating Folds
-- | These are much like lazy right folds.
, index
, head
, find
, lookup
, findIndex
, elemIndex
, null
, elem
, notElem
, all
, any
, and
, or
-- * Combinators
-- | Combinators are modifiers of folds. In the type @Fold m a b@, @a@ is
-- the input type and @b@ is the output type. Transformations can be
-- applied either on the input side or on the output side. Therefore,
-- combinators are of one of the following general shapes:
--
-- * @... -> Fold m a b -> Fold m c b@ (input transformation)
-- * @... -> Fold m a b -> Fold m a c@ (output transformation)
--
-- Output transformations are also known as covariant transformations, and
-- input transformations are also known as contravariant transformations.
-- The input side transformations are more interesting for folds. Most of
-- the following sections describe the input transformation operations on a
-- fold. The names and signatures of the operations are consistent with
-- corresponding operations in "Streamly.Prelude". When an operation makes
-- sense on both input and output side we use the prefix @l@ (for left) for
-- input side operations and the prefix @r@ (for right) for output side
-- operations.
-- ** Mapping on output
-- | The 'Functor' instance of a fold maps on the output of the fold:
--
-- >>> Stream.fold (fmap show Fold.sum) (Stream.enumerateFromTo 1 100)
-- "5050"
--
, rmapM
-- ** Mapping on Input
, lmap
, lmapM
-- ** Filtering
, filter
, filterM
-- -- ** Mapping Filters
, catMaybes
, mapMaybe
-- ** Trimming
, take
-- , takeInterval
, takeEndBy_
, takeEndBy
-- ** Serial Append
, serialWith
-- ** Parallel Distribution
-- | For applicative composition using distribution see
-- "Streamly.Internal.Data.Fold.Tee".
, teeWith
, tee
, distribute
-- ** Partitioning
-- | Direct items in the input stream to different folds using a binary
-- fold selector.
, partition
-- ** Unzipping
, unzip
-- ** Splitting
, many
, chunksOf
-- , intervalsOf
-- ** Nesting
, concatMap
-- * Deprecated
, sequence
, mapM
)
where
import Prelude
hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
foldl, map, mapM_, sequence, all, any, sum, product, elem,
notElem, maximum, minimum, head, last, tail, length, null,
reverse, iterate, init, and, or, lookup, foldr1, (!!),
scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,
span, splitAt, break, mapM)
import Streamly.Internal.Data.Fold
-- $setup
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Prelude as Stream