in-other-words-0.1.0.0: src/Control/Effect/State.hs
{-# LANGUAGE BlockArguments #-}
module Control.Effect.State
( -- * Effect
State(..)
-- * Actions
, state
, state'
, get
, gets
, put
, modify
, modify'
-- * Interpretations
, runState
, evalState
, execState
, runStateLazy
, evalStateLazy
, execStateLazy
, stateToIO
, runStateIORef
-- * Simple variants of interpretations
, stateToIOSimple
, runStateIORefSimple
-- * Threading constraints
, StateThreads
, StateLazyThreads
-- * Carriers
, StateC
, StateLazyC
) where
import Data.IORef
import Data.Tuple
import Control.Effect
import Control.Effect.Internal.State
import qualified Control.Monad.Trans.State.Strict as SSt
import qualified Control.Monad.Trans.State.Lazy as LSt
state :: Eff (State s) m => (s -> (s, a)) -> m a
state f = do
(s, a) <- f <$> get
put s
return a
{-# INLINE state #-}
-- | A variant of 'state' that forces the resulting state (but not the return value)
state' :: Eff (State s) m => (s -> (s, a)) -> m a
state' f = do
(s, a) <- f <$> get
put $! s
return a
{-# INLINE state' #-}
get :: Eff (State s) m => m s
get = send Get
{-# INLINE get #-}
gets :: Eff (State s) m => (s -> a) -> m a
gets = (<$> get)
{-# INLINE gets #-}
put :: Eff (State s) m => s -> m ()
put = send . Put
{-# INLINE put #-}
modify :: Eff (State s) m => (s -> s) -> m ()
modify f = do
s <- get
put (f s)
-- | A variant of 'modify' that forces the resulting state.
modify' :: Eff (State s) m => (s -> s) -> m ()
modify' f = do
s <- get
put $! f s
-- | Runs a @'State' s@ effect by transforming it into non-atomic
-- operations over an 'IORef'.
--
-- This has a higher-rank type, as it makes use of 'InterpretReifiedC'.
-- __This makes 'runStateIORef' very difficult to use partially applied.__
-- __In particular, it can't be composed using @'.'@.__
--
-- If performance is secondary, consider using the slower
-- 'runStateIORefSimple', which doesn't have a higher-rank type.
runStateIORef :: forall s m a
. Eff (Embed IO) m
=> IORef s
-> InterpretReifiedC (State s) m a
-> m a
runStateIORef ref = interpret $ \case
Get -> embed $ readIORef ref
Put s -> embed $ writeIORef ref s
{-# INLINE runStateIORef #-}
-- | Runs a @'State' s@ effect by transforming it into non-atomic
-- operations in IO.
--
-- This has a higher-rank type, as it makes use of 'InterpretReifiedC'.
-- __This makes 'stateToIO' very difficult to use partially applied.__
-- __In particular, it can't be composed using @'.'@.__
--
-- If performance is secondary, consider using the slower
-- 'stateToIOSimple', which doesn't have a higher-rank type.
stateToIO :: forall s m a
. Eff (Embed IO) m
=> s
-> InterpretReifiedC (State s) m a
-> m (s, a)
stateToIO s main = do
ref <- embed $ newIORef s
a <- runStateIORef ref main
s' <- embed $ readIORef ref
return (s', a)
{-# INLINE stateToIO #-}
-- | Runs a @'State' s@ effect by transforming it into non-atomic
-- operations over an 'IORef'.
--
-- This is a less performant version of 'runStateIORef' that doesn't have
-- a higher-rank type, making it much easier to use partially applied.
runStateIORefSimple :: forall s m a p
. ( Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> IORef s
-> InterpretSimpleC (State s) m a
-> m a
runStateIORefSimple ref = interpretSimple $ \case
Get -> embed $ readIORef ref
Put s -> embed $ writeIORef ref s
{-# INLINE runStateIORefSimple #-}
-- | Runs a @'State' s@ effect by transforming it into non-atomic
-- operations in IO.
--
-- This is a less performant version of 'stateToIO' that doesn't have
-- a higher-rank type, making it much easier to use partially applied.
stateToIOSimple :: forall s m a p
. ( Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> s
-> InterpretSimpleC (State s) m a
-> m (s, a)
stateToIOSimple s main = do
ref <- embed $ newIORef s
a <- runStateIORefSimple ref main
s' <- embed $ readIORef ref
return (s', a)
{-# INLINE stateToIOSimple #-}
-- | Runs a @'State' s@ effect purely.
--
-- @'Derivs' ('StateC' s m) = 'State' s ': 'Derivs' m@
--
-- @'Control.Effect.Carrier.Prims' ('StateC' e m) = 'Control.Effect.Carrier.Prims' m@
runState :: forall s m a p
. ( Carrier m
, Threaders '[StateThreads] m p
)
=> s
-> StateC s m a
-> m (s, a)
runState sInit m = do
(a, sEnd) <- SSt.runStateT (unStateC m) sInit
return (sEnd, a)
{-# INLINE runState #-}
-- | Runs a @'State' s@ effect purely, discarding
-- the end state.
evalState :: forall s m a p
. ( Carrier m
, Threaders '[StateThreads] m p
)
=> s
-> StateC s m a
-> m a
evalState sInit m = do
(a, _) <- SSt.runStateT (unStateC m) sInit
return a
{-# INLINE evalState #-}
-- | Runs a @'State' s@ effect purely, discarding
-- the end result.
execState :: forall s m a p
. ( Carrier m
, Threaders '[StateThreads] m p
)
=> s
-> StateC s m a
-> m s
execState sInit m = do
(_, sEnd) <- SSt.runStateT (unStateC m) sInit
return sEnd
{-# INLINE execState #-}
-- | Runs a @'State' s@ effect purely and lazily.
--
-- @'Derivs' ('StateLazyC' s m) = 'State' s ': 'Derivs' m@
--
-- @'Control.Effect.Carrier.Prims' ('StateLazyC' e m) = 'Control.Effect.Carrier.Prims' m@
runStateLazy :: forall s m a p
. ( Carrier m
, Threaders '[StateLazyThreads] m p
)
=> s
-> StateLazyC s m a
-> m (s, a)
runStateLazy sInit m = swap <$> LSt.runStateT (unStateLazyC m) sInit
{-# INLINE runStateLazy #-}
-- | Runs a @'State' s@ effect purely and lazily,
-- discarding the final state.
evalStateLazy :: forall s m a p
. ( Carrier m
, Threaders '[StateLazyThreads] m p
)
=> s
-> StateLazyC s m a
-> m a
evalStateLazy sInit m = fst <$> LSt.runStateT (unStateLazyC m) sInit
{-# INLINE evalStateLazy #-}
-- | Runs a @'State' s@ effect purely and lazily,
-- discarding the end result.
execStateLazy :: forall s m a p
. ( Carrier m
, Threaders '[StateLazyThreads] m p
)
=> s
-> StateLazyC s m a
-> m s
execStateLazy sInit m = snd <$> LSt.runStateT (unStateLazyC m) sInit
{-# INLINE execStateLazy #-}