Consider the following program.
import Control.Monad.State
import Control.Monad.Catch
ex1 :: StateT Int IO ()
ex1 = do
modify (+10)
liftIO . ioError $ userError "something went wrong"
ex2 :: StateT Int IO ()
ex2 = do
x <- get
liftIO $ print x
ex3 :: StateT Int IO ()
ex3 = ex1 `onException` ex2
main :: IO ()
main = evalStateT ex3 0
When we run the program we get the following output.
$ runhaskell Test.hs
0
Test.hs: user error (something went wrong)
However, I expected the output to be as follows.
$ runhaskell Test.hs
10
Test.hs: user error (something went wrong)
How do I preserve the intermediate state in ex1
in the exception handler ex2
?
The Monad instance declaration for State looks something like this: instance Monad (State s) where return x = state (\st -> (x, st)) act >>= k = state $ \st -> let (x, st') = runState act st in runState (k x) st' Notice that State s is not a type but a type constructor: it needs one more type variable to become a type.
State monad code looks as if the state were a global mutable variable. You access it using get with no arguments, and you modify it by calling put that returns no value. So what have we gained in comparison to C? We might not see the hidden effects, but the compiler does. It desugars every do block and type-checks it.
Monadic bind makes sure that the state is threaded from function to function. It's never shared. If you make your Haskell code concurrent, there will be no data races. Ex 1. Define the reader monad. It's supposed to model computations that have access to some read-only environment.
How to solve java.io.IOException IOException is a Java exception that occurs when an IO operation fails. Develop can explicitly handle the exception in a try-catch-finally block and print out the root cause of the failure. The developer can take the correct actions to solve this situation by having additional code in the catch and finally blocks.
Use an IORef
(or MVar
or TVar
or whatever) instead.
newtype IOStateT s m a = IOStateT { unIOStateT :: ReaderT (IORef s) m a }
deriving (Functor, Applicative, Monad, MonadTrans, MonadIO)
-- N.B. not MonadReader! you want that instance to pass through,
-- unlike ReaderT's instance, so you have to write the instance
-- by hand
runIOStateT :: IOStateT s m a -> IORef s -> m a
runIOStateT = runReaderT . unIOStateT -- or runIOStateT = coerce if you're feeling cheeky
instance MonadIO m => MonadState s (IOStateT s m) where
state f = IOStateT $ do
ref <- ask
liftIO $ do
s <- readIORef ref
let (a, s') = f s
writeIORef ref s'
pure a
This feels like a pattern I've seen enough times that there ought to be a Hackage package for it, but I don't know of one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With