-- |
-- Module: Optics.State.Operators
-- Description: Infix operators for state-modifying optics.
--
-- Defines infix operators for the operations in "Optics.State".  These
-- operators are not exposed by the main @Optics@ module, but must be imported
-- explicitly.
--
module Optics.State.Operators (
  -- * State modifying optics
  (.=), (?=), (%=),
  -- * State modifying optics with passthrough
  (%%=),
  -- * Returning new value
  (<.=), (<?=), (<%=),
  -- * Returning old value
  (<<.=), (<<?=), (<<%=),
  -- * Passthrough
  PermeableOptic (..),
  ) where

import Control.Monad.State (MonadState)
import qualified Control.Monad.State as State

import Optics.Core
import Optics.Passthrough
import Optics.State
import Optics.View

infix 4 .=, ?=, %=

-- | Replace the target(s) of an 'Optic' in our monadic state with a new value,
-- irrespective of the old.
--
-- This is an infix version of 'assign'.
(.=)
  :: (Is k A_Setter, MonadState s m)
  => Optic k is s s a b
  -> b
  -> m ()
(.=) = assign
{-# INLINE (.=) #-}

-- | Replace the target(s) of an 'Optic' in our monadic state with 'Just' a new
-- value, irrespective of the old.
(?=)
  :: (Is k A_Setter, MonadState s m)
  => Optic k is s s (Maybe a) (Maybe b)
  -> b
  -> m ()
(?=) = \o -> assign o . Just
{-# INLINE (?=) #-}

-- | Map over the target(s) of an 'Optic' in our monadic state.
--
-- This is an infix version of 'modifying'.
(%=)
  :: (Is k A_Setter, MonadState s m)
  => Optic k is s s a b
  -> (a -> b)
  -> m ()
(%=) = modifying
{-# INLINE (%=) #-}

-------------------------------------------------------------------------------
-- Extra stuff
-------------------------------------------------------------------------------

-- | Modify the target of an 'PermeableOptic' in the current state returning
-- some extra information of type depending on the optic (@r@, @Maybe r@ or
-- monoidal summary).
infix 4 %%=
(%%=)
  :: (PermeableOptic k r, MonadState s m)
  => Optic k is s s a b
  -> (a -> (r, b))
  -> m (ViewResult k r)
o %%= f = State.state (passthrough o f)
{-# INLINE (%%=) #-}

-------------------------------------------------------------------------------
-- Returning new value
-------------------------------------------------------------------------------

infix 4 <.=, <?=, <%=

-- | Modify the target of a 'PermeableOptic' into your 'Monad''s state by a user
-- supplied function and return the result.
(<%=)
  :: (PermeableOptic k b, MonadState s m)
  => Optic k is s s a b
  -> (a -> b)
  -> m (ViewResult k b)
o <%= f = o %%= \a -> let b = f a in (b, b)
{-# INLINE (<%=) #-}

-- | Set 'Just' a value with pass-through.
--
-- This is useful for chaining assignment without round-tripping through your
-- 'Monad' stack.
(<?=)
  :: (PermeableOptic k (Maybe b), MonadState s m)
  => Optic k is s s (Maybe a) (Maybe b)
  -> b
  -> m (ViewResult k (Maybe b))
o <?= b = o <.= Just b
{-# INLINE (<?=) #-}

-- | Set with pass-through.
--
-- This is useful for chaining assignment without round-tripping through your
-- 'Monad' stack.
(<.=)
  :: (PermeableOptic k b, MonadState s m)
  => Optic k is s s a b
  -> b
  -> m (ViewResult k b)
o <.= b = o <%= const b
{-# INLINE (<.=) #-}

-------------------------------------------------------------------------------
-- Returning old value
-------------------------------------------------------------------------------

infix 4 <<.=, <<?=, <<%=

-- | Modify the target of a 'PermeableOptic' into your 'Monad''s state by a user
-- supplied function and return the /old/ value that was replaced.
(<<%=)
  :: (PermeableOptic k a, MonadState s m)
  => Optic k is s s a b
  -> (a -> b)
  -> m (ViewResult k a)
o <<%= f = o %%= \a -> (a, f a)
{-# INLINE (<<%=) #-}

-- | Replace the target of a 'PermeableOptic' into your 'Monad''s state with
-- 'Just' a user supplied value and return the /old/ value that was replaced.
(<<?=)
  :: (PermeableOptic k (Maybe a), MonadState s m)
  => Optic k is s s (Maybe a) (Maybe b)
  -> b
  -> m (ViewResult k (Maybe a))
o <<?= b = o <<.= Just b
{-# INLINE (<<?=) #-}

-- | Replace the target of a 'PermeableOptic' into your 'Monad''s state with a
-- user supplied value and return the /old/ value that was replaced.
(<<.=)
  :: (PermeableOptic k a, MonadState s m)
  => Optic k is s s a b
  -> b
  -> m (ViewResult k a)
o <<.= b = o <<%= const b
{-# INLINE (<<.=) #-}
