{-# LANGUAGE CPP #-}
module Control.Effect.AtomicState
(
AtomicState(..)
, atomicState
, atomicState'
, atomicGet
, atomicGets
, atomicModify
, atomicModify'
, atomicPut
, atomicStateToIO
, runAtomicStateIORef
, runAtomicStateTVar
, atomicStateToState
, atomicStateToIOSimple
, runAtomicStateIORefSimple
, runAtomicStateTVarSimple
, AtomicStateToStateC
) where
import Data.IORef
import Control.Concurrent.STM
import Control.Effect
import Control.Effect.State
#if MIN_VERSION_base(4,13,0)
import GHC.IORef (atomicModifyIORefP)
#else
data Box a = Box a
atomicModifyIORefP :: IORef s -> (s -> (s, a)) -> IO a
atomicModifyIORefP ref f = do
Box a <- atomicModifyIORef ref $ \s -> let !(s', a) = f s in (s', Box a)
return a
{-# INLINE atomicModifyIORefP #-}
# endif
data AtomicState s m a where
AtomicState :: (s -> (s, a)) -> AtomicState s m a
AtomicGet :: AtomicState s m s
atomicState :: Eff (AtomicState s) m => (s -> (s, a)) -> m a
atomicState = send . AtomicState
{-# INLINE atomicState #-}
atomicState' :: Eff (AtomicState s) m => (s -> (s, a)) -> m a
atomicState' f = atomicState $ \s -> let (!s', a) = f s in (s', a)
{-# INLINE atomicState' #-}
atomicGet :: Eff (AtomicState s) m => m s
atomicGet = send AtomicGet
{-# INLINE atomicGet #-}
atomicGets :: Eff (AtomicState s) m => (s -> a) -> m a
atomicGets = (<$> atomicGet)
{-# INLINE atomicGets #-}
atomicModify :: Eff (AtomicState s) m => (s -> s) -> m ()
atomicModify f = atomicState $ \s -> (f s, ())
{-# INLINE atomicModify #-}
atomicModify' :: Eff (AtomicState s) m => (s -> s) -> m ()
atomicModify' f = atomicState $ \s -> let !s' = f s in (s', ())
{-# INLINE atomicModify' #-}
atomicPut :: Eff (AtomicState s) m => s -> m ()
atomicPut s = atomicState $ \_ -> (s, ())
{-# INLINE atomicPut #-}
atomicStateToIO :: forall s m a
. Eff (Embed IO) m
=> s
-> InterpretReifiedC (AtomicState s) m a
-> m (s, a)
atomicStateToIO sInit main = do
ref <- embed $ newIORef sInit
a <- runAtomicStateIORef ref main
sEnd <- embed $ readIORef ref
return (sEnd, a)
{-# INLINE atomicStateToIO #-}
atomicStateToIOSimple :: forall s m a p
. ( Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> s
-> InterpretSimpleC (AtomicState s) m a
-> m (s, a)
atomicStateToIOSimple sInit main = do
ref <- embed $ newIORef sInit
a <- runAtomicStateIORefSimple ref main
sEnd <- embed $ readIORef ref
return (sEnd, a)
{-# INLINE atomicStateToIOSimple #-}
runAtomicStateIORef :: forall s m a
. Eff (Embed IO) m
=> IORef s
-> InterpretReifiedC (AtomicState s) m a
-> m a
runAtomicStateIORef ref = interpret $ \case
AtomicState f -> embed (atomicModifyIORefP ref f)
AtomicGet -> embed (readIORef ref)
{-# INLINE runAtomicStateIORef #-}
runAtomicStateIORefSimple :: forall s m a p
. ( Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> IORef s
-> InterpretSimpleC (AtomicState s) m a
-> m a
runAtomicStateIORefSimple ref = interpretSimple $ \case
AtomicState f -> embed (atomicModifyIORefP ref f)
AtomicGet -> embed (readIORef ref)
{-# INLINE runAtomicStateIORefSimple #-}
runAtomicStateTVar :: forall s m a
. Eff (Embed IO) m
=> TVar s
-> InterpretReifiedC (AtomicState s) m a
-> m a
runAtomicStateTVar tvar = interpret $ \case
AtomicState f -> embed $ atomically $ do
(s, a) <- f <$> readTVar tvar
writeTVar tvar s
return a
AtomicGet -> embed (readTVarIO tvar)
{-# INLINE runAtomicStateTVar #-}
runAtomicStateTVarSimple :: forall s m a p
. ( Eff (Embed IO) m
, Threaders '[ReaderThreads] m p
)
=> TVar s
-> InterpretSimpleC (AtomicState s) m a
-> m a
runAtomicStateTVarSimple tvar = interpretSimple $ \case
AtomicState f -> embed $ atomically $ do
(s, a) <- f <$> readTVar tvar
writeTVar tvar s
return a
AtomicGet -> embed (readTVarIO tvar)
{-# INLINE runAtomicStateTVarSimple #-}
data AtomicStateToStateH
type AtomicStateToStateC s = InterpretC AtomicStateToStateH (AtomicState s)
instance Eff (State s) m
=> Handler AtomicStateToStateH (AtomicState s) m where
effHandler = \case
AtomicState f -> state f
AtomicGet -> get
{-# INLINEABLE effHandler #-}
atomicStateToState :: Eff (State s) m
=> AtomicStateToStateC s m a
-> m a
atomicStateToState = interpretViaHandler
{-# INLINE atomicStateToState #-}