State and error monad stack with state rollback on error - haskell

I have a problem that I will illustrate through the following example:
Let's say I want to do some computations that can yield a result or an error, while carrying a state. For that, I have the following monad stack:
import Control.Monad.Trans.State ( get, modify, State )
import Control.Monad.Trans.Except ( catchE, throwE, ExceptT )
type MyMonad a = ExceptT String (State [Int]) a
So, the state is a list of ints, errors are strings and computations can yield a value of any type "a". I can do things like:
putNumber :: Int -> MyMonad ()
putNumber i = lift $ modify (i:)
Now, suppose I defined a function that adds the half of the last number to the state:
putHalf :: MyMonad ()
putHalf = do
s <- lift get
case s of
(x:_) -> if even x then putNumber (div x 2) else throwE "Number can't be halved"
[] -> throwE "The state is empty"
Using putHalf will either add a number to the state and return void, or yield any of the two errors.
If an error occurs, I would like to be able to call an alternative function. I know I can achieve this with catchE by doing something like this:
putWithAlternative :: MyMonad ()
putWithAlternative = putHalf `catchE` (\_ -> putNumber 12)
In this case, if putHalf fails for any reason, the number 12 will be added to the state. Up to this point everything is fine. However, I could define a function that called putHalf twice:
putHalfTwice :: MyMonad ()
putHalfTwice = putHalf >> putHalf
The problem is that if, for example, the state contained only number 2, the first call to putHalf would succeed and modify the state, but the second one would fail. I need putHalfTwice to do both calls and modify the state twice, or none at all and leave the state as it is. I can't use catchE or putWithAlternative, because the state is still modified in the first call.
I know the Parsec library does this through its <|> and try operators. How could I go about defining these myself? Is there any already defined monad transformer that could achieve this?

If, in your problem domain, failure should never modify the state, the most straightforward thing to do is to invert the layers:
type MyMonad' a = StateT [Int] (Except String) a
Your original monad is isomorphic to:
s -> (Either e a, s)
so it always returns a new state, whether it succeeds or fails. This new monad is isomorphic to:
s -> Either e (a, s)
so it either fails or returns a new state.
The following program recovers from putHalfTwice without mangling the state:
import Control.Monad.Trans
import Control.Monad.Trans.State
import Control.Monad.Trans.Except
type MyMonad' a = StateT [Int] (Except String) a
putNumber :: Int -> MyMonad' ()
putNumber i = modify (i:)
putHalf :: MyMonad' ()
putHalf = do
s <- get
case s of
(x:_) -> if even x then putNumber (div x 2) else lift $ throwE "Number can't be halved"
[] -> lift $ throwE "the state is empty"
putHalfTwice :: MyMonad' ()
putHalfTwice = putHalf >> putHalf
foo :: MyMonad' ()
foo = liftCatch catchE putHalfTwice (\_ -> putNumber 12)
main :: IO ()
main = do
print $ runExcept (runStateT foo [2])
Otherwise, if you want backtracking to be optional, then you can write your own try that catches, restores the state, and rethrows:
try :: MyMonad a -> MyMonad a
try act = do
s <- lift get
act `catchE` (\e -> lift (put s) >> throwE e)
and then:
import Control.Monad.Trans
import Control.Monad.Trans.State
import Control.Monad.Trans.Except
type MyMonad a = ExceptT String (State [Int]) a
putNumber :: Int -> MyMonad ()
putNumber i = lift $ modify (i:)
putHalf :: MyMonad ()
putHalf = do
s <- lift get
case s of
(x:_) -> if even x then putNumber (div x 2) else throwE "Number can't be halved"
[] -> throwE "The state is empty"
putHalfTwice :: MyMonad ()
putHalfTwice = putHalf >> putHalf
try :: MyMonad a -> MyMonad a
try act = do
s <- lift get
act `catchE` (\e -> lift (put s) >> throwE e)
foo :: MyMonad ()
foo = putHalfTwice `catchE` (\_ -> putNumber 12)
bar :: MyMonad ()
bar = try putHalfTwice `catchE` (\_ -> putNumber 12)
main :: IO ()
main = do
print $ runState (runExceptT foo) [2]
print $ runState (runExceptT bar) [2]

Related

How to refactor code handling IO (Maybe a) to use monad transformer?

