{-# LANGUAGE MagicHash #-}{-# LANGUAGE CPP #-}{-# LANGUAGE Rank2Types #-}{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE MultiParamTypeClasses #-}{-# LANGUAGE FunctionalDependencies #-}#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ > 704{-# LANGUAGE Trustworthy #-}#endif#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ > 706{-# LANGUAGE DefaultSignatures #-}#define MPTC_DEFAULTS#endif#ifndef MIN_VERSION_containers#define MIN_VERSION_containers(x,y,z) 1#endif--------------------------------------------------------------------------------- |-- Module : Control.Lens.WithIndex-- Copyright : (C) 2012 Edward Kmett-- License : BSD-style (see the file LICENSE)-- Maintainer : Edward Kmett <ekmett@gmail.com>-- Stability : provisional-- Portability : Rank2Types---- (These need to be defined together for @DefaultSignatures@ to work.)-------------------------------------------------------------------------------moduleControl.Lens.WithIndex(-- * Indexed FunctorsFunctorWithIndex(..),imapped-- * Indexed Foldables,FoldableWithIndex(..),ifolded,ifolding-- ** Indexed Foldable Combinators,iany,iall,itraverse_,ifor_,imapM_,iforM_,iconcatMap,ifind,ifoldrM,ifoldlM,itoList-- * Converting to Folds,withIndices,indices-- * Indexed Traversables,TraversableWithIndex(..),itraversed-- * Indexed Traversable Combinators,ifor,imapM,iforM,imapAccumR,imapAccumL,iwhere)whereimportControl.ApplicativeimportControl.Applicative.BackwardsimportControl.Monad(void,liftM)importControl.Monad.Trans.State.LazyasLazyimportControl.Lens.FoldimportControl.Lens.InternalimportControl.Lens.UnsafeimportControl.Lens.IndexedimportControl.Lens.IndexedSetterimportControl.Lens.IndexedFoldimportControl.Lens.IndexedTraversalimportData.FoldableimportData.HashableimportData.HashMap.LazyasHashMapimportData.IntMapasIntMapimportData.MapasMapimportData.MonoidimportData.Sequencehiding(index)importData.TraversableimportData.Vector(Vector)importqualifiedData.VectorasV-- $setup-- >>> import Control.Lens--------------------------------------------------------------------------------- FunctorWithIndex--------------------------------------------------------------------------------- | A 'Functor' with an additional index.---- Instances must satisfy a modified form of the 'Functor' laws:---- @-- 'imap' f '.' 'imap' g ≡ 'imap' (\i -> f i . g i)-- 'imap' (\_ a -> a) ≡ 'id'-- @classFunctorf=>FunctorWithIndexif|f->iwhere-- | Map with access to the index.imap::(i->a->b)->fa->fb#ifdef MPTC_DEFAULTSdefaultimap::TraversableWithIndexif=>(i->a->b)->fa->fbimap=imapOfitraversed#endif-- | The 'IndexedSetter' for a 'FunctorWithIndex'.---- If you don't need access to the index, then 'mapped' is more flexible in what it accepts.imapped::FunctorWithIndexif=>IndexedSetteri(fa)(fb)abimapped=isetsimap{-# INLINE imapped #-}--------------------------------------------------------------------------------- FoldableWithIndex--------------------------------------------------------------------------------- | A container that supports folding with an additional index.classFoldablef=>FoldableWithIndexif|f->iwhere---- |-- Fold a container by mapping value to an arbitrary 'Monoid' with access to the index @i@.---- When you don't need access to the index then 'foldMap' is more flexible in what it accepts.---- @'foldMap' ≡ 'ifoldMap' '.' 'const'@ifoldMap::Monoidm=>(i->a->m)->fa->m#ifdef MPTC_DEFAULTSdefaultifoldMap::(TraversableWithIndexif,Monoidm)=>(i->a->m)->fa->mifoldMap=ifoldMapOfitraversed{-# INLINE ifoldMap #-}#endif-- | Right-associative fold of an indexed container with access to the index @i@.---- When you don't need access to the index then 'Data.Foldable.foldr' is more flexible in what it accepts.---- @'Data.Foldable.foldr' ≡ 'ifoldr' '.' 'const'@ifoldr::(i->a->b->b)->b->fa->bifoldrfzt=appEndo(ifoldMap(\i->endo#(fi))t)z-- |-- Left-associative fold of an indexed container with access to the index @i@.---- When you don't need access to the index then 'foldl' is more flexible in what it accepts.---- @'foldl' ≡ 'ifoldl' '.' 'const'@ifoldl::(i->b->a->b)->b->fa->bifoldlfzt=appEndo(getDual(ifoldMap(\i->dual#(endo#(flip(fi))))t))z-- | /Strictly/ fold right over the elements of a structure with access to the index @i@.---- When you don't need access to the index then 'foldr'' is more flexible in what it accepts.---- @'foldr'' ≡ 'ifoldr'' '.' 'const'@ifoldr'::(i->a->b->b)->b->fa->bifoldr'fz0xs=ifoldlf'idxsz0wheref'ikxz=k$!fixz-- | Fold over the elements of a structure with an index, associating to the left, but /strictly/.---- When you don't need access to the index then 'Control.Lens.Fold.foldlOf'' is more flexible in what it accepts.---- @'Control.Lens.Fold.foldlOf'' l ≡ 'ifoldlOf'' l '.' 'const'@---- @-- 'ifoldlOf'' :: 'Control.Lens.IndexedGetter.IndexedGetter' i a c -> (i -> e -> c -> e) -> e -> a -> e-- 'ifoldlOf'' :: 'IndexedFold' i a c -> (i -> e -> c -> e) -> e -> a -> e-- 'ifoldlOf'' :: 'Control.Lens.IndexedLens.SimpleIndexedLens' i a c -> (i -> e -> c -> e) -> e -> a -> e-- 'ifoldlOf'' :: 'Control.Lens.IndexedTraversal.SimpleIndexedTraversal' i a c -> (i -> e -> c -> e) -> e -> a -> e-- @ifoldl'::(i->b->a->b)->b->fa->bifoldl'fz0xs=ifoldrf'idxsz0wheref'ixkz=k$!fizx-- | The 'IndexedFold' of a 'FoldableWithIndex' container.ifolded::FoldableWithIndexif=>IndexedFoldi(fa)aifolded=index$\f->coerce.getFolding.ifoldMap(\i->folding#(fi)){-# INLINE ifolded #-}-- | 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'.ifolding::FoldableWithIndexif=>(a->fc)->IndexedFoldiacifoldingafc=index$\icgd->coerce.itraverse_icgd.afc{-# INLINE ifolding #-}-- |-- Return whether or not any element in a container satisfies a predicate, with access to the index @i@.---- When you don't need access to the index then 'any' is more flexible in what it accepts.---- @'any' = 'iany' '.' 'const'@iany::FoldableWithIndexif=>(i->a->Bool)->fa->Boolianyf=getAny#(ifoldMap(\i->any#(fi))){-# INLINE iany #-}-- |-- Return whether or not all elements in a container satisfy a predicate, with access to the index @i@.---- When you don't need access to the index then 'all' is more flexible in what it accepts.---- @'all' ≡ 'iall' '.' 'const'@iall::FoldableWithIndexif=>(i->a->Bool)->fa->Booliallf=getAll#(ifoldMap(\i->all#(fi))){-# INLINE iall #-}-- |-- Traverse elements with access to the index @i@, discarding the results.---- When you don't need access to the index then 'traverse_' is more flexible in what it accepts.---- @'traverse_' l = 'itraverse' '.' 'const'@itraverse_::(FoldableWithIndexit,Applicativef)=>(i->a->fb)->ta->f()itraverse_f=getTraversed#(ifoldMap(\i->traversed#(void.fi))){-# INLINE itraverse_ #-}-- |-- Traverse elements with access to the index @i@, discarding the results (with the arguments flipped).---- @'ifor_' ≡ 'flip' 'itraverse_'@---- When you don't need access to the index then 'for_' is more flexible in what it accepts.---- @'for_' a ≡ 'ifor_' a '.' 'const'@ifor_::(FoldableWithIndexit,Applicativef)=>ta->(i->a->fb)->f()ifor_=flipitraverse_{-# INLINE ifor_ #-}-- |-- Run monadic actions for each target of an 'IndexedFold' or 'Control.Lens.IndexedTraversal.IndexedTraversal' with access to the index,-- discarding the results.---- When you don't need access to the index then 'Control.Lens.Fold.mapMOf_' is more flexible in what it accepts.---- @'mapM_' ≡ 'imapM' '.' 'const'@imapM_::(FoldableWithIndexit,Monadm)=>(i->a->mb)->ta->m()imapM_f=getSequenced#(ifoldMap(\i->sequenced#(liftMskip.fi))){-# INLINE imapM_ #-}-- |-- Run monadic actions for each target of an 'IndexedFold' or 'Control.Lens.IndexedTraversal.IndexedTraversal' with access to the index,-- discarding the results (with the arguments flipped).---- @'iforM_' ≡ 'flip' 'imapM_'@---- When you don't need access to the index then 'Control.Lens.Fold.forMOf_' is more flexible in what it accepts.---- @'Control.Lens.Fold.forMOf_' l a ≡ 'iforMOf' l a '.' 'const'@iforM_::(FoldableWithIndexit,Monadm)=>ta->(i->a->mb)->m()iforM_=flipimapM_{-# INLINE iforM_ #-}-- |-- Concatenate the results of a function of the elements of an indexed container with access to the index.---- When you don't need access to the index then 'concatMap' is more flexible in what it accepts.---- @-- 'concatMap' ≡ 'iconcatMap' . 'const'-- 'iconcatMap' ≡ 'ifoldMap'-- @iconcatMap::FoldableWithIndexif=>(i->a->[b])->fa->[b]iconcatMap=ifoldMap{-# INLINE iconcatMap #-}-- | Searches a container with a predicate that is also supplied the index, returning the left-most element of the structure-- matching the predicate, or 'Nothing' if there is no such element.---- When you don't need access to the index then 'find' is more flexible in what it accepts.---- @'find' ≡ 'ifind' '.' 'const'@ifind::FoldableWithIndexif=>(i->a->Bool)->fa->Maybe(i,a)ifindp=getFirst.ifoldMapstepwherestepic|pic=First$Just(i,c)|otherwise=FirstNothing{-# INLINE ifind #-}-- | Monadic fold right over the elements of a structure with an index.---- When you don't need access to the index then 'foldrM' is more flexible in what it accepts.---- @'foldrM' ≡ 'ifoldrM' '.' 'const'@ifoldrM::(FoldableWithIndexif,Monadm)=>(i->a->b->mb)->b->fa->mbifoldrMfz0xs=ifoldlf'returnxsz0wheref'ikxz=fixz>>=k{-# INLINE ifoldrM #-}-- | Monadic fold over the elements of a structure with an index, associating to the left.---- When you don't need access to the index then 'foldlM' is more flexible in what it accepts.---- @'foldlM' ≡ 'ifoldlM' '.' 'const'@ifoldlM::(FoldableWithIndexif,Monadm)=>(i->b->a->mb)->b->fa->mbifoldlMfz0xs=ifoldrf'returnxsz0wheref'ixkz=fizx>>=k{-# INLINE ifoldlM #-}-- | Extract the key-value pairs from a structure.---- When you don't need access to the indices in the result, then 'toList' is more flexible in what it accepts.---- @'toList' ≡ 'map' 'fst' '.' 'itoList'@itoList::FoldableWithIndexif=>fa->[(i,a)]itoList=ifoldr(\ic->((i,c):))[]{-# INLINE itoList #-}--------------------------------------------------------------------------------- Converting to Folds--------------------------------------------------------------------------------- | Fold a container with indices returning both the indices and the values.withIndices::FoldableWithIndexif=>Fold(fa)(i,a)withIndicesf=coerce.getFolding.ifoldMap(\ia->Folding(f(i,a))){-# INLINE withIndices #-}-- | Fold a container with indices returning only the indices.indices::FoldableWithIndexif=>Fold(fa)iindicesf=coerce.getFolding#(ifoldMap(const.folding#f)){-# INLINE indices #-}--------------------------------------------------------------------------------- TraversableWithIndex--------------------------------------------------------------------------------- | A 'Traversable' with an additional index.---- An instance must satisfy a (modified) form of the 'Traversable' laws:---- @-- 'itraverse' ('const' 'Data.Functor.Identity.Identity') ≡ 'Data.Functor.Identity.Identity'-- 'fmap' ('itraverse' f) '.' 'itraverse' g ≡ 'getCompose' '.' 'itraverse' (\i -> 'Compose' '.' 'fmap' (f i) '.' g i)-- @class(FunctorWithIndexit,FoldableWithIndexit,Traversablet)=>TraversableWithIndexit|t->iwhere-- | Traverse an indexed container.itraverse::Applicativef=>(i->a->fb)->ta->f(tb)#ifdef MPTC_DEFAULTSdefaultitraverse::Applicativef=>(Int->a->fb)->ta->f(tb)itraverse=withIndex(indexedtraverse){-# INLINE itraverse #-}#endif-- | The 'IndexedTraversal' of a 'TraversableWithIndex' container.itraversed::TraversableWithIndexif=>IndexedTraversali(fa)(fb)abitraversed=indexitraverse{-# INLINE itraversed #-}-- |-- Traverse with an index (and the arguments flipped)---- @-- 'for' a ≡ 'ifor' a '.' 'const'-- 'ifor' ≡ 'flip' 'itraverse'-- @ifor::(TraversableWithIndexit,Applicativef)=>ta->(i->a->fb)->f(tb)ifor=flipitraverse{-# INLINE ifor #-}-- | Map each element of a structure to a monadic action,-- evaluate these actions from left to right, and collect the results, with access-- the index.---- When you don't need access to the index 'mapM' is more liberal in what it can accept.---- @'mapM' ≡ 'imapM' '.' 'const'@imapM::(TraversableWithIndexit,Monadm)=>(i->a->mb)->ta->m(tb)imapMf=unwrapMonad#(itraverse(\i->wrapMonad#(fi))){-# INLINE imapM #-}-- | Map each element of a structure to a monadic action,-- evaluate these actions from left to right, and collect the results, with access-- its position (and the arguments flipped).---- @-- 'forM' a ≡ 'iforM' a '.' 'const'-- 'iforM' ≡ 'flip' 'imapM'-- @iforM::(TraversableWithIndexit,Monadm)=>ta->(i->a->mb)->m(tb)iforM=flipimapM{-# INLINE iforM #-}-- | Generalizes 'Data.Traversable.mapAccumR' to add access to the index.---- 'imapAccumROf' accumulates state from right to left.---- @'Control.Lens.Traversal.mapAccumR' ≡ 'imapAccumR' '.' 'const'@imapAccumR::TraversableWithIndexit=>(i->s->a->(s,b))->s->ta->(s,tb)imapAccumRfs0a=swap(Lazy.runState(itraverse(\ic->Lazy.state(\s->swap(fisc)))a)s0){-# INLINE imapAccumR #-}-- | Generalizes 'Data.Traversable.mapAccumL' to add access to the index.---- 'imapAccumLOf' accumulates state from left to right.---- @'Control.Lens.Traversal.mapAccumLOf' ≡ 'imapAccumL' '.' 'const'@imapAccumL::TraversableWithIndexit=>(i->s->a->(s,b))->s->ta->(s,tb)imapAccumLfs0a=swap(Lazy.runState(forwards(itraverse(\ic->Backwards(Lazy.state(\s->swap(fisc))))a))s0){-# INLINE imapAccumL #-}-- | Access the element of an indexed container where the index matches a predicate.---- >>> over (iwhere (>0)) Prelude.reverse $ ["He","was","stressed","o_O"]-- ["He","saw","desserts","O_o"]iwhere::(TraversableWithIndexit)=>(i->Bool)->SimpleIndexedTraversali(ta)aiwherep=index$\fa->itraverse(\ic->ifpithenficelsepurec)a{-# INLINE iwhere #-}--------------------------------------------------------------------------------- Instances--------------------------------------------------------------------------------- | The position in the list is available as the index.instanceFunctorWithIndexInt[]whereimap=imapOfitraversedinstanceFoldableWithIndexInt[]whereifoldMap=ifoldMapOfitraversedinstanceTraversableWithIndexInt[]whereitraverse=withIndex(indexedtraverse)-- | The position in the sequence is available as the index.instanceFunctorWithIndexIntSeqwhereimap=imapOfitraversedinstanceFoldableWithIndexIntSeqwhereifoldMap=ifoldMapOfitraversedinstanceTraversableWithIndexIntSeqwhereitraverse=withIndex(indexedtraverse)instanceFunctorWithIndexIntVectorwhereimap=V.imapinstanceFoldableWithIndexIntVectorwhereifoldMap=ifoldMapOfitraversedifoldr=V.ifoldrifoldl=V.ifoldl.flipifoldr'=V.ifoldr'ifoldl'=V.ifoldl'.flipinstanceTraversableWithIndexIntVectorwhereitraversef=sequenceA.V.imapfinstanceFunctorWithIndexIntIntMapwhereimap=imapOfitraversedinstanceFoldableWithIndexIntIntMapwhereifoldMap=ifoldMapOfitraversedinstanceTraversableWithIndexIntIntMapwhere#if MIN_VERSION_containers(0,5,0)itraverse=IntMap.traverseWithKey#elseitraversef=sequenceA.IntMap.mapWithKeyf#endif{-# INLINE itraverse #-}instanceOrdk=>FunctorWithIndexk(Mapk)whereimap=imapOfitraversedinstanceOrdk=>FoldableWithIndexk(Mapk)whereifoldMap=ifoldMapOfitraversedinstanceOrdk=>TraversableWithIndexk(Mapk)where#if MIN_VERSION_containers(0,5,0)itraverse=Map.traverseWithKey#elseitraversef=sequenceA.Map.mapWithKeyf#endif{-# INLINE itraverse #-}instance(Eqk,Hashablek)=>FunctorWithIndexk(HashMapk)whereimap=imapOfitraversedinstance(Eqk,Hashablek)=>FoldableWithIndexk(HashMapk)whereifoldMap=ifoldMapOfitraversedinstance(Eqk,Hashablek)=>TraversableWithIndexk(HashMapk)whereitraverse=HashMap.traverseWithKey{-# INLINE itraverse #-}--------------------------------------------------------------------------------- Misc.-------------------------------------------------------------------------------swap::(a,b)->(b,a)swap(a,b)=(b,a){-# INLINE swap #-}skip::a->()skip_=(){-# INLINE skip #-}