packages feed

effectful-0.0.0.0: src/Effectful/State/Dynamic.hs

-- | The 'State' as an effect with dynamic dispatch.
--
-- It's not clear in which situation it's beneficial to use this instead of
-- "Effective.State" or "Effective.State.MVar" as you either:
--
-- - Share state between threads and need the synchonized version.
--
-- - Don't share state between threads and are free to use the faster, pure
--   version.
--
-- However, let's include this for now.
--
module Effectful.State.Dynamic
  ( State

  -- * Pure
  , runState
  , evalState
  , execState

  -- * MVar
  , runStateMVar
  , evalStateMVar
  , execStateMVar

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

import Control.Concurrent.MVar

import Effectful.Internal.Has
import Effectful.Internal.Monad

-- | Provide access to a mutable state of type @s@.
--
-- Whether the state is represented as a pure value or an 'MVar' depends on the
-- interpretation.
data State s = forall ref. State
  { _ref     :: ref
  , _get     :: forall es.   ref -> Eff es s
  , _put     :: forall es.   ref -> s -> Eff es ref
  , _state   :: forall es a. ref -> (s -> Eff es (a, s)) -> Eff es (a, ref)
  }

----------------------------------------
-- Pure

runState :: s -> Eff (State s : es) a -> Eff es (a, s)
runState s m = evalEffect (statePure s) $ (,) <$> m <*> get

evalState :: s -> Eff (State s : es) a -> Eff es a
evalState s = evalEffect (statePure s)

execState :: s -> Eff (State s : es) a -> Eff es s
execState s m = evalEffect (statePure s) $ m *> get

statePure :: s -> State s
statePure s0 = State
  { _ref     = s0
  , _get     = pure
  , _put     = \_ -> pure
  , _state   = \s f -> f s
  }

----------------------------------------
-- MVar

runStateMVar :: s -> Eff (State s : es) a -> Eff es (a, s)
runStateMVar s m = do
  v <- impureEff_ $ newMVar s
  evalEffect (stateMVar v) $ (,) <$> m <*> get

evalStateMVar :: s -> Eff (State s : es) a -> Eff es a
evalStateMVar s m = do
  v <- impureEff_ $ newMVar s
  evalEffect (stateMVar v) m

execStateMVar :: s -> Eff (State s : es) a -> Eff es s
execStateMVar s m = do
  v <- impureEff_ $ newMVar s
  evalEffect (stateMVar v) $ m *> get

stateMVar :: MVar s -> State s
stateMVar v0 = State
  { _ref     = v0
  , _get     = impureEff_ . readMVar
  , _put     = \v s -> impureEff_ . modifyMVar v $ \_ ->
      s `seq` pure (s, v)
  , _state   = \v f -> impureEff $ \es -> modifyMVar v $ \s0 -> do
      (a, s) <- unEff (f s0) es
      s `seq` pure (s, (a, v))
  }

----------------------------------------
-- Operations

get :: State s :> es => Eff es s
get = readerEffectM $ \State{..} -> _get _ref

put :: State s :> es => s -> Eff es ()
put s = stateEffectM $ \State{..} -> do
  ref <- _put _ref s
  pure ((), State { _ref = ref, .. })

state :: State s :> es => (s -> (a, s)) -> Eff es a
state f = stateEffectM $ \State{..} -> do
  (a, ref) <- _state _ref $ pure . f
  pure (a, State { _ref = ref, .. })

modify :: State s :> es => (s -> s) -> Eff es ()
modify f = state (\s -> ((), f s))

stateM :: State s :> es => (s -> Eff es (a, s)) -> Eff es a
stateM f = stateEffectM $ \State{..} -> do
  (a, ref) <- _state _ref f
  pure (a, State { _ref = ref, .. })

modifyM :: State s :> es => (s -> Eff es s) -> Eff es ()
modifyM f = stateM (\s -> ((), ) <$> f s)