-- |
-- Module: Optics.IxLens
-- Description: An indexed version of a 'Optics.Lens.Lens'.
--
-- An 'IxLens' is an indexed version of a 'Optics.Lens.Lens'. 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.IxLens
  (
  -- * Formation
    IxLens
  , IxLens'

  -- * Introduction
  , ilens

  -- * Elimination
  -- | An 'IxLens' is in particular an 'Optics.IxGetter.IxGetter' and an
  -- 'Optics.IxSetter.IxSetter', therefore you can specialise types to obtain:
  --
  -- @
  -- 'Optics.IxGetter.iview' :: 'IxLens' i s t a b -> s -> (i, a)
  -- @
  --
  -- @
  -- 'Optics.IxSetter.iover' :: 'IxLens' i s t a b -> (i -> a -> b) -> s -> t
  -- 'Optics.IxSetter.iset'  :: 'IxLens' i s t a b -> (i      -> b) -> s -> t
  -- @

  -- * Additional introduction forms
  , chosen
  , devoid
  , ifst
  , isnd

  -- * Subtyping
  , A_Lens

  -- * van Laarhoven encoding
  , IxLensVL
  , IxLensVL'
  , ilensVL
  , toIxLensVL
  , withIxLensVL
  ) where

import Data.Void

import Data.Profunctor.Indexed

import Optics.Internal.Indexed
import Optics.Internal.Optic
import Optics.Internal.Utils

-- | Type synonym for a type-modifying indexed lens.
type IxLens i s t a b = Optic A_Lens (WithIx i) s t a b

-- | Type synonym for a type-preserving indexed lens.
type IxLens' i s a = Optic' A_Lens (WithIx i) s a

-- | Type synonym for a type-modifying van Laarhoven indexed lens.
type IxLensVL i s t a b =
  forall f. Functor f => (i -> a -> f b) -> s -> f t

-- | Type synonym for a type-preserving van Laarhoven indexed lens.
type IxLensVL' i s a = IxLensVL i s s a a

