-- |
-- 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 :: forall i a u s v.
(forall (f :: * -> *).
 Functor f =>
 (forall r. r -> f r) -> (i -> a -> f u) -> s -> f v)
-> IxAffineFold i s a
iafoldVL forall (f :: * -> *).
Functor f =>
(forall r. r -> f r) -> (i -> a -> f u) -> s -> f v
f = forall k (is :: IxList) s t a b.
(forall (p :: * -> * -> * -> *) i.
 Profunctor p =>
 Optic_ k p i (Curry is i) s t a b)
-> Optic k is s t a b
Optic (forall (p :: * -> * -> * -> *) i c a b.
(Profunctor p, Bicontravariant p) =>
p i c a -> p i c b
rphantom forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> * -> *) i a b s t j.
Visiting p =>
(forall (f :: * -> *).
 Functor f =>
 (forall r. r -> f r) -> (i -> a -> f b) -> s -> f t)
-> p j a b -> p (i -> j) s t
ivisit forall (f :: * -> *).
Functor f =>
(forall r. r -> f r) -> (i -> a -> f u) -> s -> f v
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> * -> *) i c a b.
(Profunctor p, Bicontravariant p) =>
p i c a -> p i c b
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 :: forall k (is :: IxList) i s a.
(Is k An_AffineFold, HasSingleIndex is i) =>
Optic' k is s a -> s -> Maybe (i, a)
ipreview Optic' k is s a
o = forall k (is :: IxList) i s a r.
(Is k An_AffineFold, HasSingleIndex is i) =>
Optic' k is s a -> (i -> a -> r) -> s -> Maybe r
ipreviews Optic' k is s a
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 :: forall k (is :: IxList) i s a r.
(Is k An_AffineFold, HasSingleIndex is i) =>
Optic' k is s a -> (i -> a -> r) -> s -> Maybe r
ipreviews Optic' k is s a
o = \i -> a -> r
f -> forall r i a b. IxForgetM r i a b -> i -> a -> Maybe r
runIxForgetM
  (forall (p :: * -> * -> * -> *) k (is :: IxList) s t a b i.
Profunctor p =>
Optic k is s t a b -> Optic_ k p i (Curry is i) s t a b
getOptic (forall destKind srcKind (is :: IxList) s t a b.
Is srcKind destKind =>
Optic srcKind is s t a b -> Optic destKind is s t a b
castOptic @An_AffineFold Optic' k is s a
o) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall r i a b. (i -> a -> Maybe r) -> IxForgetM r i a b
IxForgetM forall a b. (a -> b) -> a -> b
$ \i
i -> forall a. a -> Maybe a
Just forall b c a. (b -> c) -> (a -> b) -> a -> c
. i -> a -> r
f i
i)
  forall a. a -> a
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_ :: forall k (f :: * -> *) (is :: IxList) i s a u.
(Is k An_AffineFold, Functor f, HasSingleIndex is i) =>
Optic' k is s a
-> (forall r. r -> f r) -> (i -> a -> f u) -> s -> f ()
iatraverseOf_ Optic' k is s a
o forall r. r -> f r
point i -> a -> f u
f s
s = case forall k (is :: IxList) i s a.
(Is k An_AffineFold, HasSingleIndex is i) =>
Optic' k is s a -> s -> Maybe (i, a)
ipreview Optic' k is s a
o s
s of
  Just (i
i, a
a) -> () forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ i -> a -> f u
f i
i a
a
  Maybe (i, a)
Nothing     -> forall r. r -> f r
point ()
{-# INLINE iatraverseOf_ #-}

-- | Create an 'IxAffineFold' from a partial function.
iafolding :: (s -> Maybe (i, a)) -> IxAffineFold i s a
iafolding :: forall s i a. (s -> Maybe (i, a)) -> IxAffineFold i s a
iafolding s -> Maybe (i, a)
g = forall i a u s v.
(forall (f :: * -> *).
 Functor f =>
 (forall r. r -> f r) -> (i -> a -> f u) -> s -> f v)
-> IxAffineFold i s a
iafoldVL (\forall r. r -> f r
point i -> a -> f s
f s
s -> forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall r. r -> f r
point s
s) (forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry' i -> a -> f s
f) forall a b. (a -> b) -> a -> b
$ s -> Maybe (i, a)
g s
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 :: forall k (is :: IxList) a i.
Is k An_AffineFold =>
Optic' k is a i -> IxAffineFold i a a
filteredBy Optic' k is a i
p = forall i a u s v.
(forall (f :: * -> *).
 Functor f =>
 (forall r. r -> f r) -> (i -> a -> f u) -> s -> f v)
-> IxAffineFold i s a
iafoldVL forall a b. (a -> b) -> a -> b
$ \forall r. r -> f r
point i -> a -> f a
f a
s -> case forall k (is :: IxList) s a.
Is k An_AffineFold =>
Optic' k is s a -> s -> Maybe a
preview Optic' k is a i
p a
s of
  Just i
i  -> i -> a -> f a
f i
i a
s
  Maybe i
Nothing -> forall r. r -> f r
point a
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 :: forall k l (is1 :: IxList) i (is2 :: IxList) s a.
(Is k An_AffineFold, Is l An_AffineFold, HasSingleIndex is1 i,
 HasSingleIndex is2 i) =>
Optic' k is1 s a -> Optic' l is2 s a -> IxAffineFold i s a
iafailing Optic' k is1 s a
a Optic' l is2 s a
b = forall (is :: IxList) i k s t a b.
HasSingleIndex is i =>
Optic k '[] s t a b -> Optic k is s t a b -> Optic k is s t a b
conjoined (forall k l (is :: IxList) s a (js :: IxList).
(Is k An_AffineFold, Is l An_AffineFold) =>
Optic' k is s a -> Optic' l js s a -> AffineFold s a
afailing Optic' k is1 s a
a Optic' l is2 s a
b) forall a b. (a -> b) -> a -> b
$ forall s i a. (s -> Maybe (i, a)) -> IxAffineFold i s a
iafolding forall a b. (a -> b) -> a -> b
$ \s
s ->
  forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall k (is :: IxList) i s a.
(Is k An_AffineFold, HasSingleIndex is i) =>
Optic' k is s a -> s -> Maybe (i, a)
ipreview Optic' l is2 s a
b s
s) forall a. a -> Maybe a
Just (forall k (is :: IxList) i s a.
(Is k An_AffineFold, HasSingleIndex is i) =>
Optic' k is s a -> s -> Maybe (i, a)
ipreview Optic' k is1 s a
a s
s)
infixl 3 `iafailing` -- Same as (<|>)
{-# INLINE iafailing #-}