I'm trying to understand monad transformers. I have a code like this (that doesn't work):
import System.IO (hFlush, stdout)
import Text.Read (readMaybe)
add1 :: Int -> IO (Maybe Int)
add1 x = return $ Just (x + 1)
readNumber :: IO (Maybe Int)
readNumber = do
putStr "Say a number: "
hFlush stdout
inp <- getLine
return $ (readMaybe inp :: Maybe Int)
main :: IO ()
main = do
x <- readNumber >>= add1
print x
It throws
Couldn't match type ‘Int’ with ‘Maybe Int’
Expected: Maybe Int -> IO (Maybe Int)
Actual: Int -> IO (Maybe Int)
I figured out that I can make it work by introducing
(>>>=) :: IO (Maybe a) -> (a -> IO (Maybe b)) -> IO (Maybe b)
x >>>= f =
x >>= go f
where
go _ Nothing = return Nothing
go f (Just x) = f x
and using it instead of >>=. This is strikingly similar to a monad transformer, but I can't get my head around how exactly I should refactor this code to use it.
You may wonder "why does add1 return IO?" Let's say that it can be something more complicated that uses IO.
I'm looking to understand it better, so answers like "there is a better solution" or "it is already implemented in..." won't help. I would like to learn what I would need to change to make it work with >>= assuming that I want to do operations like IO (Maybe a) -> (a -> IO (Maybe b)) -> IO (Maybe b) that already work with my >>>=.
I'd say the most common way to use monad transformers is the mtl approach. That consists of using type classes like MonadIO and MonadFail to implement your programs and then in your main function use concrete transformers like MaybeT to instantiate the type classes and get the actual result.
For your program that can look like this:
import System.IO (hFlush, stdout)
import Text.Read (readMaybe)
import Control.Monad.Trans.Maybe (runMaybeT)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad (MonadFail (fail))
add1 :: Monad m => Int -> m Int
add1 x = pure (x + 1)
prompt :: String -> IO String
prompt x = do
putStr x
hFlush stdout
getLine
readNumber :: (MonadIO m, MonadFail m) => m Int
readNumber = do
inp <- liftIO (prompt "Say a number: ")
case readMaybe inp of
Nothing -> fail "Not a number"
Just x -> pure x
main :: IO ()
main = do
x <- runMaybeT (readNumber >>= add1)
print x

How to preserve the state of the monad stack in the IO exception handler?

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?
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.

Strip off layer from MonadResource

I'm playing around with the leveldb bindings.
I'm wondering if it's possible to take a function like
MonadResource m => a -> m b
And convert it to
MonadResource m => m (a -> IO b))
It can definitely be done, but it's dangerous. Let's demonstrate first the how, by extracting the internal state of the ResourceT:
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import Control.Monad.Trans.Resource.Internal
data Foo = Foo Int
deriving Show
getFoo :: MonadResource m => Int -> m Foo
getFoo i = fmap snd $ allocate
(do
putStrLn $ "allocating Foo with " ++ show i
return $ Foo i)
(\(Foo x) -> putStrLn $ "Freeing Foo " ++ show x)
stripLayer :: MonadResource m => (a -> ResourceT IO b) -> m (a -> IO b)
stripLayer f = do
is <- liftResourceT getInternalState
return $ \a -> runInternalState (f a) is
main :: IO ()
main = do
getFoo' <- runResourceT $ stripLayer $ getFoo
getFoo' 42 >>= print
Unfortunately the output from this isn't what we'd hope for:
allocating Foo with 42
Foo 42
Notice how the "Freeing" line is never called. This is because, by the time we use getFoo', the runResourceT call has already exited, which is how we guarantee that all resources are freed. You can safely get away with this trick if you're disciplined and make sure everything lives inside the runResourceT call, but the type system won't help you. To see what this will look like:
main :: IO ()
main = runResourceT $ do
getFoo' <- stripLayer $ getFoo
liftIO $ getFoo' 42 >>= print

Types in MaybeT computation

