-- | Support for access to a mutable value of a particular type.
--
-- The value is thread local. If you want it to be shared between threads, use
-- "Effectful.State.Static.Shared".
--
-- /Note:/ unlike the 'Control.Monad.Trans.State.StateT' monad transformer from
-- the @transformers@ library, the 'State' effect doesn't discard state updates
-- when an exception is received:
--
-- >>> import qualified Control.Monad.Trans.State.Strict as S
--
-- >>> :{
--   (`S.execStateT` "Hi") . handle (\(_::ErrorCall) -> pure ()) $ do
--     S.modify (++ " there!")
--     error "oops"
-- :}
-- "Hi"
--
-- >>> :{
--   runEff . execState "Hi" . handle (\(_::ErrorCall) -> pure ()) $ do
--     modify (++ " there!")
--     error "oops"
-- :}
-- "Hi there!"
module Effectful.State.Static.Local
  ( -- * Effect
    State

    -- ** Handlers
  , runState
  , evalState
  , execState

    -- ** Operations
  , get
  , gets
  , put
  , state
  , modify
  , stateM
  , modifyM
  ) where

import Effectful
import Effectful.Dispatch.Static

-- | Provide access to a strict (WHNF), thread local, mutable value of type @s@.
data State s :: Effect

type instance DispatchOf (State s) = Static NoSideEffects
newtype instance StaticRep (State s) = State s

-- | Run the 'State' effect with the given initial state and return the final
-- value along with the final state.
runState
  :: s -- ^ The initial state.
  -> Eff (State s : es) a
  -> Eff es (a, s)
