content: -- |
  -- Module: Optics.IxAffineFold
  -- Description: An indexed version of an 'Optics.AffineFold.AffineFold'.
  --
  -- An 'IxAffineFold' is an indexed version of an 'Optics.AffineFold.AffineFold'.
  -- See the "Indexed optics" section of the overview documentation in the
  -- @Optics@ module of the main @optics@ package for more details on indexed
  -- optics.
  --
  module Optics.IxAffineFold
    (
    -- * Formation
      IxAffineFold
  
    -- * Introduction
    , iafolding
  
    -- * Elimination
    , ipreview
    , ipreviews
  
    -- * Computation
    -- |
    --
    -- @
    -- 'ipreview' ('iafolding' f) ≡ f
    -- @
  
    -- * Additional introduction forms
    , iafoldVL
  
    -- * Additional elimination forms
    , iatraverseOf_
  
    -- * Combinators
    , filteredBy
  
    -- * Monoid structure
    -- | 'IxAffineFold' admits a monoid structure where 'iafailing' combines folds
    -- (returning a result from the second fold only if the first returns none)
    -- and the identity element is 'Optics.IxAffineTraversal.ignored' (which
    -- returns no results).
    --
    -- /Note:/ There is no 'Optics.IxFold.isumming' equivalent that returns an
    -- 'IxAffineFold', because it would not need to return more than one result.
    --
    -- There is no 'Semigroup' or 'Monoid' instance for 'IxAffineFold', because
    -- there is not a unique choice of monoid to use that works for all optics,
    -- and the ('<>') operator could not be used to combine optics of different
    -- kinds.
    , iafailing
  
    -- * Subtyping
    , An_AffineFold
    ) where
  
  import Data.Profunctor.Indexed
  
  import Optics.AffineFold
  import Optics.Internal.Bi
  import Optics.Internal.Indexed
  import Optics.Internal.Optic
  import Optics.Internal.Utils
  
  -- | Type synonym for an indexed affine fold.
  type IxAffineFold i s a = Optic' An_AffineFold (WithIx i) s a
  
  -- | Obtain an 'IxAffineFold' by lifting 'itraverse_' like function.
  --
  -- @
  -- 'aifoldVL' '.' 'iatraverseOf_' ≡ 'id'
  -- 'aitraverseOf_' '.' 'iafoldVL' ≡ 'id'
  -- @
  --
  -- @since 0.3
  iafoldVL
    :: (forall f. Functor f => (forall r. r -> f r) -> (i -> a -> f u) -> s -> f v)
    -> IxAffineFold i s a
  iafoldVL f = Optic (rphantom . ivisit f . rphantom)
  {-# INLINE iafoldVL #-}
  
  -- | Retrieve the value along with its index targeted by an 'IxAffineFold'.
  ipreview
    :: (Is k An_AffineFold, is `HasSingleIndex` i)
    => Optic' k is s a
    -> s -> Maybe (i, a)
  ipreview o = ipreviews o (,)
  {-# INLINE ipreview #-}
  
  -- | Retrieve a function of the value and its index targeted by an
  -- 'IxAffineFold'.
  ipreviews
    :: (Is k An_AffineFold, is `HasSingleIndex` i)
    => Optic' k is s a
    -> (i -> a -> r) -> s -> Maybe r
  ipreviews o = \f -> runIxForgetM
    (getOptic (castOptic @An_AffineFold o) . IxForgetM $ \i -> Just . f i)
    id
  {-# INLINE ipreviews #-}
  
  -- | Traverse over the target of an 'IxAffineFold', computing a 'Functor'-based
  -- answer, but unlike 'Optics.IxAffineTraversal.iatraverseOf' do not construct a
  -- new structure.
  --
  -- @since 0.3
  iatraverseOf_
    :: (Is k An_AffineFold, Functor f, is `HasSingleIndex` i)
    => Optic' k is s a
    -> (forall r. r -> f r) -> (i -> a -> f u) -> s -> f ()
  iatraverseOf_ o point f s = case ipreview o s of
    Just (i, a) -> () <$ f i a
    Nothing     -> point ()
  {-# INLINE iatraverseOf_ #-}
  
  -- | Create an 'IxAffineFold' from a partial function.
  iafolding :: (s -> Maybe (i, a)) -> IxAffineFold i s a
  iafolding g = iafoldVL (\point f s -> maybe (point s) (uncurry' f) $ g s)
  {-# INLINE iafolding #-}
  
  -- | Obtain a potentially empty 'IxAffineFold' by taking the element from
  -- another 'AffineFold' and using it as an index.
  --
  -- @since 0.3
  filteredBy :: Is k An_AffineFold  => Optic' k is a i -> IxAffineFold i a a
  filteredBy p = iafoldVL $ \point f s -> case preview p s of
    Just i  -> f i s
    Nothing -> point s
  {-# INLINE filteredBy #-}
  
  -- | Try the first 'IxAffineFold'. If it returns no entry, try the second one.
  --
  iafailing
    :: (Is k An_AffineFold, Is l An_AffineFold,
        is1 `HasSingleIndex` i, is2 `HasSingleIndex` i)
    => Optic' k is1 s a
    -> Optic' l is2 s a
    -> IxAffineFold i s a
  iafailing a b = conjoined (afailing a b) $ iafolding $ \s ->
    maybe (ipreview b s) Just (ipreview a s)
  infixl 3 `iafailing` -- Same as (<|>)
  {-# INLINE iafailing #-}
  