Working in an IO computation I ended up with a staircase of case mbValue of …s and figured out that I should use the Maybe monad to simplify the code. Since it's within an IO computation and I need to get IO values, I used the MaybeT monad transformer so that I can lift IO computation into Maybe.
Now, I have always thought about values being “stripped” of their Maybeness after an values <- mbValue inside a Maybe computation, but this turns out to be too simple of a heuristic here.
As highlighted below, when using a Maybe a value as an a (here by passing it to read), it fails to type check:
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT)
lol :: IO (Maybe Int)
lol = return (Just 3)
lal :: IO (Maybe String)
lal = return (Just "8")
foo :: IO (Maybe Bool)
foo = do
b <- runMaybeT $ do
x <- lift lol
y <- lift lal
return (x < (read y))
return b ^-- Couldn't match type ‘Maybe String’ with ‘String’
main = foo >>= print
If I put a typed hole in for return (x < (read y)), I see that it expects a Bool, which makes sense, but also that the current bindings include
|| y :: Data.Maybe.Maybe GHC.Base.String
|| (bound at /private/tmp/test.hs:14:5)
|| x :: Data.Maybe.Maybe GHC.Types.Int
|| (bound at /private/tmp/test.hs:13:5)
I.e., y is a Maybe String. This of course explains the error, but I'm left confused. Where is my understanding wrong, and how can I fix this error?
In short: Replace lift by the MaybeT constructor.
Note that
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
and
lift :: (MonadTrans t, Monad m) => m a -> t m a
Your use of lift in
x <- lift lol
is at the type
lift :: IO (Maybe Int) -> MaybeT IO (Maybe Int)
That's why x will be a Maybe Int again. The lift adds a fresh MaybeT layer that is independent of the Maybe occurrence you already have.
But
MaybeT :: m (Maybe a) -> MaybeT m a
instead as in
x <- MaybeT lol
will be used at type
MaybeT :: IO (Maybe a) -> MaybeT IO a
and do the right thing.
When specialized to MaybeT, lift :: Monad m => m a -> MaybeT m a. Since lol :: IO (Maybe Int), m is IO and a is Maybe Int, therefore lift lol :: MaybeT IO (Maybe Int).
IO (Maybe a) is just the value contained within a MaybeT IO a newtype wrapper, so there's no need to lift it; instead use the MaybeT constructor, for example as in MaybeT lol.
But this is not how people tend to use monad transformers. Instead, just use MaybeT values and lift as needed:
import Control.Monad
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (runMaybeT, MaybeT)
lol :: MaybeT IO Int
lol = return 3
lal :: MaybeT IO String
lal = return "8"
foo :: IO (Maybe Bool)
foo =
runMaybeT $ do
x <- lol
y <- lal
_ <- lift getLine -- lift (IO String) to MaybeT IO String
_ <- return 100 -- lift any pure value
_ <- mzero -- use the MonadPlus instance to get a lifted Nothing.
return (x < (read y))
main = foo >>= print

Monads in monad transformer context

I have trouble gripping to monads and monad transformers. I have the
following contrived example (not compilable):
import Control.Monad
import Control.Monad.Error
import Control.Monad.Reader
data State = State Int Int Int
type Foo = ReaderT State IO
readEither :: String -> Either String Int
readEither s = let p = reads s
in case p of
[] -> throwError "Could not parse"
[(a, _)] -> return a
readEitherT :: IO (Either String Int)
readEitherT = let p s = reads s
in runErrorT $ do
l <- liftIO (getLine)
readEither l
foo :: Foo Int
foo = do
d <- liftIO $ readEitherT
case d of
Right dd -> return dd
Left em -> do
liftIO $ putStrLn em
return (-1)
bar :: Foo String
bar = do
liftIO $ getLine
defaultS = State 0 0 0
If I copy the functionality of readEither to readEitherT, it works, but I
have a nagging feeling that I can leverage the power of the existing
readEither function, but I can't figure out how. If I try to lift the
readEither in the readEitherT function, it lifts it to ErrorT String IO
(Either String Int) as it should. But I should somehow get it to ErrorT
String IO Int.
If I'm going to the wrong direction with this, what is the correct way to
handle errors which require IO (or other monads) and are to be called from
monadic context (see the foo function in the example)
Edit:
Apparently it was not clear what I was trying to do. Maybe the following function describes what and why I was wondering
maybePulseQuit :: Handle -> IO (Either String ())
maybePulseQuit h = runErrorT $ do
f <- liftIO $ (communicate h "finished" :: IO (Either String Bool))
(ErrorT . pure) f >>= \b → liftIO $ when b $ liftIO pulseQuit
This works, but is still ugly because of the binds. This is a lot clearer than the previous version which had case checking. Is this the recommended way to do this?
It is not clear why you need ErrorT. You can implement readEitherT like
readEitherT :: IO (Either String Int)
readEitherT = fmap readEither getLine
If you really need ErrorT for some reason, then you can create utility function eitherToErrorT:
eitherToErrorT = ErrorT . pure
readEitherT = runErrorT $ do
l <- liftIO $ getLine
eitherToErrorT $ readEither l
[ADD]
Maybe you just want to add ErrorT into your monad stack...
data State = State Int Int Int
type Foo = ErrorT String (ReaderT State IO)
runFoo :: Foo a -> State -> IO (Either String a)
runFoo foo s = runReaderT (runErrorT foo) s
doIt :: Int -> Foo Int
doIt i = if i < 0
then throwError "i < 0"
else return (i * 2)
Example:
*Main> runFoo (doIt 1 >>= doIt) (State 0 0 0)
Right 4
*Main> runFoo (doIt (-1) >>= doIt) (State 0 0 0)
Left "i < 0"

Resources