{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TemplateHaskell #-}
module Polysemy.AtomicState
(
AtomicState (..)
, atomicState
, atomicState'
, atomicGet
, atomicGets
, atomicPut
, atomicModify
, atomicModify'
, runAtomicStateIORef
, runAtomicStateTVar
, atomicStateToIO
, atomicStateToState
) where
import Control.Concurrent.STM
import Polysemy
import Polysemy.State
import Data.IORef
data AtomicState s m a where
AtomicState :: (s -> (s, a)) -> AtomicState s m a
AtomicGet :: AtomicState s m s
makeSem_ ''AtomicState
atomicState :: forall s a r
. Member (AtomicState s) r
=> (s -> (s, a))
-> Sem r a
atomicGet :: forall s r
. Member (AtomicState s) r
=> Sem r s
atomicGets :: forall s s' r
. Member (AtomicState s) r
=> (s -> s')
-> Sem r s'
atomicGets = (<$> atomicGet)
{-# INLINE atomicGets #-}
atomicState' :: forall s a r
. Member (AtomicState s) r
=> (s -> (s, a))
-> Sem r a
atomicState' f = do
!a <- atomicState $ \s ->
case f s of
v@(!_, _) -> v
return a
{-# INLINE atomicState' #-}
atomicPut :: Member (AtomicState s) r
=> s
-> Sem r ()
atomicPut s = do
!_ <- atomicState $ \_ -> (s, ())
return ()
{-# INLINE atomicPut #-}
atomicModify :: Member (AtomicState s) r
=> (s -> s)
-> Sem r ()
atomicModify f = atomicState $ \s -> (f s, ())
{-# INLINE atomicModify #-}
atomicModify' :: Member (AtomicState s) r
=> (s -> s)
-> Sem r ()
atomicModify' f = do
!_ <- atomicState $ \s -> let !s' = f s in (s', ())
return ()
{-# INLINE atomicModify' #-}
runAtomicStateIORef :: forall s r a
. Member (Embed IO) r
=> IORef s
-> Sem (AtomicState s ': r) a
-> Sem r a
runAtomicStateIORef ref = interpret $ \case
AtomicState f -> embed $ atomicModifyIORef ref f
AtomicGet -> embed $ readIORef ref
{-# INLINE runAtomicStateIORef #-}
runAtomicStateTVar :: Member (Embed IO) r
=> TVar s
-> Sem (AtomicState s ': r) a
-> Sem r 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 #-}
atomicStateToIO :: forall s r a
. Member (Embed IO) r
=> s
-> Sem (AtomicState s ': r) a
-> Sem r (s, a)
atomicStateToIO s sem = do
ref <- embed $ newIORef s
res <- runAtomicStateIORef ref sem
end <- embed $ readIORef ref
return (end, res)
{-# INLINE atomicStateToIO #-}
atomicStateToState :: Member (State s) r
=> Sem (AtomicState s ': r) a
-> Sem r a
atomicStateToState = interpret $ \case
AtomicState f -> do
(s', a) <- f <$> get
put s'
return a
AtomicGet -> get
{-# INLINE atomicStateToState #-}