foldl 1.0.8 → 1.0.9
raw patch · 7 files changed
+1295/−1295 lines, 7 filesdep ~primitivesetup-changedPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: primitive
API changes (from Hackage documentation)
Files
- LICENSE +24/−24
- Setup.hs +2/−2
- foldl.cabal +37/−37
- src/Control/Foldl.hs +805/−805
- src/Control/Foldl/ByteString.hs +198/−198
- src/Control/Foldl/Internal.hs +36/−36
- src/Control/Foldl/Text.hs +193/−193
LICENSE view
@@ -1,24 +1,24 @@-Copyright (c) 2013 Gabriel Gonzalez-All rights reserved.--Redistribution and use in source and binary forms, with or without modification,-are permitted provided that the following conditions are met:- * Redistributions of source code must retain the above copyright notice,- this list of conditions and the following disclaimer.- * Redistributions in binary form must reproduce the above copyright notice,- this list of conditions and the following disclaimer in the documentation- and/or other materials provided with the distribution.- * Neither the name of Gabriel Gonzalez nor the names of other contributors- may be used to endorse or promote products derived from this software- without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2013 Gabriel Gonzalez +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Gabriel Gonzalez nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
foldl.cabal view
@@ -1,37 +1,37 @@-Name: foldl-Version: 1.0.8-Cabal-Version: >=1.8.0.2-Build-Type: Simple-License: BSD3-License-File: LICENSE-Copyright: 2013 Gabriel Gonzalez-Author: Gabriel Gonzalez-Maintainer: Gabriel439@gmail.com-Bug-Reports: https://github.com/Gabriel439/Haskell-Foldl-Library/issues-Synopsis: Composable, streaming, and efficient left folds-Description: This library provides strict left folds that stream in constant- memory, and you can combine folds using @Applicative@ style to derive new- folds. Derived folds still traverse the container just once and are often as- efficient as hand-written folds.-Category: Control-Source-Repository head- Type: git- Location: https://github.com/Gabriel439/Haskell-Foldl-Library--Library- HS-Source-Dirs: src- Build-Depends:- base >= 4 && < 5 ,- bytestring >= 0.9.2.1 && < 0.11,- primitive < 0.6 ,- text >= 0.11.2.0 && < 1.3 ,- transformers >= 0.2.0.0 && < 0.5 ,- vector >= 0.7 && < 0.11,- containers < 0.6- Exposed-Modules:- Control.Foldl,- Control.Foldl.ByteString,- Control.Foldl.Text- Other-Modules:- Control.Foldl.Internal- GHC-Options: -O2 -Wall+Name: foldl +Version: 1.0.9 +Cabal-Version: >=1.8.0.2 +Build-Type: Simple +License: BSD3 +License-File: LICENSE +Copyright: 2013 Gabriel Gonzalez +Author: Gabriel Gonzalez +Maintainer: Gabriel439@gmail.com +Bug-Reports: https://github.com/Gabriel439/Haskell-Foldl-Library/issues +Synopsis: Composable, streaming, and efficient left folds +Description: This library provides strict left folds that stream in constant + memory, and you can combine folds using @Applicative@ style to derive new + folds. Derived folds still traverse the container just once and are often as + efficient as hand-written folds. +Category: Control +Source-Repository head + Type: git + Location: https://github.com/Gabriel439/Haskell-Foldl-Library + +Library + HS-Source-Dirs: src + Build-Depends: + base >= 4 && < 5 , + bytestring >= 0.9.2.1 && < 0.11, + primitive < 0.7 , + text >= 0.11.2.0 && < 1.3 , + transformers >= 0.2.0.0 && < 0.5 , + vector >= 0.7 && < 0.11, + containers < 0.6 + Exposed-Modules: + Control.Foldl, + Control.Foldl.ByteString, + Control.Foldl.Text + Other-Modules: + Control.Foldl.Internal + GHC-Options: -O2 -Wall
src/Control/Foldl.hs view
@@ -1,805 +1,805 @@-{-| 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, Trustworthy #-}--module Control.Foldl (- -- * Fold Types- Fold(..)- , FoldM(..)-- -- * Folding- , fold- , foldM- , scan-- -- * Folds- , Control.Foldl.mconcat- , Control.Foldl.foldMap- , head- , last- , lastDef- , null- , length- , and- , or- , all- , any- , sum- , product- , maximum- , minimum- , elem- , notElem- , find- , index- , elemIndex- , findIndex-- -- * Generic Folds- , genericLength- , genericIndex-- -- * Container folds- , list- , revList- , nub- , eqNub- , set- , vector-- -- * Utilities- -- $utilities- , purely- , impurely- , generalize- , simplify- , premap- , premapM- , pretraverse- , pretraverseM-- -- * 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 ((<=<))-import Control.Monad.Primitive (PrimMonad)-import Data.Foldable (Foldable)-import qualified Data.Foldable as F-import Data.Functor.Constant (Constant(Constant, getConstant))-import Data.Functor.Identity (Identity, runIdentity)-import Data.Monoid (Monoid(mempty, mappend), Endo(Endo, appEndo))-import Data.Vector.Generic (Vector)-import qualified Data.Vector.Generic as V-import qualified Data.Vector.Generic.Mutable as M-import qualified Data.List as List-import qualified Data.Set as Set-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-- A \''Fold' a b\' processes elements of type __a__ and results in a- value of type __b__.--}-data Fold a b- -- | @Fold @ @ step @ @ initial @ @ extract@- = 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 #-}--instance Num b => Num (Fold a b) where- fromInteger = pure . fromInteger- {-# INLINABLE fromInteger #-}-- negate = fmap negate- {-# INLINABLE negate #-}-- abs = fmap abs- {-# INLINABLE abs #-}-- signum = fmap signum- {-# INLINABLE signum #-}-- (+) = liftA2 (+)- {-# INLINABLE (+) #-}-- (*) = liftA2 (*)- {-# INLINABLE (*) #-}-- (-) = liftA2 (-)- {-# INLINABLE (-) #-}--instance Fractional b => Fractional (Fold a b) where- fromRational = pure . fromRational- {-# INLINABLE fromRational #-}-- recip = fmap recip- {-# INLINABLE recip #-}-- (/) = liftA2 (/)- {-# INLINABLE (/) #-}--instance Floating b => Floating (Fold a b) where- pi = pure pi- {-# INLINABLE pi #-}-- exp = fmap exp- {-# INLINABLE exp #-}-- sqrt = fmap sqrt- {-# INLINABLE sqrt #-}-- log = fmap log- {-# INLINABLE log #-}-- sin = fmap sin- {-# INLINABLE sin #-}-- tan = fmap tan- {-# INLINABLE tan #-}-- cos = fmap cos- {-# INLINABLE cos #-}-- asin = fmap sin- {-# INLINABLE asin #-}-- atan = fmap atan- {-# INLINABLE atan #-}-- acos = fmap acos- {-# INLINABLE acos #-}-- sinh = fmap sinh- {-# INLINABLE sinh #-}-- tanh = fmap tanh- {-# INLINABLE tanh #-}-- cosh = fmap cosh- {-# INLINABLE cosh #-}-- asinh = fmap asinh- {-# INLINABLE asinh #-}-- atanh = fmap atanh- {-# INLINABLE atanh #-}-- acosh = fmap acosh- {-# INLINABLE acosh #-}-- (**) = liftA2 (**)- {-# INLINABLE (**) #-}-- logBase = liftA2 logBase- {-# INLINABLE logBase #-}--{-| Like 'Fold', but monadic.-- A \''FoldM' m a b\' processes elements of type __a__ and- results in a monadic value of type __m b__.--}-data FoldM m a b =- -- | @FoldM @ @ step @ @ initial @ @ extract@- 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 #-}--instance (Monad m, Num b) => Num (FoldM m a b) where- fromInteger = pure . fromInteger- {-# INLINABLE fromInteger #-}-- negate = fmap negate- {-# INLINABLE negate #-}-- abs = fmap abs- {-# INLINABLE abs #-}-- signum = fmap signum- {-# INLINABLE signum #-}-- (+) = liftA2 (+)- {-# INLINABLE (+) #-}-- (*) = liftA2 (*)- {-# INLINABLE (*) #-}-- (-) = liftA2 (-)- {-# INLINABLE (-) #-}--instance (Monad m, Fractional b) => Fractional (FoldM m a b) where- fromRational = pure . fromRational- {-# INLINABLE fromRational #-}-- recip = fmap recip- {-# INLINABLE recip #-}-- (/) = liftA2 (/)- {-# INLINABLE (/) #-}--instance (Monad m, Floating b) => Floating (FoldM m a b) where- pi = pure pi- {-# INLINABLE pi #-}-- exp = fmap exp- {-# INLINABLE exp #-}-- sqrt = fmap sqrt- {-# INLINABLE sqrt #-}-- log = fmap log- {-# INLINABLE log #-}-- sin = fmap sin- {-# INLINABLE sin #-}-- tan = fmap tan- {-# INLINABLE tan #-}-- cos = fmap cos- {-# INLINABLE cos #-}-- asin = fmap sin- {-# INLINABLE asin #-}-- atan = fmap atan- {-# INLINABLE atan #-}-- acos = fmap acos- {-# INLINABLE acos #-}-- sinh = fmap sinh- {-# INLINABLE sinh #-}-- tanh = fmap tanh- {-# INLINABLE tanh #-}-- cosh = fmap cosh- {-# INLINABLE cosh #-}-- asinh = fmap asinh- {-# INLINABLE asinh #-}-- atanh = fmap atanh- {-# INLINABLE atanh #-}-- acosh = fmap acosh- {-# INLINABLE acosh #-}-- (**) = liftA2 (**)- {-# INLINABLE (**) #-}-- logBase = liftA2 logBase- {-# INLINABLE logBase #-}---- | 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 cons done as begin- where- cons a k x = k $! step x a-{-# 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 #-}---- | Convert a strict left 'Fold' into a scan-scan :: Fold a b -> [a] -> [b]-scan (Fold step begin done) as = foldr cons nil as begin- where- nil x = done x:[]- cons a k x = done x:(k $! step x a)-{-# INLINE scan #-}---- | 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 = Fold (\x a -> mappend x (to a)) mempty-{-# 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 (const Just') Nothing' lazy-{-# INLINABLE last #-}--{-| Get the last element of a container or return a default value if the container- is empty--}-lastDef :: a -> Fold a a-lastDef a = Fold (\_ a' -> a') a id-{-# INLINABLE lastDef #-}---- | 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 #-}---- | Fold all values into a list, in reverse order-revList :: Fold a [a]-revList = Fold (\x a -> a:x) [] id-{-# INLINABLE revList #-}--{-| /O(n log n)/. Fold values into a list with duplicates removed, while- preserving their first occurrences--}-nub :: Ord a => Fold a [a]-nub = Fold step (Pair Set.empty id) fin- where- step (Pair s r) a = if Set.member a s- then Pair s r- else Pair (Set.insert a s) (r . (a :))- fin (Pair _ r) = r []-{-# INLINABLE nub #-}--{-| /O(n^2)/. Fold values into a list with duplicates removed, while preserving- their first occurrences--}-eqNub :: Eq a => Fold a [a]-eqNub = Fold step (Pair [] id) fin- where- step (Pair known r) a = if List.elem a known- then Pair known r- else Pair (a : known) (r . (a :))- fin (Pair _ r) = r []-{-# INLINABLE eqNub #-}---- | Fold values into a set-set :: Ord a => Fold a (Set.Set a)-set = Fold (flip Set.insert) Set.empty id-{-# INLINABLE set #-}--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 #-}--{-| Generalize a `Fold` to a `FoldM`--> generalize (pure r) = pure r->-> generalize (f <*> x) = generalize f <*> generalize x--}-generalize :: Monad m => Fold a b -> FoldM m a b-generalize (Fold step begin done) = FoldM step' begin' done'- where- step' x a = return (step x a)- begin' = return begin- done' x = return (done x)-{-# INLINABLE generalize #-}--{-| Simplify a pure `FoldM` to a `Fold`--> simplify (pure r) = pure r->-> simplify (f <*> x) = simplify f <*> simplify x--}-simplify :: FoldM Identity a b -> Fold a b-simplify (FoldM step begin done) = Fold step' begin' done'- where- step' x a = runIdentity (step x a)- begin' = runIdentity begin- done' x = runIdentity (done x)-{-# INLINABLE simplify #-}--{-| @(premap f folder)@ returns a new 'Fold' where f is applied at each step--> fold (premap f folder) list = fold folder (map f list)-->>> fold (premap Sum mconcat) [1..10]-Sum {getSum = 55}-->>> fold mconcat (map Sum [1..10])-Sum {getSum = 55}--> premap id = id->-> premap (f . g) = premap g . premap f--> premap k (pure r) = pure r->-> premap k (f <*> x) = premap k f <*> premap k x--}-premap :: (a -> b) -> Fold b r -> Fold a r-premap f (Fold step begin done) = Fold step' begin done- where- step' x a = step x (f a)-{-# INLINABLE premap #-}--{-| @(premapM f folder)@ returns a new 'FoldM' where f is applied to each input- element--> foldM (premapM f folder) list = foldM folder (map f list)--> premapM id = id->-> premapM (f . g) = premap g . premap f--> premapM k (pure r) = pure r->-> premapM k (f <*> x) = premapM k f <*> premapM k x--}-premapM :: Monad m => (a -> b) -> FoldM m b r -> FoldM m a r-premapM f (FoldM step begin done) = FoldM step' begin done- where- step' x a = step x (f a)-{-# INLINABLE premapM #-}--type Traversal' a b = forall f . Applicative f => (b -> f b) -> a -> f a--{-| @(pretraverse t folder)@ traverses each incoming element using @Traversal'@- @t@ and folds every target of the @Traversal'@-->>> fold (pretraverse traverse sum) [[1..5],[6..10]]-55-->>> fold (pretraverse (traverse.traverse) sum) [[Nothing, Just 2, Just 7],[Just 13, Nothing, Just 20]]-42-->>> fold (pretraverse (filtered even) sum) [1,3,5,7,21,21]-42-->>> fold (pretraverse _2 mconcat) [(1,"Hello "),(2,"World"),(3,"!")]-"Hello World!"--> pretraverse id = id->-> pretraverse (f . g) = pretraverse f . pretraverse g--> pretraverse t (pure r) = pure r->-> pretraverse t (f <*> x) = pretraverse t f <*> pretraverse t x--}-pretraverse :: Traversal' a b -> Fold b r -> Fold a r-pretraverse k (Fold step begin done) = Fold step' begin done- where- step' = flip (appEndo . getConstant . k (Constant . Endo . flip step))-{-# INLINABLE pretraverse #-}--newtype EndoM m a = EndoM { appEndoM :: a -> m a }--instance Monad m => Monoid (EndoM m a) where- mempty = EndoM return- mappend (EndoM f) (EndoM g) = EndoM (f <=< g)--{-| @(pretraverseM t folder)@ traverses each incoming element using @Traversal'@- @t@ and folds every target of the @Traversal'@--> pretraverseM id = id->-> pretraverseM (f . g) = pretraverseM f . pretraverseM g--> pretraverseM t (pure r) = pure r->-> pretraverseM t (f <*> x) = pretraverseM t f <*> pretraverseM t x--}-pretraverseM :: Monad m => Traversal' a b -> FoldM m b r -> FoldM m a r-pretraverseM k (FoldM step begin done) = FoldM step' begin done- where- step' = flip (appEndoM . getConstant . k (Constant . EndoM . flip step))-{-# INLINABLE pretraverseM #-}--{- $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--}+{-| 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, Trustworthy #-} + +module Control.Foldl ( + -- * Fold Types + Fold(..) + , FoldM(..) + + -- * Folding + , fold + , foldM + , scan + + -- * Folds + , Control.Foldl.mconcat + , Control.Foldl.foldMap + , head + , last + , lastDef + , null + , length + , and + , or + , all + , any + , sum + , product + , maximum + , minimum + , elem + , notElem + , find + , index + , elemIndex + , findIndex + + -- * Generic Folds + , genericLength + , genericIndex + + -- * Container folds + , list + , revList + , nub + , eqNub + , set + , vector + + -- * Utilities + -- $utilities + , purely + , impurely + , generalize + , simplify + , premap + , premapM + , pretraverse + , pretraverseM + + -- * 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 ((<=<)) +import Control.Monad.Primitive (PrimMonad) +import Data.Foldable (Foldable) +import qualified Data.Foldable as F +import Data.Functor.Constant (Constant(Constant, getConstant)) +import Data.Functor.Identity (Identity, runIdentity) +import Data.Monoid (Monoid(mempty, mappend), Endo(Endo, appEndo)) +import Data.Vector.Generic (Vector) +import qualified Data.Vector.Generic as V +import qualified Data.Vector.Generic.Mutable as M +import qualified Data.List as List +import qualified Data.Set as Set +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 + + A \''Fold' a b\' processes elements of type __a__ and results in a + value of type __b__. +-} +data Fold a b + -- | @Fold @ @ step @ @ initial @ @ extract@ + = 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 #-} + +instance Num b => Num (Fold a b) where + fromInteger = pure . fromInteger + {-# INLINABLE fromInteger #-} + + negate = fmap negate + {-# INLINABLE negate #-} + + abs = fmap abs + {-# INLINABLE abs #-} + + signum = fmap signum + {-# INLINABLE signum #-} + + (+) = liftA2 (+) + {-# INLINABLE (+) #-} + + (*) = liftA2 (*) + {-# INLINABLE (*) #-} + + (-) = liftA2 (-) + {-# INLINABLE (-) #-} + +instance Fractional b => Fractional (Fold a b) where + fromRational = pure . fromRational + {-# INLINABLE fromRational #-} + + recip = fmap recip + {-# INLINABLE recip #-} + + (/) = liftA2 (/) + {-# INLINABLE (/) #-} + +instance Floating b => Floating (Fold a b) where + pi = pure pi + {-# INLINABLE pi #-} + + exp = fmap exp + {-# INLINABLE exp #-} + + sqrt = fmap sqrt + {-# INLINABLE sqrt #-} + + log = fmap log + {-# INLINABLE log #-} + + sin = fmap sin + {-# INLINABLE sin #-} + + tan = fmap tan + {-# INLINABLE tan #-} + + cos = fmap cos + {-# INLINABLE cos #-} + + asin = fmap sin + {-# INLINABLE asin #-} + + atan = fmap atan + {-# INLINABLE atan #-} + + acos = fmap acos + {-# INLINABLE acos #-} + + sinh = fmap sinh + {-# INLINABLE sinh #-} + + tanh = fmap tanh + {-# INLINABLE tanh #-} + + cosh = fmap cosh + {-# INLINABLE cosh #-} + + asinh = fmap asinh + {-# INLINABLE asinh #-} + + atanh = fmap atanh + {-# INLINABLE atanh #-} + + acosh = fmap acosh + {-# INLINABLE acosh #-} + + (**) = liftA2 (**) + {-# INLINABLE (**) #-} + + logBase = liftA2 logBase + {-# INLINABLE logBase #-} + +{-| Like 'Fold', but monadic. + + A \''FoldM' m a b\' processes elements of type __a__ and + results in a monadic value of type __m b__. +-} +data FoldM m a b = + -- | @FoldM @ @ step @ @ initial @ @ extract@ + 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 #-} + +instance (Monad m, Num b) => Num (FoldM m a b) where + fromInteger = pure . fromInteger + {-# INLINABLE fromInteger #-} + + negate = fmap negate + {-# INLINABLE negate #-} + + abs = fmap abs + {-# INLINABLE abs #-} + + signum = fmap signum + {-# INLINABLE signum #-} + + (+) = liftA2 (+) + {-# INLINABLE (+) #-} + + (*) = liftA2 (*) + {-# INLINABLE (*) #-} + + (-) = liftA2 (-) + {-# INLINABLE (-) #-} + +instance (Monad m, Fractional b) => Fractional (FoldM m a b) where + fromRational = pure . fromRational + {-# INLINABLE fromRational #-} + + recip = fmap recip + {-# INLINABLE recip #-} + + (/) = liftA2 (/) + {-# INLINABLE (/) #-} + +instance (Monad m, Floating b) => Floating (FoldM m a b) where + pi = pure pi + {-# INLINABLE pi #-} + + exp = fmap exp + {-# INLINABLE exp #-} + + sqrt = fmap sqrt + {-# INLINABLE sqrt #-} + + log = fmap log + {-# INLINABLE log #-} + + sin = fmap sin + {-# INLINABLE sin #-} + + tan = fmap tan + {-# INLINABLE tan #-} + + cos = fmap cos + {-# INLINABLE cos #-} + + asin = fmap sin + {-# INLINABLE asin #-} + + atan = fmap atan + {-# INLINABLE atan #-} + + acos = fmap acos + {-# INLINABLE acos #-} + + sinh = fmap sinh + {-# INLINABLE sinh #-} + + tanh = fmap tanh + {-# INLINABLE tanh #-} + + cosh = fmap cosh + {-# INLINABLE cosh #-} + + asinh = fmap asinh + {-# INLINABLE asinh #-} + + atanh = fmap atanh + {-# INLINABLE atanh #-} + + acosh = fmap acosh + {-# INLINABLE acosh #-} + + (**) = liftA2 (**) + {-# INLINABLE (**) #-} + + logBase = liftA2 logBase + {-# INLINABLE logBase #-} + +-- | 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 cons done as begin + where + cons a k x = k $! step x a +{-# 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 #-} + +-- | Convert a strict left 'Fold' into a scan +scan :: Fold a b -> [a] -> [b] +scan (Fold step begin done) as = foldr cons nil as begin + where + nil x = done x:[] + cons a k x = done x:(k $! step x a) +{-# INLINE scan #-} + +-- | 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 = Fold (\x a -> mappend x (to a)) mempty +{-# 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 (const Just') Nothing' lazy +{-# INLINABLE last #-} + +{-| Get the last element of a container or return a default value if the container + is empty +-} +lastDef :: a -> Fold a a +lastDef a = Fold (\_ a' -> a') a id +{-# INLINABLE lastDef #-} + +-- | 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 #-} + +-- | Fold all values into a list, in reverse order +revList :: Fold a [a] +revList = Fold (\x a -> a:x) [] id +{-# INLINABLE revList #-} + +{-| /O(n log n)/. Fold values into a list with duplicates removed, while + preserving their first occurrences +-} +nub :: Ord a => Fold a [a] +nub = Fold step (Pair Set.empty id) fin + where + step (Pair s r) a = if Set.member a s + then Pair s r + else Pair (Set.insert a s) (r . (a :)) + fin (Pair _ r) = r [] +{-# INLINABLE nub #-} + +{-| /O(n^2)/. Fold values into a list with duplicates removed, while preserving + their first occurrences +-} +eqNub :: Eq a => Fold a [a] +eqNub = Fold step (Pair [] id) fin + where + step (Pair known r) a = if List.elem a known + then Pair known r + else Pair (a : known) (r . (a :)) + fin (Pair _ r) = r [] +{-# INLINABLE eqNub #-} + +-- | Fold values into a set +set :: Ord a => Fold a (Set.Set a) +set = Fold (flip Set.insert) Set.empty id +{-# INLINABLE set #-} + +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 #-} + +{-| Generalize a `Fold` to a `FoldM` + +> generalize (pure r) = pure r +> +> generalize (f <*> x) = generalize f <*> generalize x +-} +generalize :: Monad m => Fold a b -> FoldM m a b +generalize (Fold step begin done) = FoldM step' begin' done' + where + step' x a = return (step x a) + begin' = return begin + done' x = return (done x) +{-# INLINABLE generalize #-} + +{-| Simplify a pure `FoldM` to a `Fold` + +> simplify (pure r) = pure r +> +> simplify (f <*> x) = simplify f <*> simplify x +-} +simplify :: FoldM Identity a b -> Fold a b +simplify (FoldM step begin done) = Fold step' begin' done' + where + step' x a = runIdentity (step x a) + begin' = runIdentity begin + done' x = runIdentity (done x) +{-# INLINABLE simplify #-} + +{-| @(premap f folder)@ returns a new 'Fold' where f is applied at each step + +> fold (premap f folder) list = fold folder (map f list) + +>>> fold (premap Sum mconcat) [1..10] +Sum {getSum = 55} + +>>> fold mconcat (map Sum [1..10]) +Sum {getSum = 55} + +> premap id = id +> +> premap (f . g) = premap g . premap f + +> premap k (pure r) = pure r +> +> premap k (f <*> x) = premap k f <*> premap k x +-} +premap :: (a -> b) -> Fold b r -> Fold a r +premap f (Fold step begin done) = Fold step' begin done + where + step' x a = step x (f a) +{-# INLINABLE premap #-} + +{-| @(premapM f folder)@ returns a new 'FoldM' where f is applied to each input + element + +> foldM (premapM f folder) list = foldM folder (map f list) + +> premapM id = id +> +> premapM (f . g) = premap g . premap f + +> premapM k (pure r) = pure r +> +> premapM k (f <*> x) = premapM k f <*> premapM k x +-} +premapM :: Monad m => (a -> b) -> FoldM m b r -> FoldM m a r +premapM f (FoldM step begin done) = FoldM step' begin done + where + step' x a = step x (f a) +{-# INLINABLE premapM #-} + +type Traversal' a b = forall f . Applicative f => (b -> f b) -> a -> f a + +{-| @(pretraverse t folder)@ traverses each incoming element using @Traversal'@ + @t@ and folds every target of the @Traversal'@ + +>>> fold (pretraverse traverse sum) [[1..5],[6..10]] +55 + +>>> fold (pretraverse (traverse.traverse) sum) [[Nothing, Just 2, Just 7],[Just 13, Nothing, Just 20]] +42 + +>>> fold (pretraverse (filtered even) sum) [1,3,5,7,21,21] +42 + +>>> fold (pretraverse _2 mconcat) [(1,"Hello "),(2,"World"),(3,"!")] +"Hello World!" + +> pretraverse id = id +> +> pretraverse (f . g) = pretraverse f . pretraverse g + +> pretraverse t (pure r) = pure r +> +> pretraverse t (f <*> x) = pretraverse t f <*> pretraverse t x +-} +pretraverse :: Traversal' a b -> Fold b r -> Fold a r +pretraverse k (Fold step begin done) = Fold step' begin done + where + step' = flip (appEndo . getConstant . k (Constant . Endo . flip step)) +{-# INLINABLE pretraverse #-} + +newtype EndoM m a = EndoM { appEndoM :: a -> m a } + +instance Monad m => Monoid (EndoM m a) where + mempty = EndoM return + mappend (EndoM f) (EndoM g) = EndoM (f <=< g) + +{-| @(pretraverseM t folder)@ traverses each incoming element using @Traversal'@ + @t@ and folds every target of the @Traversal'@ + +> pretraverseM id = id +> +> pretraverseM (f . g) = pretraverseM f . pretraverseM g + +> pretraverseM t (pure r) = pure r +> +> pretraverseM t (f <*> x) = pretraverseM t f <*> pretraverseM t x +-} +pretraverseM :: Monad m => Traversal' a b -> FoldM m b r -> FoldM m a r +pretraverseM k (FoldM step begin done) = FoldM step' begin done + where + step' = flip (appEndoM . getConstant . k (Constant . EndoM . flip step)) +{-# INLINABLE pretraverseM #-} + +{- $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 +-}
src/Control/Foldl/ByteString.hs view
@@ -1,198 +1,198 @@--- | Folds for byte streams--module Control.Foldl.ByteString (- -- * Folding- fold-- -- * Folds- , head- , last- , null- , length- , any- , all- , maximum- , minimum- , elem- , notElem- , find- , index- , elemIndex- , findIndex- , count-- -- * Re-exports- -- $reexports- , module Control.Foldl- , module Data.ByteString- , module Data.Word- ) where--import Control.Foldl (Fold)-import Control.Foldl.Internal (Maybe'(..), lazy, strict, Either'(..), hush)-import qualified Control.Foldl as L-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy.Internal as Lazy-import qualified Data.ByteString.Unsafe as BU-import Data.Word (Word8)-import Prelude hiding (- head, last, null, length, any, all, maximum, minimum, elem, notElem )---- | Appply a strict left 'Fold' to a lazy bytestring-fold :: Fold ByteString a -> Lazy.ByteString -> a-fold (L.Fold step begin done) as = done (Lazy.foldlChunks step begin as)-{-# INLINABLE fold #-}--{-| Get the first byte of a byte stream or return 'Nothing' if the stream is- empty--}-head :: Fold ByteString (Maybe Word8)-head = L.Fold step Nothing' lazy- where- step mw8 bs =- if B.null bs- then mw8- else case mw8 of- Just' _ -> mw8- Nothing' -> Just' (BU.unsafeHead bs)-{-# INLINABLE head #-}--{-| Get the last byte of a byte stream or return 'Nothing' if the byte stream is- empty--}-last :: Fold ByteString (Maybe Word8)-last = L.Fold step Nothing' lazy- where- step mw8 bs =- if B.null bs- then mw8- else Just' (B.last bs)- -- TODO: Use `unsafeLast` when Debian Stable Haskell Platform has it-{-# INLINABLE last #-}---- | Returns 'True' if the byte stream is empty, 'False' otherwise-null :: Fold ByteString Bool-null = L.Fold step True id- where- step isNull bs = isNull && B.null bs-{-# INLINABLE null #-}---- | Return the length of the byte stream in bytes-length :: Num n => Fold ByteString n-length = L.Fold (\n bs -> n + fromIntegral (B.length bs)) 0 id-{-# INLINABLE length #-}--{-| @(all predicate)@ returns 'True' if all bytes satisfy the predicate, 'False'- otherwise--}-all :: (Word8 -> Bool) -> Fold ByteString Bool-all predicate = L.Fold (\b bs -> b && B.all predicate bs) True id-{-# INLINABLE all #-}--{-| @(any predicate)@ returns 'True' if any byte satisfies the predicate,- 'False' otherwise--}-any :: (Word8 -> Bool) -> Fold ByteString Bool-any predicate = L.Fold (\b bs -> b || B.any predicate bs) False id-{-# INLINABLE any #-}---- | Computes the maximum byte-maximum :: Fold ByteString (Maybe Word8)-maximum = L.Fold step Nothing' lazy- where- step mw8 bs =- if B.null bs- then mw8- else Just' (case mw8 of- Nothing' -> B.maximum bs- Just' w8 -> max w8 (B.maximum bs) )-{-# INLINABLE maximum #-}---- | Computes the minimum byte-minimum :: Fold ByteString (Maybe Word8)-minimum = L.Fold step Nothing' lazy- where- step mw8 bs =- if B.null bs- then mw8- else Just' (case mw8 of- Nothing' -> B.minimum bs- Just' w8 -> min w8 (B.minimum bs) )-{-# INLINABLE minimum #-}--{-| @(elem w8)@ returns 'True' if the byte stream has a byte equal to @w8@,- 'False' otherwise--}-elem :: Word8 -> Fold ByteString Bool-elem w8 = any (w8 ==)-{-# INLINABLE elem #-}--{-| @(notElem w8)@ returns 'False' if the byte stream has a byte equal to @w8@,- 'True' otherwise--}-notElem :: Word8 -> Fold ByteString Bool-notElem w8 = all (w8 /=)-{-# INLINABLE notElem #-}--{-| @(find predicate)@ returns the first byte that satisfies the predicate or- 'Nothing' if no byte satisfies the predicate--}-find :: (Word8 -> Bool) -> Fold ByteString (Maybe Word8)-find predicate = L.Fold step Nothing' lazy- where- step mw8 bs = case mw8 of- Nothing' -> strict (B.find predicate bs)- Just' _ -> mw8-{-# INLINABLE find #-}--{-| @(index n)@ returns the @n@th byte of the byte stream, or 'Nothing' if the- stream has an insufficient number of bytes--}-index :: Integral n => n -> Fold ByteString (Maybe Word8)-index i = L.Fold step (Left' (fromIntegral i)) hush- where- step x bs = case x of- Left' remainder ->- let len = B.length bs- in if remainder < len- then Right' (BU.unsafeIndex bs remainder)- else Left' (remainder - len)- _ -> x-{-# INLINABLE index #-}--{-| @(elemIndex w8)@ returns the index of the first byte that equals @w8@, or- 'Nothing' if no byte matches--}-elemIndex :: Num n => Word8 -> Fold ByteString (Maybe n)-elemIndex w8 = findIndex (w8 ==)-{-# INLINABLE elemIndex #-}--{-| @(findIndex predicate)@ returns the index of the first byte that satisfies- the predicate, or 'Nothing' if no byte satisfies the predicate--}-findIndex :: Num n => (Word8 -> Bool) -> Fold ByteString (Maybe n)-findIndex predicate = L.Fold step (Left' 0) hush- where- step x bs = case x of- Left' m -> case B.findIndex predicate bs of- Nothing -> Left' (m + fromIntegral (B.length bs))- Just n -> Right' (m + fromIntegral n)- _ -> x-{-# INLINABLE findIndex #-}---- | @count w8@ returns the number of times @w8@ appears-count :: Num n => Word8 -> Fold ByteString n-count w8 = L.Fold step 0 id- where- step n bs = n + fromIntegral (B.count w8 bs)-{-# INLINABLE count #-}--{- $reexports-- "Control.Foldl" re-exports the 'Fold' type-- @Data.ByteString@ re-exports the 'ByteString' type-- @Data.Word@ re-exports the 'Word8' type--}+-- | Folds for byte streams + +module Control.Foldl.ByteString ( + -- * Folding + fold + + -- * Folds + , head + , last + , null + , length + , any + , all + , maximum + , minimum + , elem + , notElem + , find + , index + , elemIndex + , findIndex + , count + + -- * Re-exports + -- $reexports + , module Control.Foldl + , module Data.ByteString + , module Data.Word + ) where + +import Control.Foldl (Fold) +import Control.Foldl.Internal (Maybe'(..), lazy, strict, Either'(..), hush) +import qualified Control.Foldl as L +import Data.ByteString (ByteString) +import qualified Data.ByteString as B +import qualified Data.ByteString.Lazy.Internal as Lazy +import qualified Data.ByteString.Unsafe as BU +import Data.Word (Word8) +import Prelude hiding ( + head, last, null, length, any, all, maximum, minimum, elem, notElem ) + +-- | Appply a strict left 'Fold' to a lazy bytestring +fold :: Fold ByteString a -> Lazy.ByteString -> a +fold (L.Fold step begin done) as = done (Lazy.foldlChunks step begin as) +{-# INLINABLE fold #-} + +{-| Get the first byte of a byte stream or return 'Nothing' if the stream is + empty +-} +head :: Fold ByteString (Maybe Word8) +head = L.Fold step Nothing' lazy + where + step mw8 bs = + if B.null bs + then mw8 + else case mw8 of + Just' _ -> mw8 + Nothing' -> Just' (BU.unsafeHead bs) +{-# INLINABLE head #-} + +{-| Get the last byte of a byte stream or return 'Nothing' if the byte stream is + empty +-} +last :: Fold ByteString (Maybe Word8) +last = L.Fold step Nothing' lazy + where + step mw8 bs = + if B.null bs + then mw8 + else Just' (B.last bs) + -- TODO: Use `unsafeLast` when Debian Stable Haskell Platform has it +{-# INLINABLE last #-} + +-- | Returns 'True' if the byte stream is empty, 'False' otherwise +null :: Fold ByteString Bool +null = L.Fold step True id + where + step isNull bs = isNull && B.null bs +{-# INLINABLE null #-} + +-- | Return the length of the byte stream in bytes +length :: Num n => Fold ByteString n +length = L.Fold (\n bs -> n + fromIntegral (B.length bs)) 0 id +{-# INLINABLE length #-} + +{-| @(all predicate)@ returns 'True' if all bytes satisfy the predicate, 'False' + otherwise +-} +all :: (Word8 -> Bool) -> Fold ByteString Bool +all predicate = L.Fold (\b bs -> b && B.all predicate bs) True id +{-# INLINABLE all #-} + +{-| @(any predicate)@ returns 'True' if any byte satisfies the predicate, + 'False' otherwise +-} +any :: (Word8 -> Bool) -> Fold ByteString Bool +any predicate = L.Fold (\b bs -> b || B.any predicate bs) False id +{-# INLINABLE any #-} + +-- | Computes the maximum byte +maximum :: Fold ByteString (Maybe Word8) +maximum = L.Fold step Nothing' lazy + where + step mw8 bs = + if B.null bs + then mw8 + else Just' (case mw8 of + Nothing' -> B.maximum bs + Just' w8 -> max w8 (B.maximum bs) ) +{-# INLINABLE maximum #-} + +-- | Computes the minimum byte +minimum :: Fold ByteString (Maybe Word8) +minimum = L.Fold step Nothing' lazy + where + step mw8 bs = + if B.null bs + then mw8 + else Just' (case mw8 of + Nothing' -> B.minimum bs + Just' w8 -> min w8 (B.minimum bs) ) +{-# INLINABLE minimum #-} + +{-| @(elem w8)@ returns 'True' if the byte stream has a byte equal to @w8@, + 'False' otherwise +-} +elem :: Word8 -> Fold ByteString Bool +elem w8 = any (w8 ==) +{-# INLINABLE elem #-} + +{-| @(notElem w8)@ returns 'False' if the byte stream has a byte equal to @w8@, + 'True' otherwise +-} +notElem :: Word8 -> Fold ByteString Bool +notElem w8 = all (w8 /=) +{-# INLINABLE notElem #-} + +{-| @(find predicate)@ returns the first byte that satisfies the predicate or + 'Nothing' if no byte satisfies the predicate +-} +find :: (Word8 -> Bool) -> Fold ByteString (Maybe Word8) +find predicate = L.Fold step Nothing' lazy + where + step mw8 bs = case mw8 of + Nothing' -> strict (B.find predicate bs) + Just' _ -> mw8 +{-# INLINABLE find #-} + +{-| @(index n)@ returns the @n@th byte of the byte stream, or 'Nothing' if the + stream has an insufficient number of bytes +-} +index :: Integral n => n -> Fold ByteString (Maybe Word8) +index i = L.Fold step (Left' (fromIntegral i)) hush + where + step x bs = case x of + Left' remainder -> + let len = B.length bs + in if remainder < len + then Right' (BU.unsafeIndex bs remainder) + else Left' (remainder - len) + _ -> x +{-# INLINABLE index #-} + +{-| @(elemIndex w8)@ returns the index of the first byte that equals @w8@, or + 'Nothing' if no byte matches +-} +elemIndex :: Num n => Word8 -> Fold ByteString (Maybe n) +elemIndex w8 = findIndex (w8 ==) +{-# INLINABLE elemIndex #-} + +{-| @(findIndex predicate)@ returns the index of the first byte that satisfies + the predicate, or 'Nothing' if no byte satisfies the predicate +-} +findIndex :: Num n => (Word8 -> Bool) -> Fold ByteString (Maybe n) +findIndex predicate = L.Fold step (Left' 0) hush + where + step x bs = case x of + Left' m -> case B.findIndex predicate bs of + Nothing -> Left' (m + fromIntegral (B.length bs)) + Just n -> Right' (m + fromIntegral n) + _ -> x +{-# INLINABLE findIndex #-} + +-- | @count w8@ returns the number of times @w8@ appears +count :: Num n => Word8 -> Fold ByteString n +count w8 = L.Fold step 0 id + where + step n bs = n + fromIntegral (B.count w8 bs) +{-# INLINABLE count #-} + +{- $reexports + + "Control.Foldl" re-exports the 'Fold' type + + @Data.ByteString@ re-exports the 'ByteString' type + + @Data.Word@ re-exports the 'Word8' type +-}
src/Control/Foldl/Internal.hs view
@@ -1,36 +1,36 @@--- | Strict data types for use as internal accumulators that don't space leak--module Control.Foldl.Internal (- -- * Strict maybe- Maybe'(..)- , lazy- , strict-- -- * Strict Either- , Either'(..)- , hush- ) where---- | A strict 'Maybe'-data Maybe' a = Just' !a | Nothing'---- | Convert 'Maybe'' to 'Maybe'-lazy :: Maybe' a -> Maybe a-lazy Nothing' = Nothing-lazy (Just' a) = Just a-{-# INLINABLE lazy #-}---- | Convert 'Maybe' to 'Maybe''-strict :: Maybe a -> Maybe' a-strict Nothing = Nothing'-strict (Just a ) = Just' a-{-# INLINABLE strict #-}---- | A strict 'Either'-data Either' a b = Left' !a | Right' !b---- | Convert 'Either'' to 'Maybe'-hush :: Either' a b -> Maybe b-hush (Left' _) = Nothing-hush (Right' b) = Just b-{-# INLINABLE hush #-}+-- | Strict data types for use as internal accumulators that don't space leak + +module Control.Foldl.Internal ( + -- * Strict maybe + Maybe'(..) + , lazy + , strict + + -- * Strict Either + , Either'(..) + , hush + ) where + +-- | A strict 'Maybe' +data Maybe' a = Just' !a | Nothing' + +-- | Convert 'Maybe'' to 'Maybe' +lazy :: Maybe' a -> Maybe a +lazy Nothing' = Nothing +lazy (Just' a) = Just a +{-# INLINABLE lazy #-} + +-- | Convert 'Maybe' to 'Maybe'' +strict :: Maybe a -> Maybe' a +strict Nothing = Nothing' +strict (Just a ) = Just' a +{-# INLINABLE strict #-} + +-- | A strict 'Either' +data Either' a b = Left' !a | Right' !b + +-- | Convert 'Either'' to 'Maybe' +hush :: Either' a b -> Maybe b +hush (Left' _) = Nothing +hush (Right' b) = Just b +{-# INLINABLE hush #-}
src/Control/Foldl/Text.hs view
@@ -1,193 +1,193 @@--- | Folds for text streams--module Control.Foldl.Text (- -- * Folding- fold-- -- * Folds- , head- , last- , null- , length- , any- , all- , maximum- , minimum- , elem- , notElem- , find- , index- , elemIndex- , findIndex- , count-- -- * Re-exports- -- $reexports- , module Control.Foldl- , module Data.Text- ) where--import Control.Foldl (Fold)-import Control.Foldl.Internal (Maybe'(..), lazy, strict, Either'(..), hush)-import qualified Control.Foldl as L-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Lazy as Lazy-import Prelude hiding (- head, last, null, length, any, all, maximum, minimum, elem, notElem )---- | Apply a strict left 'Fold' to lazy text-fold :: Fold Text a -> Lazy.Text -> a-fold (L.Fold step begin done) as = done (Lazy.foldlChunks step begin as)-{-# INLINABLE fold #-}--{-| Get the first character of a text stream or return 'Nothing' if the stream- is empty--}-head :: Fold Text (Maybe Char)-head = L.Fold step Nothing' lazy- where- step mc txt =- if T.null txt- then mc- else case mc of- Just' _ -> mc- Nothing' -> Just' (T.head txt)-{-# INLINABLE head #-}--{-| Get the last character of a text stream or return 'Nothing' if the text- stream is empty--}-last :: Fold Text (Maybe Char)-last = L.Fold step Nothing' lazy- where- step mc txt =- if T.null txt- then mc- else Just' (T.last txt)- -- TODO: Use `unsafeLast` when Debian Stable Haskell Platform has it-{-# INLINABLE last #-}---- | Returns 'True' if the text stream is empty, 'False' otherwise-null :: Fold Text Bool-null = L.Fold step True id- where- step isNull txt = isNull && T.null txt -{-# INLINABLE null #-}---- | Return the length of the text stream in characters-length :: Num n => Fold Text n-length = L.Fold (\n txt -> n + fromIntegral (T.length txt)) 0 id-{-# INLINABLE length #-}--{-| @(all predicate)@ returns 'True' if all characters satisfy the predicate,- 'False' otherwise--}-all :: (Char -> Bool) -> Fold Text Bool-all predicate = L.Fold (\b txt -> b && T.all predicate txt) True id-{-# INLINABLE all #-}--{-| @(any predicate)@ returns 'True' if any character satisfies the predicate,- 'False' otherwise--}-any :: (Char -> Bool) -> Fold Text Bool-any predicate = L.Fold (\b txt -> b || T.any predicate txt) False id-{-# INLINABLE any #-}---- | Computes the maximum character-maximum :: Fold Text (Maybe Char)-maximum = L.Fold step Nothing' lazy- where- step mc txt =- if T.null txt- then mc- else Just' (case mc of- Nothing' -> T.maximum txt- Just' c -> max c (T.maximum txt) )-{-# INLINABLE maximum #-}---- | Computes the minimum character-minimum :: Fold Text (Maybe Char)-minimum = L.Fold step Nothing' lazy- where- step mc txt =- if T.null txt- then mc- else Just' (case mc of- Nothing' -> T.minimum txt- Just' c -> min c (T.minimum txt) )-{-# INLINABLE minimum #-}--{-| @(elem c)@ returns 'True' if the text stream has a character equal to @c@,- 'False' otherwise--}-elem :: Char -> Fold Text Bool-elem c = any (c ==)-{-# INLINABLE elem #-}--{-| @(notElem c)@ returns 'False' if the text stream has a character equal to- @c@, 'True' otherwise--}-notElem :: Char -> Fold Text Bool-notElem c = all (c /=)-{-# INLINABLE notElem #-}--{-| @(find predicate)@ returns the first character that satisfies the predicate- or 'Nothing' if no character satisfies the predicate--}-find :: (Char -> Bool) -> Fold Text (Maybe Char)-find predicate = L.Fold step Nothing' lazy- where- step mc txt = case mc of- Nothing' -> strict (T.find predicate txt)- Just' _ -> mc-{-# INLINABLE find #-}--{-| @(index n)@ returns the @n@th character of the text stream, or 'Nothing' if- the stream has an insufficient number of characters--}-index :: Integral n => n -> Fold Text (Maybe Char)-index i = L.Fold step (Left' (fromIntegral i)) hush- where- step x txt = case x of- Left' remainder ->- let len = T.length txt- in if remainder < len- then Right' (T.index txt remainder)- else Left' (remainder - len)- _ -> x-{-# INLINABLE index #-}--{-| @(elemIndex c)@ returns the index of the first character that equals @c@,- or 'Nothing' if no character matches--}-elemIndex :: Num n => Char -> Fold Text (Maybe n)-elemIndex c = findIndex (c ==)-{-# INLINABLE elemIndex #-}--{-| @(findIndex predicate)@ returns the index of the first character that- satisfies the predicate, or 'Nothing' if no character satisfies the- predicate--}-findIndex :: Num n => (Char -> Bool) -> Fold Text (Maybe n)-findIndex predicate = L.Fold step (Left' 0) hush- where- step x txt = case x of- Left' m -> case T.findIndex predicate txt of- Nothing -> Left' (m + fromIntegral (T.length txt))- Just n -> Right' (m + fromIntegral n)- _ -> x-{-# INLINABLE findIndex #-}---- | @(count c)@ returns the number of times @c@ appears-count :: Num n => Char -> Fold Text n-count c = L.Fold step 0 id- where- step n txt = n + fromIntegral (T.count (T.singleton c) txt)-{-# INLINABLE count #-}--{- $reexports- "Control.Foldl" re-exports the 'Fold' type-- @Data.Text@ re-exports the 'Text' type--}+-- | Folds for text streams + +module Control.Foldl.Text ( + -- * Folding + fold + + -- * Folds + , head + , last + , null + , length + , any + , all + , maximum + , minimum + , elem + , notElem + , find + , index + , elemIndex + , findIndex + , count + + -- * Re-exports + -- $reexports + , module Control.Foldl + , module Data.Text + ) where + +import Control.Foldl (Fold) +import Control.Foldl.Internal (Maybe'(..), lazy, strict, Either'(..), hush) +import qualified Control.Foldl as L +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.Lazy as Lazy +import Prelude hiding ( + head, last, null, length, any, all, maximum, minimum, elem, notElem ) + +-- | Apply a strict left 'Fold' to lazy text +fold :: Fold Text a -> Lazy.Text -> a +fold (L.Fold step begin done) as = done (Lazy.foldlChunks step begin as) +{-# INLINABLE fold #-} + +{-| Get the first character of a text stream or return 'Nothing' if the stream + is empty +-} +head :: Fold Text (Maybe Char) +head = L.Fold step Nothing' lazy + where + step mc txt = + if T.null txt + then mc + else case mc of + Just' _ -> mc + Nothing' -> Just' (T.head txt) +{-# INLINABLE head #-} + +{-| Get the last character of a text stream or return 'Nothing' if the text + stream is empty +-} +last :: Fold Text (Maybe Char) +last = L.Fold step Nothing' lazy + where + step mc txt = + if T.null txt + then mc + else Just' (T.last txt) + -- TODO: Use `unsafeLast` when Debian Stable Haskell Platform has it +{-# INLINABLE last #-} + +-- | Returns 'True' if the text stream is empty, 'False' otherwise +null :: Fold Text Bool +null = L.Fold step True id + where + step isNull txt = isNull && T.null txt +{-# INLINABLE null #-} + +-- | Return the length of the text stream in characters +length :: Num n => Fold Text n +length = L.Fold (\n txt -> n + fromIntegral (T.length txt)) 0 id +{-# INLINABLE length #-} + +{-| @(all predicate)@ returns 'True' if all characters satisfy the predicate, + 'False' otherwise +-} +all :: (Char -> Bool) -> Fold Text Bool +all predicate = L.Fold (\b txt -> b && T.all predicate txt) True id +{-# INLINABLE all #-} + +{-| @(any predicate)@ returns 'True' if any character satisfies the predicate, + 'False' otherwise +-} +any :: (Char -> Bool) -> Fold Text Bool +any predicate = L.Fold (\b txt -> b || T.any predicate txt) False id +{-# INLINABLE any #-} + +-- | Computes the maximum character +maximum :: Fold Text (Maybe Char) +maximum = L.Fold step Nothing' lazy + where + step mc txt = + if T.null txt + then mc + else Just' (case mc of + Nothing' -> T.maximum txt + Just' c -> max c (T.maximum txt) ) +{-# INLINABLE maximum #-} + +-- | Computes the minimum character +minimum :: Fold Text (Maybe Char) +minimum = L.Fold step Nothing' lazy + where + step mc txt = + if T.null txt + then mc + else Just' (case mc of + Nothing' -> T.minimum txt + Just' c -> min c (T.minimum txt) ) +{-# INLINABLE minimum #-} + +{-| @(elem c)@ returns 'True' if the text stream has a character equal to @c@, + 'False' otherwise +-} +elem :: Char -> Fold Text Bool +elem c = any (c ==) +{-# INLINABLE elem #-} + +{-| @(notElem c)@ returns 'False' if the text stream has a character equal to + @c@, 'True' otherwise +-} +notElem :: Char -> Fold Text Bool +notElem c = all (c /=) +{-# INLINABLE notElem #-} + +{-| @(find predicate)@ returns the first character that satisfies the predicate + or 'Nothing' if no character satisfies the predicate +-} +find :: (Char -> Bool) -> Fold Text (Maybe Char) +find predicate = L.Fold step Nothing' lazy + where + step mc txt = case mc of + Nothing' -> strict (T.find predicate txt) + Just' _ -> mc +{-# INLINABLE find #-} + +{-| @(index n)@ returns the @n@th character of the text stream, or 'Nothing' if + the stream has an insufficient number of characters +-} +index :: Integral n => n -> Fold Text (Maybe Char) +index i = L.Fold step (Left' (fromIntegral i)) hush + where + step x txt = case x of + Left' remainder -> + let len = T.length txt + in if remainder < len + then Right' (T.index txt remainder) + else Left' (remainder - len) + _ -> x +{-# INLINABLE index #-} + +{-| @(elemIndex c)@ returns the index of the first character that equals @c@, + or 'Nothing' if no character matches +-} +elemIndex :: Num n => Char -> Fold Text (Maybe n) +elemIndex c = findIndex (c ==) +{-# INLINABLE elemIndex #-} + +{-| @(findIndex predicate)@ returns the index of the first character that + satisfies the predicate, or 'Nothing' if no character satisfies the + predicate +-} +findIndex :: Num n => (Char -> Bool) -> Fold Text (Maybe n) +findIndex predicate = L.Fold step (Left' 0) hush + where + step x txt = case x of + Left' m -> case T.findIndex predicate txt of + Nothing -> Left' (m + fromIntegral (T.length txt)) + Just n -> Right' (m + fromIntegral n) + _ -> x +{-# INLINABLE findIndex #-} + +-- | @(count c)@ returns the number of times @c@ appears +count :: Num n => Char -> Fold Text n +count c = L.Fold step 0 id + where + step n txt = n + fromIntegral (T.count (T.singleton c) txt) +{-# INLINABLE count #-} + +{- $reexports + "Control.Foldl" re-exports the 'Fold' type + + @Data.Text@ re-exports the 'Text' type +-}