runState :: s -> Eff (State s : es) a -> Eff es (a, s)
runState s
s0 Eff (State s : es) a
m = do
  (a
a, State s) <- StaticRep (State s)
-> Eff (State s : es) a -> Eff es (a, StaticRep (State s))
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(DispatchOf e ~ 'Static sideEffects, MaybeIOE sideEffects es) =>
StaticRep e -> Eff (e : es) a -> Eff es (a, StaticRep e)
runStaticRep (s -> StaticRep (State s)
forall s. s -> StaticRep (State s)
State s
s0) Eff (State s : es) a
m
  (a, s) -> Eff es (a, s)
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (a
a, s
s)

-- | Run the 'State' effect with the given initial state and return the final
-- value, discarding the final state.
evalState
  :: s -- ^ The initial state.
  -> Eff (State s : es) a
  -> Eff es a
evalState :: s -> Eff (State s : es) a -> Eff es a
evalState s
s = StaticRep (State s) -> Eff (State s : es) a -> Eff es a
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(DispatchOf e ~ 'Static sideEffects, MaybeIOE sideEffects es) =>
StaticRep e -> Eff (e : es) a -> Eff es a
evalStaticRep (s -> StaticRep (State s)
forall s. s -> StaticRep (State s)
State s
s)

-- | Run the 'State' effect with the given initial state and return the final
-- state, discarding the final value.
execState
  :: s -- ^ The initial state.
  -> Eff (State s : es) a
  -> Eff es s
execState :: s -> Eff (State s : es) a -> Eff es s
execState s
s0 Eff (State s : es) a
m = do
  State s <- StaticRep (State s)
-> Eff (State s : es) a -> Eff es (StaticRep (State s))
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(DispatchOf e ~ 'Static sideEffects, MaybeIOE sideEffects es) =>
StaticRep e -> Eff (e : es) a -> Eff es (StaticRep e)
execStaticRep (s -> StaticRep (State s)
forall s. s -> StaticRep (State s)
State s
s0) Eff (State s : es) a
m
  s -> Eff es s
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure s
s

-- | Fetch the current value of the state.
get :: State s :> es => Eff es s
get :: Eff es s
get = do
  State s <- Eff es (StaticRep (State s))
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]).
(DispatchOf e ~ 'Static sideEffects, e :> es) =>
Eff es (StaticRep e)
getStaticRep
  s -> Eff es s
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure s
s

-- | Get a function of the current state.
--
-- @'gets' f ≡ f '<$>' 'get'@
gets
  :: State s :> es
  => (s -> a) -- ^ The function to apply to the state.
  -> Eff es a
gets :: (s -> a) -> Eff es a
gets s -> a
f = s -> a
f (s -> a) -> Eff es s -> Eff es a
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff es s
forall s (es :: [(Type -> Type) -> Type -> Type]).
(State s :> es) =>
Eff es s
get

-- | Set the current state to the given value.
put :: State s :> es => s -> Eff es ()
put :: s -> Eff es ()
put s
s = StaticRep (State s) -> Eff es ()
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]).
(DispatchOf e ~ 'Static sideEffects, e :> es) =>
StaticRep e -> Eff es ()
putStaticRep (s -> StaticRep (State s)
forall s. s -> StaticRep (State s)
State s
s)

-- | Apply the function to the current state and return a value.
state
  :: State s :> es
  => (s -> (a, s)) -- ^ The function to modify the state.
  -> Eff es a
state :: (s -> (a, s)) -> Eff es a
state s -> (a, s)
f = (StaticRep (State s) -> (a, StaticRep (State s))) -> Eff es a
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(DispatchOf e ~ 'Static sideEffects, e :> es) =>
(StaticRep e -> (a, StaticRep e)) -> Eff es a
stateStaticRep ((StaticRep (State s) -> (a, StaticRep (State s))) -> Eff es a)
-> (StaticRep (State s) -> (a, StaticRep (State s))) -> Eff es a
forall a b. (a -> b) -> a -> b
$ \(State s0) -> let (a
a, s
s) = s -> (a, s)
f s
s0 in (a
a, s -> StaticRep (State s)
forall s. s -> StaticRep (State s)
State s
s)

-- | Apply the function to the current state.
--
-- @'modify' f ≡ 'state' (\\s -> ((), f s))@
modify
  :: State s :> es
  => (s -> s) -- ^ The function to modify the state.
  -> Eff es ()
modify :: (s -> s) -> Eff es ()
modify s -> s
f = (s -> ((), s)) -> Eff es ()
forall s (es :: [(Type -> Type) -> Type -> Type]) a.
(State s :> es) =>
(s -> (a, s)) -> Eff es a
state ((s -> ((), s)) -> Eff es ()) -> (s -> ((), s)) -> Eff es ()
forall a b. (a -> b) -> a -> b
$ \s
s -> ((), s -> s
f s
s)

-- | Apply the monadic function to the current state and return a value.
stateM
  :: State s :> es
  => (s -> Eff es (a, s)) -- ^ The function to modify the state.
  -> Eff es a
stateM :: (s -> Eff es (a, s)) -> Eff es a
stateM s -> Eff es (a, s)
f = (StaticRep (State s) -> Eff es (a, StaticRep (State s)))
-> Eff es a
forall (e :: (Type -> Type) -> Type -> Type)
       (sideEffects :: SideEffects)
       (es :: [(Type -> Type) -> Type -> Type]) a.
(DispatchOf e ~ 'Static sideEffects, e :> es) =>
(StaticRep e -> Eff es (a, StaticRep e)) -> Eff es a
stateStaticRepM ((StaticRep (State s) -> Eff es (a, StaticRep (State s)))
 -> Eff es a)
-> (StaticRep (State s) -> Eff es (a, StaticRep (State s)))
-> Eff es a
forall a b. (a -> b) -> a -> b
$ \(State s0) -> do
  (a
a, s
s) <- s -> Eff es (a, s)
f s
s0
  (a, StaticRep (State s)) -> Eff es (a, StaticRep (State s))
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (a
a, s -> StaticRep (State s)
forall s. s -> StaticRep (State s)
State s
s)

-- | Apply the monadic function to the current state.
--
-- @'modifyM' f ≡ 'stateM' (\\s -> ((), ) '<$>' f s)@
modifyM
  :: State s :> es
  => (s -> Eff es s) -- ^ The monadic function to modify the state.
  -> Eff es ()
modifyM :: (s -> Eff es s) -> Eff es ()
modifyM s -> Eff es s
f = (s -> Eff es ((), s)) -> Eff es ()
forall s (es :: [(Type -> Type) -> Type -> Type]) a.
(State s :> es) =>
(s -> Eff es (a, s)) -> Eff es a
stateM (\s
s -> ((), ) (s -> ((), s)) -> Eff es s -> Eff es ((), s)
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> s -> Eff es s
f s
s)

-- $setup
-- >>> import Control.Exception (ErrorCall)
-- >>> import Control.Monad.Catch