Created
April 13, 2018 00:14
-
-
Save parsonsmatt/5cb35804adc70dd3445fc3de8c75b80f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Why use `mtl` constraints? | |
-- well, suppose you've got some tests using `MonadState`, but then that was a | |
-- mistake, because of the way that Alternative's IO works: | |
-- you had state rollback! Oh no! | |
-- | |
-- Fortunately, it is relatively easy to write a state instance that Just Works | |
-- with IO references, which preserves state across threads and exceptions. | |
-- And, with GeneralizedNewtypeDeriving, you get all the necessary instance | |
-- boilerplate for free. | |
-- | |
-- The only boring instance I have to write is the MonadState one. | |
-- | |
-- The only change I needed to make to the code was swapping `execStateT` for `execRefT`. | |
newtype RefT s m a | |
= RefT | |
{ unRefT :: ReaderT (IORef s) m a | |
} deriving | |
( Functor, Applicative, Monad, MonadIO, MonadTrans, Alternative, MonadCatch | |
, MonadThrow, MonadPlus | |
) | |
instance MonadIO m => MonadState s (RefT s m) where | |
get = RefT ask >>= readIORef | |
put x = RefT ask >>= (`writeIORef` x) | |
runRefT :: MonadIO m => RefT s m a -> s -> m (a, s) | |
runRefT act i = do | |
init <- newIORef i | |
a <- runReaderT (unRefT act) init | |
s <- readIORef init | |
pure (a, s) | |
execRefT :: MonadIO m => RefT s m a -> s -> m s | |
execRefT x = fmap snd . runRefT x |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment