-- |
-- Module: Optics.Setter
-- Description: Applies an update to all contained values.
--
-- A @'Setter' S T A B@ has the ability to lift a function of type
-- @A -> B@ 'over' a function of type @S -> T@, applying the function
-- to update all the @A@s contained in @S@.  This can be used to 'set'
-- all the @A@s to a single value (by lifting a constant function).
--
-- This can be seen as a generalisation of 'fmap', where the type @S@
-- does not need to be a type constructor with @A@ as its last
-- parameter.
--
module Optics.Setter
  (
  -- * Formation
    Setter
  , Setter'

  -- * Introduction
  , sets

  -- * Elimination
  , over

  -- * Computation
  -- |
  --
  -- @
  -- 'over' ('sets' f) ≡ f
  -- @

  -- * Well-formedness
  -- |
  --
  -- * __PutPut__: Setting twice is the same as setting once:
  --
  --     @
  --     'Optics.Setter.set' l v' ('Optics.Setter.set' l v s) ≡ 'Optics.Setter.set' l v' s
  --     @
  --
  -- * __Functoriality__: 'Setter's must preserve identities and composition:
  --
  --     @
  --     'over' s 'id' ≡ 'id'
  --     'over' s f '.' 'over' s g ≡ 'over' s (f '.' g)
  --     @

  -- * Additional introduction forms
  -- | See also 'Data.Set.Optics.setmapped', which changes the elements of a 'Data.Set.Set'.
  , mapped

  -- * Additional elimination forms
  , set
  , set'
  , over'
  , rewriteOf
  , transformOf

  -- * Subtyping
  , A_Setter
  -- | <<diagrams/Setter.png Setter in the optics hierarchy>>
  ) where

import Data.Profunctor.Indexed

import Optics.Internal.Optic
import Optics.Internal.Setter
import Optics.Internal.Utils

-- | Type synonym for a type-modifying setter.
type Setter s t a b = Optic A_Setter NoIx s t a b

-- | Type synonym for a type-preserving setter.
type Setter' s a = Optic' A_Setter NoIx s a

-- | Apply a setter as a modifier.
over
  :: Is k A_Setter
  => Optic k is s t a b
  -> (a -> b) -> s -> t
over :: forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> (a -> b) -> s -> t
over Optic k is s t a b
o = \a -> b
f -> forall (i :: OpticKind) (a :: OpticKind) (b :: OpticKind).
FunArrow i a b -> a -> b
runFunArrow forall (a :: OpticKind) b. (a -> b) -> a -> b
$ forall (p :: OpticKind -> OpticKind -> OpticKind -> OpticKind)
       (k :: OpticKind) (is :: IxList) (s :: OpticKind) (t :: OpticKind)
       (a :: OpticKind) (b :: OpticKind) (i :: OpticKind).
Profunctor p =>
Optic k is s t a b -> Optic_ k p i (Curry is i) s t a b
getOptic (forall (destKind :: OpticKind) (srcKind :: OpticKind)
       (is :: IxList) (s :: OpticKind) (t :: OpticKind) (a :: OpticKind)
       (b :: OpticKind).
Is srcKind destKind =>
Optic srcKind is s t a b -> Optic destKind is s t a b
castOptic @A_Setter Optic k is s t a b
o) (forall (i :: OpticKind) (a :: OpticKind) (b :: OpticKind).
(a -> b) -> FunArrow i a b
FunArrow a -> b
f)
{-# INLINE over #-}

-- | Apply a setter as a modifier, strictly.
--
-- TODO DOC: what exactly is the strictness property?
--
-- Example:
--
-- @
--  f :: Int -> (Int, a) -> (Int, a)
--  f k acc
--    | k > 0     = f (k - 1) $ 'over'' 'Data.Tuple.Optics._1' (+1) acc
--    | otherwise = acc
-- @
--
-- runs in constant space, but would result in a space leak if used with 'over'.
--
-- Note that replacing '$' with '$!' or 'Data.Tuple.Optics._1' with
-- 'Data.Tuple.Optics._1'' (which amount to the same thing) doesn't help when
-- 'over' is used, because the first coordinate of a pair is never forced.
--
over'
  :: Is k A_Setter
  => Optic k is s t a b
  -> (a -> b) -> s -> t
over' :: forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> (a -> b) -> s -> t
over' Optic k is s t a b
o = \a -> b
f ->
  let star :: Star Identity' (Curry is Any) s t
star = forall (p :: OpticKind -> OpticKind -> OpticKind -> OpticKind)
       (k :: OpticKind) (is :: IxList) (s :: OpticKind) (t :: OpticKind)
       (a :: OpticKind) (b :: OpticKind) (i :: OpticKind).
Profunctor p =>
Optic k is s t a b -> Optic_ k p i (Curry is i) s t a b
getOptic (forall (destKind :: OpticKind) (srcKind :: OpticKind)
       (is :: IxList) (s :: OpticKind) (t :: OpticKind) (a :: OpticKind)
       (b :: OpticKind).
Is srcKind destKind =>
Optic srcKind is s t a b -> Optic destKind is s t a b
castOptic @A_Setter Optic k is s t a b
o) forall (a :: OpticKind) b. (a -> b) -> a -> b
$ forall (f :: OpticKind -> OpticKind) (i :: OpticKind)
       (a :: OpticKind) (b :: OpticKind).
(a -> f b) -> Star f i a b
Star (forall (a :: OpticKind). a -> Identity' a
wrapIdentity' forall (b :: OpticKind) (c :: OpticKind) (a :: OpticKind).
(b -> c) -> (a -> b) -> a -> c
. a -> b
f)
  in forall (a :: OpticKind). Identity' a -> a
unwrapIdentity' forall (b :: OpticKind) (c :: OpticKind) (a :: OpticKind).
(b -> c) -> (a -> b) -> a -> c
. forall (f :: OpticKind -> OpticKind) (i :: OpticKind)
       (a :: OpticKind) (b :: OpticKind).
Star f i a b -> a -> f b
runStar Star Identity' (Curry is Any) s t
star
{-# INLINE over' #-}

-- | Apply a setter.
--
-- @
-- 'set' o v ≡ 'over' o ('const' v)
-- @
--
-- >>> set _1 'x' ('y', 'z')
-- ('x','z')
--
set
  :: Is k A_Setter
  => Optic k is s t a b
  -> b -> s -> t
set :: forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> b -> s -> t
set Optic k is s t a b
o = forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> (a -> b) -> s -> t
over Optic k is s t a b
o forall (b :: OpticKind) (c :: OpticKind) (a :: OpticKind).
(b -> c) -> (a -> b) -> a -> c
. forall (a :: OpticKind) (b :: OpticKind). a -> b -> a
const
{-# INLINE set #-}

-- | Apply a setter, strictly.
--
-- TODO DOC: what exactly is the strictness property?
--
set'
  :: Is k A_Setter
  => Optic k is s t a b
  -> b -> s -> t
set' :: forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> b -> s -> t
set' Optic k is s t a b
o = forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> (a -> b) -> s -> t
over' Optic k is s t a b
o forall (b :: OpticKind) (c :: OpticKind) (a :: OpticKind).
(b -> c) -> (a -> b) -> a -> c
. forall (a :: OpticKind) (b :: OpticKind). a -> b -> a
const
{-# INLINE set' #-}

-- | Build a setter from a function to modify the element(s), which must respect
-- the well-formedness laws.
sets
  :: ((a -> b) -> s -> t)
  -> Setter s t a b
sets :: forall (a :: OpticKind) (b :: OpticKind) (s :: OpticKind)
       (t :: OpticKind).
((a -> b) -> s -> t) -> Setter s t a b
sets (a -> b) -> s -> t
f = forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
(forall (p :: OpticKind -> OpticKind -> OpticKind -> OpticKind)
        (i :: OpticKind).
 Profunctor p =>
 Optic_ k p i (Curry is i) s t a b)
-> Optic k is s t a b
Optic (forall (p :: OpticKind -> OpticKind -> OpticKind -> OpticKind)
       (a :: OpticKind) (b :: OpticKind) (s :: OpticKind) (t :: OpticKind)
       (i :: OpticKind).
Mapping p =>
((a -> b) -> s -> t) -> p i a b -> p i s t
roam (a -> b) -> s -> t
f)
{-# INLINE sets #-}

-- | Create a 'Setter' for a 'Functor'.
--
-- @
-- 'over' 'mapped' ≡ 'fmap'
-- @
--
mapped :: Functor f => Setter (f a) (f b) a b
mapped :: forall (f :: OpticKind -> OpticKind) (a :: OpticKind)
       (b :: OpticKind).
Functor f =>
Setter (f a) (f b) a b
mapped = forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
(forall (p :: OpticKind -> OpticKind -> OpticKind -> OpticKind)
        (i :: OpticKind).
 Profunctor p =>
 Optic_ k p i (Curry is i) s t a b)
-> Optic k is s t a b
Optic forall (p :: OpticKind -> OpticKind -> OpticKind -> OpticKind)
       (f :: OpticKind -> OpticKind) (i :: OpticKind) (a :: OpticKind)
       (b :: OpticKind).
(Mapping p, Functor f) =>
Optic__ p i i (f a) (f b) a b
mapped__
{-# INLINE mapped #-}

-- | Rewrite by applying a rule everywhere you can. Ensures that the rule cannot
-- be applied anywhere in the result:
--
-- @
-- propRewriteOf l r x = 'all' ('Data.Just.isNothing' '.' r) ('universeOf' l ('rewriteOf' l r x))
-- @
--
-- Usually 'transformOf' is more appropriate, but 'rewriteOf' can give better
-- compositionality. Given two single transformations @f@ and @g@, you can
-- construct @\\a -> f a '<|>' g a@ which performs both rewrites until a fixed
-- point.
--
-- @since 0.4.1
rewriteOf :: Is k A_Setter => Optic k is a b a b -> (b -> Maybe a) -> a -> b
rewriteOf :: forall (k :: OpticKind) (is :: IxList) (a :: OpticKind)
       (b :: OpticKind).
Is k A_Setter =>
Optic k is a b a b -> (b -> Maybe a) -> a -> b
rewriteOf Optic k is a b a b
o b -> Maybe a
f = a -> b
go
  where
    go :: a -> b
go = forall (k :: OpticKind) (is :: IxList) (a :: OpticKind)
       (b :: OpticKind).
Is k A_Setter =>
Optic k is a b a b -> (b -> b) -> a -> b
transformOf Optic k is a b a b
o forall (a :: OpticKind) b. (a -> b) -> a -> b
$ \b
x -> forall (b :: OpticKind) (a :: OpticKind).
b -> (a -> b) -> Maybe a -> b
maybe b
x a -> b
go (b -> Maybe a
f b
x)
{-# INLINE rewriteOf #-}

-- | Transform every element by recursively applying a given 'Setter' in a
-- bottom-up manner.
--
-- @since 0.4.1
transformOf :: Is k A_Setter => Optic k is a b a b -> (b -> b) -> a -> b
transformOf :: forall (k :: OpticKind) (is :: IxList) (a :: OpticKind)
       (b :: OpticKind).
Is k A_Setter =>
Optic k is a b a b -> (b -> b) -> a -> b
transformOf Optic k is a b a b
o b -> b
f = a -> b
go
  where
    go :: a -> b
go = b -> b
f forall (b :: OpticKind) (c :: OpticKind) (a :: OpticKind).
(b -> c) -> (a -> b) -> a -> c
. forall (k :: OpticKind) (is :: IxList) (s :: OpticKind)
       (t :: OpticKind) (a :: OpticKind) (b :: OpticKind).
Is k A_Setter =>
Optic k is s t a b -> (a -> b) -> s -> t
over Optic k is a b a b
o a -> b
go
{-# INLINE transformOf #-}

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