{-# LANGUAGE CPP #-} {-# LANGUAGE Rank2Types #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} #ifdef TRUSTWORTHY {-# LANGUAGE Trustworthy #-} #endif {-# OPTIONS_GHC -fno-warn-orphans #-} ---------------------------------------------------------------------------- -- | -- Module : Control.Lens.Fold -- Copyright : (C) 2012-14 Edward Kmett -- License : BSD-style (see the file LICENSE) -- Maintainer : Edward Kmett <ekmett@gmail.com> -- Stability : provisional -- Portability : Rank2Types -- -- A @'Fold' s a@ is a generalization of something 'Foldable'. It allows -- you to extract multiple results from a container. A 'Foldable' container -- can be characterized by the behavior of -- @'Data.Foldable.foldMap' :: ('Foldable' t, 'Monoid' m) => (a -> m) -> t a -> m@. -- Since we want to be able to work with monomorphic containers, we could -- generalize this signature to @forall m. 'Monoid' m => (a -> m) -> s -> m@, -- and then decorate it with 'Const' to obtain -- -- @type 'Fold' s a = forall m. 'Monoid' m => 'Getting' m s a@ -- -- Every 'Getter' is a valid 'Fold' that simply doesn't use the 'Monoid' -- it is passed. -- -- In practice the type we use is slightly more complicated to allow for -- better error messages and for it to be transformed by certain -- 'Applicative' transformers. -- -- Everything you can do with a 'Foldable' container, you can with with a 'Fold' and there are -- combinators that generalize the usual 'Foldable' operations here. ---------------------------------------------------------------------------- module Control.Lens.Fold ( -- * Folds Fold , IndexedFold -- * Getting Started , (^..) , (^?) , (^?!) , pre, ipre , preview, previews, ipreview, ipreviews , preuse, preuses, ipreuse, ipreuses , has, hasn't -- ** Building Folds , folding, ifolding , folded , folded64 , unfolded , iterated , filtered , backwards , repeated , replicated , cycled , takingWhile , droppingWhile , worded, lined -- ** Folding , foldMapOf, foldOf , foldrOf, foldlOf , toListOf , anyOf, allOf, noneOf , andOf, orOf , productOf, sumOf , traverseOf_, forOf_, sequenceAOf_ , mapMOf_, forMOf_, sequenceOf_ , asumOf, msumOf , concatMapOf, concatOf , elemOf, notElemOf , lengthOf , nullOf, notNullOf , firstOf, lastOf , maximumOf, minimumOf , maximumByOf, minimumByOf , findOf , findMOf , foldrOf', foldlOf' , foldr1Of, foldl1Of , foldr1Of', foldl1Of' , foldrMOf, foldlMOf -- * Indexed Folds , (^@..) , (^@?) , (^@?!) -- ** Indexed Folding , ifoldMapOf , ifoldrOf , ifoldlOf , ianyOf , iallOf , inoneOf , itraverseOf_ , iforOf_ , imapMOf_ , iforMOf_ , iconcatMapOf , ifindOf , ifindMOf , ifoldrOf' , ifoldlOf' , ifoldrMOf , ifoldlMOf , itoListOf -- ** Building Indexed Folds , ifiltered , itakingWhile , idroppingWhile -- * Deprecated , headOf -- * Internal types , Leftmost , Rightmost , Traversed , Sequenced -- * Fold with Reified Monoid , foldBy , foldByOf , foldMapBy , foldMapByOf ) where import Control.Applicative as Applicative import Control.Applicative.Backwards import Control.Comonad import Control.Lens.Getter import Control.Lens.Internal.Fold import Control.Lens.Internal.Getter import Control.Lens.Internal.Indexed import Control.Lens.Internal.Magma import Control.Lens.Type import Control.Monad as Monad import Control.Monad.Reader import Control.Monad.State import Data.Foldable as Foldable import Data.Functor.Apply import Data.Functor.Compose import Data.Int (Int64) import Data.List (intercalate) import Data.Maybe import Data.Monoid import Data.Profunctor import Data.Profunctor.Rep import Data.Profunctor.Unsafe import Data.Traversable -- $setup -- >>> :set -XNoOverloadedStrings -- >>> import Control.Lens -- >>> import Control.Lens.Extras (is) -- >>> import Data.Function -- >>> import Data.List.Lens -- >>> import Debug.SimpleReflect.Expr -- >>> import Debug.SimpleReflect.Vars as Vars hiding (f,g) -- >>> import Control.DeepSeq (NFData (..), force) -- >>> import Control.Exception (evaluate) -- >>> import Data.Maybe (fromMaybe) -- >>> import System.Timeout (timeout) -- >>> let f :: Expr -> Expr; f = Debug.SimpleReflect.Vars.f -- >>> let g :: Expr -> Expr; g = Debug.SimpleReflect.Vars.g -- >>> let timingOut :: NFData a => a -> IO a; timingOut = fmap (fromMaybe (error "timeout")) . timeout (5*10^6) . evaluate . force #ifndef DISABLE_TEMPLATE_HASKELL #ifdef HLINT {-# ANN module "HLint: ignore Eta reduce" #-} {-# ANN module "HLint: ignore Use camelCase" #-} {-# ANN module "HLint: ignore Use curry" #-} #endif #endif infixl 8 ^.., ^?, ^?!, ^@.., ^@?, ^@?! -------------------------- -- Folds -------------------------- -- | Obtain a 'Fold' by lifting an operation that returns a 'Foldable' result. -- -- This can be useful to lift operations from @Data.List@ and elsewhere into a 'Fold'. -- -- >>> [1,2,3,4]^..folding tail -- [2,3,4] folding :: (Foldable f, Contravariant g, Applicative g) => (s -> f a) -> LensLike g s t a b folding sfa agb = coerce . traverse_ agb . sfa {-# INLINE folding #-} ifolding :: (Foldable f, Indexable i p, Contravariant g, Applicative g) => (s -> f (i, a)) -> Over p g s t a b ifolding sfa f = coerce . traverse_ (coerce . uncurry (indexed f)) . sfa {-# INLINE ifolding #-} -- | Obtain a 'Fold' from any 'Foldable' indexed by ordinal position. -- -- >>> Just 3^..folded -- [3] -- -- >>> Nothing^..folded -- [] -- -- >>> [(1,2),(3,4)]^..folded.both -- [1,2,3,4] folded :: Foldable f => IndexedFold Int (f a) a folded = conjoined folded' (indexing folded') {-# INLINE folded #-} -- | Obtain a 'Fold' from any 'Foldable' indexed by ordinal position. folded64 :: Foldable f => IndexedFold Int64 (f a) a folded64 = conjoined folded' (indexing64 folded') {-# INLINE folded64 #-} folded' :: Foldable f => Fold (f a) a folded' f = coerce . getFolding . foldMap (Folding #. f) {-# INLINE folded' #-} -- | Form a 'Fold1' by repeating the input forever. -- -- @ -- 'repeat' ≡ 'toListOf' 'repeated' -- @ -- -- >>> timingOut $ 5^..taking 20 repeated -- [5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5] repeated :: Fold1 a a repeated f a = as where as = f a .> as {-# INLINE repeated #-} -- | A 'Fold' that replicates its input @n@ times. -- -- @ -- 'replicate' n ≡ 'toListOf' ('replicated' n) -- @ -- -- >>> 5^..replicated 20 -- [5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5] replicated :: Int -> Fold a a replicated n0 f a = go n0 where m = f a go 0 = noEffect go n = m *> go (n - 1) {-# INLINE replicated #-} -- | Transform a non-empty 'Fold' into a 'Fold1' that loops over its elements over and over. -- -- >>> timingOut $ [1,2,3]^..taking 7 (cycled traverse) -- [1,2,3,1,2,3,1] cycled :: (Contravariant f, Apply f) => LensLike f s t a b -> LensLike f s t a b cycled l f a = as where as = l f a .> as {-# INLINE cycled #-} -- | Build a 'Fold' that unfolds its values from a seed. -- -- @ -- 'Prelude.unfoldr' ≡ 'toListOf' '.' 'unfolded' -- @ -- -- >>> 10^..unfolded (\b -> if b == 0 then Nothing else Just (b, b-1)) -- [10,9,8,7,6,5,4,3,2,1] unfolded :: (b -> Maybe (a, b)) -> Fold b a unfolded f g b0 = go b0 where go b = case f b of Just (a, b') -> g a *> go b' Nothing -> noEffect {-# INLINE unfolded #-} -- | @x '^.' 'iterated' f@ returns an infinite 'Fold1' of repeated applications of @f@ to @x@. -- -- @ -- 'toListOf' ('iterated' f) a ≡ 'iterate' f a -- @ iterated :: (a -> a) -> Fold1 a a iterated f g a0 = go a0 where go a = g a .> go (f a) {-# INLINE iterated #-} -- | Obtain an 'AffineFold' that can be composed with to filter another 'Lens', 'Iso', 'Getter', 'Fold' (or 'Traversal'). -- -- Note: This is /not/ a legal 'AffineTraversal', unless you are very careful not to invalidate the predicate on the target. -- -- Note: This is also /not/ a legal 'Prism', unless you are very careful not to inject a value that matches the predicate. -- -- As a counter example, consider that given @evens = 'filtered' 'even'@ the second 'Traversal' law is violated: -- -- @ -- 'Control.Lens.Setter.over' evens 'succ' '.' 'Control.Lens.Setter.over' evens 'succ' '/=' 'Control.Lens.Setter.over' evens ('succ' '.' 'succ') -- @ -- -- So, in order for this to qualify as a legal 'AffineTraversal' you can only use it for actions that preserve the result of the predicate! -- -- >>> [1..10]^..folded.filtered even -- [2,4,6,8,10] -- -- This will preserve an index if it is present. filtered :: (Choice p, Applicative f) => (a -> Bool) -> Optic' p f a a filtered p = dimap (\x -> if p x then Right x else Left x) (either pure id) . right' {-# INLINE filtered #-} -- | Obtain a 'Fold' by taking elements from another 'Fold', 'Lens', 'Iso', 'Getter' or 'Traversal' while a predicate holds. -- -- @ -- 'takeWhile' p ≡ 'toListOf' ('takingWhile' p 'folded') -- @ -- -- >>> timingOut $ toListOf (takingWhile (<=3) folded) [1..] -- [1,2,3] -- -- @ -- 'takingWhile' :: (a -> 'Bool') -> 'Fold' s a -> 'Fold' s a -- 'takingWhile' :: (a -> 'Bool') -> 'Getter' s a -> 'Fold' s a -- 'takingWhile' :: (a -> 'Bool') -> 'Traversal'' s a -> 'Fold' s a -- * See note below -- 'takingWhile' :: (a -> 'Bool') -> 'Lens'' s a -> 'Fold' s a -- * See note below -- 'takingWhile' :: (a -> 'Bool') -> 'Prism'' s a -> 'Fold' s a -- * See note below -- 'takingWhile' :: (a -> 'Bool') -> 'Iso'' s a -> 'Fold' s a -- * See note below -- 'takingWhile' :: (a -> 'Bool') -> 'IndexedTraversal'' i s a -> 'IndexedFold' i s a -- * See note below -- 'takingWhile' :: (a -> 'Bool') -> 'IndexedLens'' i s a -> 'IndexedFold' i s a -- * See note below -- 'takingWhile' :: (a -> 'Bool') -> 'IndexedFold' i s a -> 'IndexedFold' i s a -- 'takingWhile' :: (a -> 'Bool') -> 'IndexedGetter' i s a -> 'IndexedFold' i s a -- @ -- -- /Note:/ When applied to a 'Traversal', 'takingWhile' yields something that can be used as if it were a 'Traversal', but -- which is not a 'Traversal' per the laws, unless you are careful to ensure that you do not invalidate the predicate when -- writing back through it. takingWhile :: (Conjoined p, Applicative f) => (a -> Bool) -> Over p (TakingWhile p f a a) s t a a -> Over p f s t a a takingWhile p l pafb = fmap runMagma . traverse (corep pafb) . runTakingWhile . l flag where flag = cotabulate $ \wa -> let a = extract wa; r = p a in TakingWhile r a $ \pr -> if pr && r then Magma () wa else MagmaPure a {-# INLINE takingWhile #-} -- | Obtain a 'Fold' by dropping elements from another 'Fold', 'Lens', 'Iso', 'Getter' or 'Traversal' while a predicate holds. -- -- @ -- 'dropWhile' p ≡ 'toListOf' ('droppingWhile' p 'folded') -- @ -- -- >>> toListOf (droppingWhile (<=3) folded) [1..6] -- [4,5,6] -- -- >>> toListOf (droppingWhile (<=3) folded) [1,6,1] -- [6,1] -- -- @ -- 'droppingWhile' :: (a -> 'Bool') -> 'Fold' s a -> 'Fold' s a -- 'droppingWhile' :: (a -> 'Bool') -> 'Getter' s a -> 'Fold' s a -- 'droppingWhile' :: (a -> 'Bool') -> 'Traversal'' s a -> 'Fold' s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'Lens'' s a -> 'Fold' s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'Prism'' s a -> 'Fold' s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'Iso'' s a -> 'Fold' s a -- see notes -- @ -- -- @ -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexPreservingTraversal'' s a -> 'IndexPreservingFold' s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexPreservingLens'' s a -> 'IndexPreservingFold' s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexPreservingGetter' s a -> 'IndexPreservingFold' s a -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexPreservingFold' s a -> 'IndexPreservingFold' s a -- @ -- -- @ -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexedTraversal'' i s a -> 'IndexedFold' i s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexedLens'' i s a -> 'IndexedFold' i s a -- see notes -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexedGetter' i s a -> 'IndexedFold' i s a -- 'droppingWhile' :: (a -> 'Bool') -> 'IndexedFold' i s a -> 'IndexedFold' i s a -- @ -- -- Note: Many uses of this combinator will yield something that meets the types, but not the laws of a valid -- 'Traversal' or 'IndexedTraversal'. The 'Traversal' and 'IndexedTraversal' laws are only satisfied if the -- new values you assign also pass the predicate! Otherwise subsequent traversals will visit fewer elements -- and 'Traversal' fusion is not sound. droppingWhile :: (Conjoined p, Profunctor q, Applicative f) => (a -> Bool) -> Optical p q (Compose (State Bool) f) s t a a -> Optical p q f s t a a droppingWhile p l f = (flip evalState True .# getCompose) `rmap` l g where g = cotabulate $ \wa -> Compose $ state $ \b -> let a = extract wa b' = b && p a in (if b' then pure a else corep f wa, b') {-# INLINE droppingWhile #-} -- | A 'Fold' over the individual 'words' of a 'String'. -- -- @ -- 'worded' :: 'Fold' 'String' 'String' -- 'worded' :: 'Traversal'' 'String' 'String' -- @ -- -- @ -- 'worded' :: 'IndexedFold' 'Int' 'String' 'String' -- 'worded' :: 'IndexedTraversal'' 'Int' 'String' 'String' -- @ -- -- Note: This function type-checks as a 'Traversal' but it doesn't satisfy the laws. It's only valid to use it -- when you don't insert any whitespace characters while traversing, and if your original 'String' contains only -- isolated space characters (and no other characters that count as space, such as non-breaking spaces). worded :: Applicative f => IndexedLensLike' Int f String String worded f = fmap unwords . conjoined traverse (indexing traverse) f . words {-# INLINE worded #-} -- | A 'Fold' over the individual 'lines' of a 'String'. -- -- @ -- 'lined' :: 'Fold' 'String' 'String' -- 'lined' :: 'Traversal'' 'String' 'String' -- @ -- -- @ -- 'lined' :: 'IndexedFold' 'Int' 'String' 'String' -- 'lined' :: 'IndexedTraversal'' 'Int' 'String' 'String' -- @ -- -- Note: This function type-checks as a 'Traversal' but it doesn't satisfy the laws. It's only valid to use it -- when you don't insert any newline characters while traversing, and if your original 'String' contains only -- isolated newline characters. lined :: Applicative f => IndexedLensLike' Int f String String lined f = fmap (intercalate "\n") . conjoined traverse (indexing traverse) f . lines {-# INLINE lined #-} -------------------------- -- Fold/Getter combinators -------------------------- -- | @ -- 'Data.Foldable.foldMap' = 'foldMapOf' 'folded' -- @ -- -- @ -- 'foldMapOf' ≡ 'views' -- 'ifoldMapOf' l = 'foldMapOf' l '.' 'Indexed' -- @ -- -- @ -- 'foldMapOf' :: 'Getter' s a -> (a -> r) -> s -> r -- 'foldMapOf' :: 'Monoid' r => 'Fold' s a -> (a -> r) -> s -> r -- 'foldMapOf' :: 'Lens'' s a -> (a -> r) -> s -> r -- 'foldMapOf' :: 'Iso'' s a -> (a -> r) -> s -> r -- 'foldMapOf' :: 'Monoid' r => 'Traversal'' s a -> (a -> r) -> s -> r -- 'foldMapOf' :: 'Monoid' r => 'Prism'' s a -> (a -> r) -> s -> r -- @ -- -- @ -- 'foldMapOf' :: 'Getting' r s a -> (a -> r) -> s -> r -- @ foldMapOf :: Profunctor p => Accessing p r s a -> p a r -> s -> r foldMapOf l f = getConst