haskell-halogen-core-0.9.0: src/Control/Monad/State/Delayed/Trans.hs
{-# LANGUAGE DerivingVia #-}
module Control.Monad.State.Delayed.Trans where
import Control.Monad.State.Delayed.Class
import Control.Monad.State.Delayed.Delayer
import Control.Monad.Trans
import Data.Foreign (unsafeRefEq)
import HPrelude
type DelayedStateT :: Type -> (Type -> Type) -> Type -> Type
newtype DelayedStateT s m a = DelayedStateT (Delayer s -> m a)
deriving (Functor, Applicative, Monad, MonadIO, MonadUnliftIO) via ReaderT (Delayer s) m
runDelayedStateT :: (Monad m) => Delayer s -> DelayedStateT s m a -> m a
runDelayedStateT d (DelayedStateT f) = f d
instance MonadTrans (DelayedStateT s) where
lift = DelayedStateT . const
instance (MonadUnliftIO m, MonadState s m) => MonadState s (DelayedStateT s m) where
state f = DelayedStateT $ \(Delayer timeout var) -> do
let handleStateChange newState = do
newDelState <- withRunInIO
$ \runInIO -> mkTimedOutDelayerState timeout newState
$ \s -> modifyMVar var $ const $ runInIO (put s) $> (Nothing, ())
pure (Just newDelState)
modifyMVar var $ \ds'm -> case ds'm of
-- no previous suspended state, check if f changes the state
-- if it does, create a new suspended state
Nothing -> do
baseState <- get
let (a, newState) = f baseState
if unsafeRefEq baseState newState
then pure (Nothing, a)
else do
nvar <- handleStateChange newState
pure (nvar, a)
-- there is a previous suspended state
-- check if f changes the state, if it does, create a new suspended state and cancel the previous one
Just (DelayerState delayedState fiber) -> do
let (a, newState) = f delayedState
if unsafeRefEq delayedState newState
then pure (ds'm, a)
else do
liftIO $ cancelWith fiber TimeoutKilled
nvar <- handleStateChange newState
pure (nvar, a)
instance (MonadUnliftIO m, MonadState s m) => MonadDelayedState s (DelayedStateT s m) where
commit = DelayedStateT $ \(Delayer _ var) -> withMVar var $ \case
Nothing -> pass
Just (DelayerState s fiber) -> do
liftIO $ cancelWith fiber TimeoutKilled
put s