-- | Build an indexed lens from a getter and a setter.
--
-- If you want to build an 'IxLens' from the van Laarhoven representation, use
-- 'ilensVL'.
ilens :: (s -> (i, a)) -> (s -> b -> t) -> IxLens i s t a b
ilens :: forall s i a b t.
(s -> (i, a)) -> (s -> b -> t) -> IxLens i s t a b
ilens s -> (i, a)
get s -> b -> t
set = forall i s t a b. IxLensVL i s t a b -> IxLens i s t a b
ilensVL forall a b. (a -> b) -> a -> b
$ \i -> a -> f b
f s
s -> s -> b -> t
set s
s forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry' i -> a -> f b
f (s -> (i, a)
get s
s)
{-# INLINE ilens #-}

-- | Build an indexed lens from the van Laarhoven representation.
ilensVL :: IxLensVL i s t a b -> IxLens i s t a b
ilensVL :: forall i s t a b. IxLensVL i s t a b -> IxLens i s t a b
ilensVL IxLensVL i s t a b
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 a b s t j.
Strong p =>
(forall (f :: * -> *). Functor f => (i -> a -> f b) -> s -> f t)
-> p j a b -> p (i -> j) s t
ilinear IxLensVL i s t a b
f)
{-# INLINE ilensVL #-}

-- | Convert an indexed lens to its van Laarhoven representation.
toIxLensVL
  :: (Is k A_Lens, is `HasSingleIndex` i)
  => Optic k is s t a b
  -> IxLensVL i s t a b
toIxLensVL :: forall k (is :: IxList) i s t a b.
(Is k A_Lens, HasSingleIndex is i) =>
Optic k is s t a b -> IxLensVL i s t a b
toIxLensVL Optic k is s t a b
o = \i -> a -> f b
f ->
  forall (f :: * -> *) i a b. IxStar f i a b -> i -> a -> f b
runIxStar (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 @A_Lens Optic k is s t a b
o) (forall (f :: * -> *) i a b. (i -> a -> f b) -> IxStar f i a b
IxStar i -> a -> f b
f)) forall a. a -> a
id
{-# INLINE toIxLensVL #-}

-- | Work with an indexed lens in the van Laarhoven representation.
withIxLensVL
  :: (Is k A_Lens, is `HasSingleIndex` i)
  => Optic k is s t a b
  -> (IxLensVL i s t a b -> r)
  -> r
withIxLensVL :: forall k (is :: IxList) i s t a b r.
(Is k A_Lens, HasSingleIndex is i) =>
Optic k is s t a b -> (IxLensVL i s t a b -> r) -> r
withIxLensVL Optic k is s t a b
o IxLensVL i s t a b -> r
k = IxLensVL i s t a b -> r
k (forall k (is :: IxList) i s t a b.
(Is k A_Lens, HasSingleIndex is i) =>
Optic k is s t a b -> IxLensVL i s t a b
toIxLensVL Optic k is s t a b
o)
{-# INLINE withIxLensVL #-}

----------------------------------------
-- Lenses

-- | Focus on both sides of an 'Either'.
chosen :: IxLens (Either () ()) (Either a a) (Either b b) a b
chosen :: forall a b. IxLens (Either () ()) (Either a a) (Either b b) a b
chosen = forall i s t a b. IxLensVL i s t a b -> IxLens i s t a b
ilensVL forall a b. (a -> b) -> a -> b
$ \Either () () -> a -> f b
f -> \case
  Left  a
a -> forall a b. a -> Either a b
Left  forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either () () -> a -> f b
f (forall a b. a -> Either a b
Left ())  a
a
  Right a
a -> forall a b. b -> Either a b
Right forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Either () () -> a -> f b
f (forall a b. b -> Either a b
Right ()) a
a
{-# INLINE chosen #-}

-- | There is an indexed field for every type in the 'Void'.
--
-- >>> set (mapped % devoid) 1 []
-- []
--
-- >>> over (_Just % devoid) abs Nothing
-- Nothing
--
devoid :: IxLens' i Void a
devoid :: forall i a. IxLens' i Void a
devoid = forall s i a b t.
(s -> (i, a)) -> (s -> b -> t) -> IxLens i s t a b
ilens forall a. Void -> a
absurd forall a b. a -> b -> a
const
{-# INLINE devoid #-}

-- | Indexed '_1' with other half of a pair as an index.
--
-- See 'isnd' for examples.
--
-- @since 0.4
--
ifst :: IxLens i (a, i) (b, i) a b
ifst :: forall i a b. IxLens i (a, i) (b, i) a b
ifst = forall s i a b t.
(s -> (i, a)) -> (s -> b -> t) -> IxLens i s t a b
ilens (\(a
a, i
i) -> (i
i, a
a)) (\(a
_,i
i) b
b -> (b
b, i
i))

-- | Indexed '_2' with other half of a pair as an index.
-- Specialized version of 'itraversed' to pairs, which can be 'IxLens'.
--
-- >>> iview isnd ('a', True)
-- ('a',True)
--
-- That is not possible with 'itraversed', because it is an 'IxTraversal'.
--
-- >>> :t itraversed :: IxTraversal i (i, a) (i, b) a b
-- itraversed :: IxTraversal i (i, a) (i, b) a b
--   :: IxTraversal i (i, a) (i, b) a b
--
-- @since 0.4
--
isnd :: IxLens i (i, a) (i, b) a b
isnd :: forall i a b. IxLens i (i, a) (i, b) a b
isnd = forall s i a b t.
(s -> (i, a)) -> (s -> b -> t) -> IxLens i s t a b
ilens forall a. a -> a
id (\(i
i,a
_) b
b -> (i
i, b
b))

-- $setup
-- >>> import Optics.Core