[Haskell-beginners] Best practice for catching pure errors in IO?
Aleksandar Dimitrov
aleks.dimitrov at googlemail.com
Wed Oct 27 16:06:04 EDT 2010
> 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.)
More information about the Beginners
mailing list