
I have a pure value which may throw errors when it is evaluated in an IO monad. My code to handle it looks like this: temp <- try $ evaluate value case temp of Left (error::ErrorCall) -> scoldTheUserAndTryAgain Right correct -> doSomething Can this be done without the temporary variable, or with less plumbing? I tried lifting either, but GHC was fussing about ambiguous types, appending a signature to a pattern match is no longer allowed(?), and it didn't look that much cleaner.

I have a pure value which may throw errors when it is evaluated in an IO monad. My code to handle it looks like this:
"Throwing" errors (i.e. calling the `error` function from pure code) isn't something you should do except in cases you're *very* sure that there is *no* way for the program to recover ever again. Maybe and Either are better alternatives. `try` basically just forces using the cleaner way of handling exceptions (Either) when you have to deal with something (say, library code) that you know may call `error` (Prelude's `head` does that when given an empty list) but you'd prefer to recover more gracefully. If you have written the code yourself and you *know* you can recover, throwing an exception and recovering with `try` is not pretty. That's good, you don't want it to be pretty, you want it to hurt, since it's not optimal. You should instead make your `evaluate` have a signature like this: Value -> Either ErrorMessage Result, where ErrorMessage is probably `type`d to String.
temp <- try $ evaluate value case temp of Left (error::ErrorCall) -> scoldTheUserAndTryAgain Right correct -> doSomething
Can this be done without the temporary variable, or with less plumbing? I tried lifting either, but GHC was fussing about ambiguous types, appending a signature to a pattern match is no longer allowed(?), and it didn't look that much cleaner.
It can be done without a temporary variable. You can write a function like this (scoldTheUserWith and tryToObtainAResult should be in the IO Monad, of course!)
treat :: (Either ErrorMessage Result) -> IO Result treat (Left e) = scoldTheUserWith e >> tryToObtainAResult treat (Right r) = return r
Be careful, the result types of treat have to be the same for Left and Right! Now do something like this, inside the IO monad:
tryToObtainAResult :: IO Result tryToObtainAResult = do result <- treat $ evaluate value doSomethingWith result
I hope this doesn't contain errors, I was too lazy to check it :-P Also, it can perhaps be done more gracefully. HTH, Aleks PS: GHC might complain about appending a type signature in pattern matches if you have something like this:
import Control.Monad (liftM) main = do r :: Integer <- liftM read getLine print $ show r
You can fix line 2 in 2 ways:
main = do r <- liftM read hGetLine :: IO Integer
Or compiling with -XScopedTypeVariables (or setting {-# LANGUAGE ScopedTypeVariables #-} as the very first line in your file.)
participants (2)
-
Aleksandar Dimitrov
-
John Smith