In The acquire-use-release cycle section from Real World Haskell, the type of bracket is shown:
ghci> :type bracket
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
Now, from the description of bracket, I understand that an exception might be thrown while the function of type a -> IO c is running. With reference to the book, this exception is caught by the calling function, via handle:
getFileSize path = handle (\_ -> return Nothing) $
bracket (openFile path ReadMode) hClose $ \h -> do
size <- hFileSize h
return (Just size)
I can't help but thinking that when the exception does occur from within bracket's 3rd argument, bracket is not returning an IO c.
How does this go well with purity?
I think the answer might be exactly this, but I'm not sure.
I can't help but thinking that when the exception does occur from within bracket's 3rd argument, bracket is not returning an IO c.
Prelude> fail "gotcha" :: IO Bool
*** Exception: user error (gotcha)
As you note, no Bool (respectively, c) value is produced. That's ok, because the action does not conclude – instead it re-raises the exception. That exception might then either crash the program, or it might be caught again somewhere else in calling code – importantly, whoever catches it will a) not get the result value (“the c”), you never do that in case of an exception; b) doesn't need to worry about closing the file handle, because that has already been done by bracket.
I want to read from file and am using pattern matching on Left/Right to tell is said file exists like so:
tehfile <- readIniFile "somefile_that_does_not_exist.ini"
s <- case tehfile of
Left a -> print "woot"
Right b -> ...
The error I'm getting is the one generated by Data.Ini library: "openFile: does not exist (No such file or directory)".
Ideally, I'd like to override that text entirely or at least append my super useful "woot" string after the one produced by the library. How can I do that and why doesn't it happen in the code I have now?
If you look at the implementation for readIniFile, it's just
readIniFile :: FilePath -> IO (Either String Ini)
readIniFile = fmap parseIni . T.readFile
First it tries to open and read the file, then it tries to parse the contents as an INI config. That first part would fail with an exception if the file doesn't exist, and the second fails with a Left. To print your own message and resume the computation, instead of pattern matching on the Left case you'll want to catch the exception.
λ> import Control.Exception
λ> handler :: IOException -> IO (Either String Ini); handler _ = putStrLn "woot" >> pure (Left "woot")
λ> tehfile <- readIniFile "somefile_that_does_not_exist.ini" `catch` handler
woot
Or you can throw an error with your own message, to end the computation
λ> handler :: IOException -> IO (Either String Ini); handler _ = error "woot"
λ> tehfile <- readIniFile "somefile_that_does_not_exist.ini" `catch` handler
*** Exception: woot
CallStack (from HasCallStack):
error, called at <interactive>...
Or write your own exception and throw that
λ> data Woot = Woot deriving (Show)
λ> instance Exception Woot
λ> handler :: IOException -> IO (Either String Ini); handler _ = throwIO Woot
λ> tehfile <- readIniFile "somefile_that_does_not_exist.ini" `catch` handler
*** Exception: Woot
I want to catch all exceptions in IO String function. When I run this code:
import Control.Exception.Base
handler :: SomeException -> IO String
handler _ = return "Gotta catch'em all!"
boom :: IO String
boom = return (show (3 `div` 0))
get :: IO String
get = boom `catch` handler
main :: IO()
main = do
x <- get
print x
I get
exctest: divide by zero
However this code works:
import Control.Exception.Base
handler2 :: SomeException -> IO ()
handler2 _ = print "Gotta catch'em all!"
boom2 :: IO ()
boom2 = print $ show (3 `div` 0)
main :: IO()
main = do
boom2 `catch` handler2
with result
> Gotta catch'em all!
When I change boom in first example to
boom = error "ERR"
The exception is caught ok. Why does it behave this way? What should I do to catch exception in first example?
This has nothing to do with () vs. other types. Note that the following also doesn't catch:
boom = return $ error "ERR"
The reason catch won't do anything about that is that return is lazy, thus the error isn't actually triggered by catch, only if you try to get at the contained value.
It is for precisely this reason that the Exception module has evaluate, which is equivalent to return but strict.
boom :: IO String
boom = evaluate . show $ 3`div`0
main :: IO ()
main = do
res <- try (mapM_ (callProcess "pandoc") [["--help"],["-eoc"],["-exois"],["--help"]]) :: IO (Either SomeException ())
case res of
Right a -> print "done"
Left e -> print "error"
in the above code, some of the argument for the command pandoc are wrong and pandoc will throw errors. but I want my code to continue without stopping there and finish the mapM_
What Exception handling should I use?
The error I have is:
pandoc: unrecognized option `-e'
Try pandoc --help for more information.
"error"
Instead of trying your best to do a whole block of commands, have a block of commands that each try their best. Change try (mapM_ foo bar) into mapM_ (try . foo) bar.
I am building a Haskell application and trying to figure out how I am going to build the error handling mechanism. In the real application, I'm doing a bunch of work with Mongo. But, for this, I'm going to simplify by working with basic IO operations on a file.
So, for this test application, I want to read in a file and verify that it contains a proper fibonnacci sequence, with each value separated by a space:
1 1 2 3 5 8 13 21
Now, when reading the file, any number of things could actually be wrong, and I am going to call all of those exceptions in the Haskell usage of the word.
data FibException = FileUnreadable IOError
| FormatError String String
| InvalidValue Integer
| Unknown String
instance Error FibException where
noMsg = Unknown "No error message"
strMsg = Unknown
Writing a pure function that verifies the sequence and throws an error in the case that the sequence is invalid is easy (though I could probably do better):
verifySequence :: String -> (Integer, Integer) -> Either FibException ()
verifySequence "" (prev1, prev2) = return ()
verifySequence s (prev1, prev2) =
let readInt = reads :: ReadS Integer
res = readInt s in
case res of
[] -> throwError $ FormatError s
(val, rest):[] -> case (prev1, prev2, val) of
(0, 0, 1) -> verifySequence rest (0, 1)
(p1, p2, val') -> (if p1 + p2 /= val'
then throwError $ InvalidValue val'
else verifySequence rest (p2, val))
_ -> throwError $ InvalidValue val
After that, I want the function that reads the file and verifies the sequence:
type FibIOMonad = ErrorT FibException IO
verifyFibFile :: FilePath -> FibIOMonad ()
verifyFibFile path = do
sequenceStr <- liftIO $ readFile path
case (verifySequence sequenceStr (0, 0)) of
Right res -> return res
Left err -> throwError err
This function does exactly what I want if the file is in the invalid format (it returns Left (FormatError "something")) or if the file has a number out of sequence (Left (InvalidValue 15)). But it throws an error if the file specified does not exist.
How do I catch the IO errors that readFile may produce so that I can transform them into the FileUnreadable error?
As a side question, is this even the best way to do it? I see the advantage that the caller of verifyFibFile does not have to set up two different exception handling mechanisms and can instead catch just one exception type.
You might consider EitherT and the errors package in general. http://hackage.haskell.org/packages/archive/errors/1.3.1/doc/html/Control-Error-Util.html has a utility tryIO for catching IOError in EitherT and you could use fmapLT to map error values to your custom type.
Specifically:
type FibIOMonad = EitherT FibException IO
verifyFibFile :: FilePath -> FibIOMonad ()
verifyFibFile path = do
sequenceStr <- fmapLT FileUnreadable (tryIO $ readFile path)
hoistEither $ verifySequence sequenceStr (0, 0)
#Savanni D'Gerinel: you are on the right track. Let's extract your error-catching code from verifyFibFile to make it more generic, and modify it slightly so that it works directly in ErrorT:
catchError' :: ErrorT e IO a -> (IOError -> ErrorT e IO a) -> ErrorT e IO a
catchError' m f =
ErrorT $ catchError (runErrorT m) (fmap runErrorT f)
verifyFibFile can now be written as:
verifyFibFile' :: FilePath -> FibIOMonad ()
verifyFibFile' path = do
sequenceStr <- catchError' (liftIO $ readFile path) (throwError . FileUnReadable)
ErrorT . return $ verifySequence sequenceStr' (0, 0)
Notice what we have done in catchError'. We have stripped the ErrorT constructor from the ErrorT e IO a action, and also from the return value of the error-handling function, knowing than we can reconstruct them afterwards by wrapping the result of the control operation in ErrorT again.
Turns out that this is a common pattern, and it can be done with monad transformers other than ErrorT. It can get tricky though (how to do this with ReaderT for example?). Luckily, the monad-control packgage already provides this functionality for many common transformers.
The type signatures in monad-control can seem scary at first. Start by looking at just one function: control. It has the type:
control :: MonadBaseControl b m => (RunInBase m b -> b (StM m a)) -> m a
Let's make it more specific by making b be IO:
control :: MonadBaseControl IO m => (RunInBase m IO -> IO (StM m a)) -> m a
m is a monad stack built on top of IO. In your case, it would be ErrorT IO.
RunInBase m IO is a type alias for a magical function, that takes a value of type m a and returns a value of type IO *something*, something being some complex magic that encodes the state of the whole monad stack inside IO and lets you reconstruct the m a value afterwards, once you have "fooled" the control operation that only accepts IO values. control provides you with that function, and also handles the reconstruction for you.
Applying this to your problem, we rewrite verifyFibFile once more as:
import Control.Monad.Trans.Control (control)
import Control.Exception (catch)
verifyFibFile'' :: FilePath -> FibIOMonad ()
verifyFibFile'' path = do
sequenceStr <- control $ \run -> catch (run . liftIO $ readFile path)
(run . throwError . FileUnreadable)
ErrorT . return $ verifySequence sequenceStr' (0, 0)
Keep in mind that this only works when the proper instance of MonadBaseControl b m exists.
Here is a nice introduction to monad-control.
So, here's an answer that I have developed. It centers around getting readFile wrapped into the proper catchError statement, and then lifted.
verifyFibFile :: FilePath -> FibIOMonad ()
verifyFibFile path = do
contents <- liftIO $ catchError (readFile path >>= return . Right) (return . Left . FileUnreadable)
case contents of
Right sequenceStr' -> case (verifySequence sequenceStr' (0, 0)) of
Right res -> return res
Left err -> throwError err
Left err -> throwError err
So, verifyFibFile gets a little more nested in this solution.
readFile path has type IO String, obviously. In this context, the type for catchError will be:
catchError :: IO String -> (IOError -> IO String) -> IO String
So, my strategy was to catch the error and turn it into the left side of an Either, and turn the successful value into the right side, changing my data type to this:
catchError :: IO (Either FibException String) -> (IOError -> IO (Either FibException String)) -> IO (Either FibException String)
I do this by, in the first parameter, simply wrapping the result into Right. I figure that I won't actually execute the return . Right branch of the code unless readFile path was successful. In the other parameter to catch, I start with an IOError, wrap it in Left, and then return it back into the IO context. After that, no matter what the result is, I lift the IO value up into the FibIOMonad context.
I'm bothered by the fact that the code gets even more nested. I have Left values, and all of those Left values get thrown. I'm basically in an Either context, and I had thought that one of the benefits Either's implementation of the Monad class was that Left values would simply be passed along through the binding operations and that no further code in that context would be executed. I would love some elucidation on this, or to see how the nesting can be removed from this function.
Maybe it can't. It does seem that the caller, however, can call verifyFibFile repeatedly and execution basically stops the first time verifyFibFile returns an error. This works:
runTest = do
res <- verifyFibFile "goodfib.txt"
liftIO $ putStrLn "goodfib.txt"
--liftIO $ printResult "goodfib.txt" res
res <- verifyFibFile "invalidValue.txt"
liftIO $ putStrLn "invalidValue.txt"
res <- verifyFibFile "formatError.txt"
liftIO $ putStrLn "formatError.txt"
Main> runErrorT $ runTest
goodfib.txt
Left (InvalidValue 17)
Given the files that I have created, both invalidValue.txt and formatError.txt cause errors, but this function returns Left (InvalidValue ...) for me.
That's okay, but I still feel like I've missed something with my solution. And I have no idea whether I'll be able to translate this into something that makes MongoDB access more robust.