foldl-1.0.2: src/Control/Foldl.hs
{-| This module provides efficient and streaming left folds that you can combine
using 'Applicative' style.
Import this module qualified to avoid clashing with the Prelude:
>>> import qualified Control.Foldl as L
Use 'fold' to apply a 'Fold' to a list:
>>> L.fold L.sum [1..100]
5050
'Fold's are 'Applicative's, so you can combine them using 'Applicative'
combinators:
>>> import Control.Applicative
>>> let average = (/) <$> L.sum <*> L.genericLength
These combined folds will still traverse the list only once, streaming
efficiently over the list in constant space without space leaks:
>>> L.fold average [1..10000000]
5000000.5
>>> L.fold ((,) <$> L.minimum <*> L.maximum) [1..10000000]
(Just 1,Just 10000000)
-}
{-# LANGUAGE ExistentialQuantification, RankNTypes #-}
module Control.Foldl (
-- * Fold Types
Fold(..)
, FoldM(..)
-- * Folding
, fold
, foldM
-- * Folds
, mconcat
, foldMap
, head
, last
, null
, length
, and
, or
, all
, any
, sum
, product
, maximum
, minimum
, elem
, notElem
, find
, index
, elemIndex
, findIndex
-- * Generic Folds
, genericLength
, genericIndex
-- * Container folds
, list
, vector
-- * Utilities
-- $utilities
, purely
, impurely
, premap
-- * Re-exports
-- $reexports
, module Control.Monad.Primitive
, module Data.Foldable
, module Data.Vector.Generic
) where
import Control.Applicative (Applicative(pure, (<*>)),liftA2)
import Control.Foldl.Internal (Maybe'(..), lazy, Either'(..), hush)
import Control.Monad.Primitive (PrimMonad)
import Data.Foldable (Foldable)
import qualified Data.Foldable as F
import Data.Monoid (Monoid(mempty, mappend))
import Data.Vector.Generic (Vector)
import qualified Data.Vector.Generic as V
import qualified Data.Vector.Generic.Mutable as M
import Prelude hiding
( head
, last
, null
, length
, and
, or
, all
, any
, sum
, product
, maximum
, minimum
, elem
, notElem
)
{-| Efficient representation of a left fold that preserves the fold's step
function, initial accumulator, and extraction function
This allows the 'Applicative' instance to assemble derived folds that
traverse the container only once
-}
data Fold a b = forall x . Fold (x -> a -> x) x (x -> b)
data Pair a b = Pair !a !b
instance Functor (Fold a) where
fmap f (Fold step begin done) = Fold step begin (f . done)
{-# INLINABLE fmap #-}
instance Applicative (Fold a) where
pure b = Fold (\() _ -> ()) () (\() -> b)
{-# INLINABLE pure #-}
(Fold stepL beginL doneL) <*> (Fold stepR beginR doneR) =
let step (Pair xL xR) a = Pair (stepL xL a) (stepR xR a)
begin = Pair beginL beginR
done (Pair xL xR) = (doneL xL) (doneR xR)
in Fold step begin done
{-# INLINABLE (<*>) #-}
instance Monoid b => Monoid (Fold a b) where
mempty = pure mempty
{-# INLINABLE mempty #-}
mappend = liftA2 mappend
{-# INLINABLE mappend #-}
-- | Like 'Fold', but monadic
data FoldM m a b = forall x . FoldM (x -> a -> m x) (m x) (x -> m b)
instance (Monad m) => Functor (FoldM m a) where
fmap f (FoldM step start done) = FoldM step start done'
where
done' x = do
b <- done x
return $! f b
{-# INLINABLE fmap #-}
instance (Monad m) => Applicative (FoldM m a) where
pure b = FoldM (\() _ -> return ()) (return ()) (\() -> return b)
{-# INLINABLE pure #-}
(FoldM stepL beginL doneL) <*> (FoldM stepR beginR doneR) =
let step (Pair xL xR) a = do
xL' <- stepL xL a
xR' <- stepR xR a
return $! Pair xL' xR'
begin = do
xL <- beginL
xR <- beginR
return $! Pair xL xR
done (Pair xL xR) = do
f <- doneL xL
x <- doneR xR
return $! f x
in FoldM step begin done
{-# INLINABLE (<*>) #-}
instance (Monoid b, Monad m) => Monoid (FoldM m a b) where
mempty = pure mempty
{-# INLINABLE mempty #-}
mappend = liftA2 mappend
{-# INLINABLE mappend #-}
-- | Apply a strict left 'Fold' to a 'Foldable' container
fold :: (Foldable f) => Fold a b -> f a -> b
fold (Fold step begin done) as = F.foldr step' done as begin
where
step' x k z = k $! step z x
{-# INLINE fold #-}
-- | Like 'fold', but monadic
foldM :: (Foldable f, Monad m) => FoldM m a b -> f a -> m b
foldM (FoldM step begin done) as0 = do
x0 <- begin
F.foldr step' done as0 $! x0
where
step' a k x = do
x' <- step x a
k $! x'
{-# INLINE foldM #-}
-- | Fold all values within a container using 'mappend' and 'mempty'
mconcat :: (Monoid a) => Fold a a
mconcat = Fold mappend mempty id
{-# INLINABLE mconcat #-}
-- | Convert a \"@foldMap@\" to a 'Fold'
foldMap :: (Monoid w) => (a -> w) -> (w -> b) -> Fold a b
foldMap to from = Fold (\x a -> mappend x (to a)) mempty from
{-# INLINABLE foldMap #-}
{-| Get the first element of a container or return 'Nothing' if the container is
empty
-}
head :: Fold a (Maybe a)
head = Fold step Nothing' lazy
where
step x a = case x of
Nothing' -> Just' a
_ -> x
{-# INLINABLE head #-}
{-| Get the last element of a container or return 'Nothing' if the container is
empty
-}
last :: Fold a (Maybe a)
last = Fold (\_ -> Just') Nothing' lazy
{-# INLINABLE last #-}
-- | Returns 'True' if the container is empty, 'False' otherwise
null :: Fold a Bool
null = Fold (\_ _ -> False) True id
{-# INLINABLE null #-}
-- | Return the length of the container
length :: Fold a Int
length = genericLength
{- Technically, 'length' is just 'genericLength' specialized to 'Int's. I keep
the two separate so that I can later provide an 'Int'-specialized
implementation of 'length' for performance reasons like "GHC.List" does
without breaking backwards compatibility.
-}
{-# INLINABLE length #-}
-- | Returns 'True' if all elements are 'True', 'False' otherwise
and :: Fold Bool Bool
and = Fold (&&) True id
{-# INLINABLE and #-}
-- | Returns 'True' if any element is 'True', 'False' otherwise
or :: Fold Bool Bool
or = Fold (||) False id
{-# INLINABLE or #-}
{-| @(all predicate)@ returns 'True' if all elements satisfy the predicate,
'False' otherwise
-}
all :: (a -> Bool) -> Fold a Bool
all predicate = Fold (\x a -> x && predicate a) True id
{-# INLINABLE all #-}
{-| @(any predicate)@ returns 'True' if any element satisfies the predicate,
'False' otherwise
-}
any :: (a -> Bool) -> Fold a Bool
any predicate = Fold (\x a -> x || predicate a) False id
{-# INLINABLE any #-}
-- | Computes the sum of all elements
sum :: (Num a) => Fold a a
sum = Fold (+) 0 id
{-# INLINABLE sum #-}
-- | Computes the product all elements
product :: (Num a) => Fold a a
product = Fold (*) 1 id
{-# INLINABLE product #-}
-- | Computes the maximum element
maximum :: (Ord a) => Fold a (Maybe a)
maximum = Fold step Nothing' lazy
where
step x a = Just' (case x of
Nothing' -> a
Just' a' -> max a a')
{-# INLINABLE maximum #-}
-- | Computes the minimum element
minimum :: (Ord a) => Fold a (Maybe a)
minimum = Fold step Nothing' lazy
where
step x a = Just' (case x of
Nothing' -> a
Just' a' -> min a a')
{-# INLINABLE minimum #-}
{-| @(elem a)@ returns 'True' if the container has an element equal to @a@,
'False' otherwise
-}
elem :: (Eq a) => a -> Fold a Bool
elem a = any (a ==)
{-# INLINABLE elem #-}
{-| @(notElem a)@ returns 'False' if the container has an element equal to @a@,
'True' otherwise
-}
notElem :: (Eq a) => a -> Fold a Bool
notElem a = all (a /=)
{-# INLINABLE notElem #-}
{-| @(find predicate)@ returns the first element that satisfies the predicate or
'Nothing' if no element satisfies the predicate
-}
find :: (a -> Bool) -> Fold a (Maybe a)
find predicate = Fold step Nothing' lazy
where
step x a = case x of
Nothing' -> if (predicate a) then Just' a else Nothing'
_ -> x
{-# INLINABLE find #-}
{-| @(index n)@ returns the @n@th element of the container, or 'Nothing' if the
container has an insufficient number of elements
-}
index :: Int -> Fold a (Maybe a)
index = genericIndex
{-# INLINABLE index #-}
{-| @(elemIndex a)@ returns the index of the first element that equals @a@, or
'Nothing' if no element matches
-}
elemIndex :: (Eq a) => a -> Fold a (Maybe Int)
elemIndex a = findIndex (a ==)
{-# INLINABLE elemIndex #-}
{-| @(findIndex predicate)@ returns the index of the first element that
satisfies the predicate, or 'Nothing' if no element satisfies the predicate
-}
findIndex :: (a -> Bool) -> Fold a (Maybe Int)
findIndex predicate = Fold step (Left' 0) hush
where
step x a = case x of
Left' i ->
if predicate a
then Right' i
else Left' (i + 1)
_ -> x
{-# INLINABLE findIndex #-}
-- | Like 'length', except with a more general 'Num' return value
genericLength :: (Num b) => Fold a b
genericLength = Fold (\n _ -> n + 1) 0 id
{-# INLINABLE genericLength #-}
-- | Like 'index', except with a more general 'Integral' argument
genericIndex :: (Integral i) => i -> Fold a (Maybe a)
genericIndex i = Fold step (Left' 0) done
where
step x a = case x of
Left' j -> if (i == j) then Right' a else Left' (j + 1)
_ -> x
done x = case x of
Left' _ -> Nothing
Right' a -> Just a
{-# INLINABLE genericIndex #-}
-- | Fold all values into a list
list :: Fold a [a]
list = Fold (\x a -> x . (a:)) id ($ [])
{-# INLINABLE list #-}
maxChunkSize :: Int
maxChunkSize = 8 * 1024 * 1024
-- | Fold all values into a vector
vector :: (PrimMonad m, Vector v a) => FoldM m a (v a)
vector = FoldM step begin done
where
begin = do
mv <- M.unsafeNew 10
return (Pair mv 0)
step (Pair mv idx) a = do
let len = M.length mv
mv' <- if (idx >= len)
then M.unsafeGrow mv (min len maxChunkSize)
else return mv
M.unsafeWrite mv' idx a
return (Pair mv' (idx + 1))
done (Pair mv idx) = do
v <- V.unsafeFreeze mv
return (V.unsafeTake idx v)
{-# INLINABLE vector #-}
{- $utilities
'purely' and 'impurely' allow you to write folds compatible with the @foldl@
library without incurring a @foldl@ dependency. Write your fold to accept
three parameters corresponding to the step function, initial
accumulator, and extraction function and then users can upgrade your
function to accept a 'Fold' or 'FoldM' using the 'purely' or 'impurely'
combinators.
For example, the @pipes@ library implements a @foldM@ function in
@Pipes.Prelude@ with the following type:
> foldM
> :: (Monad m)
> => (x -> a -> m x) -> m x -> (x -> m b) -> Producer a m () -> m b
@foldM@ is set up so that you can wrap it with 'impurely' to accept a
'FoldM' instead:
> impurely foldM :: (Monad m) => FoldM m a b -> Producer a m () -> m b
-}
-- | Upgrade a fold to accept the 'Fold' type
purely :: (forall x . (x -> a -> x) -> x -> (x -> b) -> r) -> Fold a b -> r
purely f (Fold step begin done) = f step begin done
{-# INLINABLE purely #-}
-- | Upgrade a monadic fold to accept the 'FoldM' type
impurely
:: (Monad m)
=> (forall x . (x -> a -> m x) -> m x -> (x -> m b) -> r)
-> FoldM m a b
-> r
impurely f (FoldM step begin done) = f step begin done
{-# INLINABLE impurely #-}
{-| @(premap f folder)@ returns a new 'Fold' where f is applied at each step
@fold (premap f folder) list@ == @fold folder (map f list)@
-}
premap :: (a -> b) -> Fold b r -> Fold a r
premap f (Fold step begin done) = Fold step' begin done
where
step' x = step x . f
{-# INLINABLE premap #-}
{- $reexports
@Control.Monad.Primitive@ re-exports the 'PrimMonad' type class
@Data.Foldable@ re-exports the 'Foldable' type class
@Data.Vector.Generic@ re-exports the 'Vector' type class
-}