Dealing with IO exceptions inside the MonadError - haskell

I'm writing a function that queries a REST end-point to fetch some data, say queryServer:
queryServer :: (MonadIO m, MonadError Message m) => URL -> m String
The reason why I want to use the MonadError from Control.Monad.Except is that I want to have the short-circuiting behavior of this monad. So for instance it can be used as follows:
queryServers :: (MonadIO m, MonadError Message m) => m (String, String)
queryServers = do
res0 <- queryServer "server0"
res1 <- queryServer "server1"
return (res0, res1)
In this way, I do not have to worry about catching exceptions originated at queryServer and the function fails as soon as an error is encountered.
The problem I have with this approach (and there might be others I'm unaware of) is that I have to use some boilerplate to catch the IO exceptions and re-throw them with throwError:
queryServer url = do
result <- liftIO $ try (fetchSomething url)
case result of
Left (e :: IOException) -> throwError "error when fetching"
Right sth -> return sth
fetchSomething :: URL -> IO String
fetchSomething = undefined
I've seen several questions regarding the proper use of exceptions in Haskell, like this one, however none of them deals with the MonadError.
What is the right way to dealing with IO exceptions within the `MonadError?

Related

How to add Mask to a monadic stack

I am trying to use the bracket function from Exception.Safe Package which has a return type of
forall m a b c. MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c
This implies that my monad stack must have the Mask monad added to the stack ?
My calling function looks like this
sendMessage :: String -> Config.KafkaP (Either KafkaError ())
sendMessage msg=do
getProperties <- producerProps
let
mkProducer = newProducer getProperties
clProducer (Left _) = return ()
clProducer (Right prod) = closeProducer prod
runHandler (Left err) = return $ Left err
runHandler (Right prod) = messageSender prod msg
res <- bracket mkProducer clProducer runHandler
return res
And config.KakfaP has the type
ReaderT ProducerConfig IO a
And the error I get is ,
No instance for (exceptions-0.10.0:Control.Monad.Catch.MonadMask
Config.KafkaP)
arising from a use of ‘bracket’
Does this mean the monad stack needs to be something like this
Mask (ReaderT ProducerConfig IO a)
Ideally I would want the function to return what the run Handler returns which is Config.KafkaP (Either KafkaError ()) ,or anything more robust .
Adding a solution based on an answer
sendMessage :: String -> Config.KafkaP (Either KafkaError ())
sendMessage msg=do
getProperties <- producerProps
let
mkProducer = newProducer getProperties
--newProducer :: MonadIO m => ProducerProperties -> m (Either KafkaError KafkaProducer)
--mkProducer :: Config.KafkaP (Either KafkaError KafkaProducer)
clProducer (Left _) = return ()
clProducer (Right prod) = closeProducer prod
--closeProducer :: MonadIO m => KafkaProducer -> m ()
--clProducer :: Config.KafkaP (Either () ()) -- ??
runHandler (Left err) = return $ Left err
runHandler (Right prod) = messageSender prod msg
--messageSender :: KafkaProducer -> String -> Config.KafkaP (Either KafkaError ())
--runHandler :: Config.KafkaP (Either KafkaError ()) -- ??
Config.KafkaP $ bracket (Config.runK mkProducer) (Config.runK .clProducer) (Config.runK .runHandler)
If you were using the type ReaderT ProducerConfig IO a directly, there wouldn't be a problem, because the exceptions package provides an instance
MonadMask IO
That says you can use bracket with IO, and another instance
MonadMask m => MonadMask (ReaderT r m)
That says that if the base monad is an instance of MonadMask, then ReaderT over that monad is also an instance of MonadMask.
Notice that MonadMask is not a transformer that is part of the monad stack. Instead, it is a constraint that says "this monad stack supports masking / bracketing operations".
If you were using a type synonym like
type KafkaP a = ReaderT ProducerConfig IO a
there wouldn't be a problem either, because type synonyms don't create a new type, they just give an alias to an exiting one. One can still make use of all the existing typeclass instances for the type.
You mention in the comments that KafkaP is a newtype. A newtype is a cheap way of creating, well, a new type out of another. It's basically a constructor that holds a value of the original type.
And that's the problem. Because it is a new type, it doesn't share automatically all the typeclass instances of the old one. In fact, having different typeclass instances in newtypes is one of the main motivations for using newtypes!
What can be done? Well, supposing the newtype constructor is exported (sometimes they are hidden for purposes of encapsulation) you could unwrap the KafkaPs actions into ReaderT ProducerConfig IO a before sending them into bracket, and then re-wrap the result into KafkaP again. Some of the parameters are KafkaP-returning functions, so you'll probably need to throw in some function composition as well. Perhaps something like (assuming KafkaP is the name of the constructor, and runKafkaP the name of the corresponding accessor):
KafkaP $ bracket (runKafkaP mkProducer) (runKafkaP . clProducer) (runKafkaP . runHandler)
All this wrapping and unwrapping is tedious; sometimes using coerce from Data.Coerce can help. But this only works when the newtype constructor is exported.
You might also think of defining your own MonadMask instance for KafkaP using the technique above. This can be done, but instances which are defined neither in the module that defines the typeclass nor in the module that defines the type are called orphan instances and are somewhat frowned upon.

How to deal with a monadic return value `m (Maybe Foo)`?

I have a function which returns a value wrapped in a Monad,
produceMessage :: MonadIO m => KafkaProducer -> ProducerRecord -> m (Maybe KafkaError)
And I have some code to call this function like so
err <- produceMessage prod message
return $ Right ()
The above code has been written by someone else, I am just trying to understand what is happening here. This is the rest of the function
messageSender :: KafkaProducer -> String -> Config.KafkaP (Either KafkaError ())
messageSender prod msg = do
message <- mkMessage Nothing (Just $ pack msg)
err <- produceMessage prod message
--forM_ err print
return $ Right ()
I have three specific questions,
I am confused what does the type signature of produceMessage mean? The type constraint is MonadIO m, what does this mean?
The return type is m (Maybe KafkaError), so this returns a Maybe value wrapped in which monad?
How does Right () come into picture here? In general I am really not
able to understand the last two lines of messageSender.
The type constraint means that the function can be used to return a value for any type m that has a MonadIO instance. Typically, that means IO itself or a monad stack built on top of IO.
The value returned by produceMessage is in part determined by the caller. Need an IO (Maybe KafkaError) value? You can get that, because IO has a MonadIO instance. Need a MyCustomMonadStack (Maybe KafkaError)? You can get that, if you define a MonadIO instance for MyCustomMonadStack.
Presumably, Config.KafkaP also has a MonadIO instance, based on how messageSender uses produceMessage.
messageSender has a return value of Config.KafkaP (Either KafkaError ()). The expression return $ Right () first uses Right () to produce a value of type Either KafkaError (), then return is applied to that to produce a value of type Config.KafkaP (Either KafkaError ()). Note that the commented line -- forM_ err print is the only thing that might have used the value coming from produceMessage, so right now messageSender pretends that produceMessage worked, whether it did or not.
A more robust definition would actually use the return value of produceMessage in some way, perhaps like
err <- produceMessage prod message
return $ case err of
Nothing -> Right ()
Just theError -> Left theError

how haskell catch throwIO called exception in ExceptT

when use monad transformer ExceptT to wrapper the IO Monad.
and then throwIO e in the inner IO monad,
so how this exception was catch by outer Exception and converted to and Either e a Type one
for example:
my program define type :
newtype HandlerT e a = HandlerT { runHandlerT :: ExceptT e (ReaderT (IO a)) }
then when in inner IO monad do the HTTP request to access and no-existen web site, the innert http.LBS will throw exception by call throwIO.
then ,
runExceptT $ runReaderT $ runHandlerT (http.LBS "http://notexist.com")
will return an Either e a
the e has the exception the inner call throw out.
You can use the safe-exceptions package (tutorial at: https://haskell-lang.org/library/safe-exceptions), which provides a set of functions which will help here in two ways:
They are lifted to work in many different monads, including ExceptT and ReaderT. Note that you will likely need to derive instances for MonadThrow and MonadCatch for your HandlerT.
You can use the catchAny and tryAny functions to catch all synchronous exceptions, which is probably what you want here. (See the linked tutorial for more information on synchronous vs asynchronous.)
That said: given that the httpLBS function is (most likely) living in IO itself, you can also do something like:
liftIO (tryAny (httpLBS req))
and avoid the need to bother with runtime exceptions in a monad transformer at all.

Use or Abuse of the Error Monad Transformer

Background
I am working with a monad built of a stack of transformers one of which is ErrorT. My monad therefore implements the MonadError class and throwError is a valid statement. I want to throw and error only when a certain condition is met. I am inclined to do the following:
if badCondition
then throwError "You did a bad, bad thing..."
else return ()
doGoodThings
[...]
In looking at this I think I can even generalize it to the following:
throwErrorWhen :: (MonadError e m) => Bool -> e -> m ()
throeErrorWhen cond err = if cond then throwError e else return ()
In fact this seems so obvious that I was surprised that it was not the MonadError Library.
Question:
Is there a better/more correct way to raise an error only is some condition is met?
There's the when function in Control.Monad that evaluates its 2nd argument if the Boolean is true:
when :: Monad m => Bool -> m () -> m ()
So you get,
throwErrorWhen cond e = when cond (throwError e)

How cleanly weave an IO operation into a call stack between two functions which use a custom Monad?

I am practicing at getting consistent with my error handling, and I keep hoping to see the code that I've written start shrinking. But I built up a domain-meaningful persistence function, and the amount of code I had to write just to do monad handling and custom error handling is astounding.
For "programming errors", I just call error "assertion blown"
For really mundane things, I return Nothing (the requested object doesn't exist)
For errors that should be handled, I'm returning Either E V or its equivalent by creating a Control.Monad.Error instance to handle it.
I have in my application multiple functions which I would call primitives, but they can catch certain errors and will raise them my throwing a value of the DBError type. So, I've defined them like so:
data DBError = ConversionError ConvertError
| SaveError String
| OtherError String
deriving (Show, Eq)
instance Error DBError where
noMsg = OtherError "No message found"
strMsg s = OtherError s
type DBMonad = ErrorT DBError IO
selectWorkoutByID :: IConnection a => UUID -> a -> DBMonad (Maybe SetRepWorkout)
insertWorkout :: IConnection a => SetRepWorkout -> a -> DBMonad ()
At the level of the calling application, a Workout is a unique object persisted to the database, so the application only ever calls saveWorkout, which itself uses selectWorkoutByID, insertWorkout, and updateWorkout in the ways you would expect:
saveWorkout :: IConnection a => SetRepWorkout -> a -> DBMonad ()
saveWorkout workout conn =
r <- liftIO $ withTransaction conn $ \conn -> runErrorT $ do
w_res <- selectWorkoutByID (uuid workout) conn
case w_res of
Just w -> updateWorkout workout conn >> return ()
Nothing -> insertWorkout workout conn >> return ()
case r of
Right _ -> return ()
Left err -> throwError err
This is ugly. I have to run and unwrap a DBMonad, run that in the IO monad, lift the IO back up into the DBMonad, and then check the results and re-wrap the results in the DBMonad.
How can I do this with less, and easier to read, code?
I'm expecting that using my custom application monad to handle recoverable errors would help me to reduce the amount of code I have to write, but this is doing the opposite!
Here are some additional questions:
Is there a better way to build up application-semantic errors?
Should I be using Control.Exception instead?
After reviewing http://en.wikibooks.org/wiki/Haskell/Monad_transformers, which is the first document on Monad Transformers that really helped me understand them, I figured out a decent solution.
A new version of the saveWorkout function would look like this:
saveWorkout :: IConnection a => SetRepWorkout -> a -> DBMonad ()
saveWorkout workout conn =
ErrorT $ liftIO $ withTransaction conn $ \conn -> runErrorT $ do
w_res <- selectWorkoutByID (uuid workout) conn
case w_res of
Just w -> updateWorkout workout conn >> return ()
Nothing -> insertWorkout workout conn >> return ()
The deal is this:
withTransaction is returning IO Either DBError (). liftIO has the type MonadIO m => IO a -> m a. ErrorT is the standard constructor for everything of the ErrorT monad, and I defined DBMonad to be of that monad. So, I am working with these types:
withTransaction conn $ <bunch of code> :: IO (Either DBError ())
liftIO :: MonadIO m => IO (Either DBError ()) -> m (Either DBError ())
ErrorT :: IO (Either DBError ()) -> ErrorT IO DBError ()
Ideally, since ErrorT/DBMonad are part of the MonadTrans class, I would use simply lift in order to lift IO (Either DBError ()) back up into the ErrorT monad, but at this time I cannot get it to actually type check correctly. This solution, however, still makes the code better by removing the redundent re-wrapping that I had before.

Resources