
On Saturday 24 September 2011, 19:52:24, Ovidiu Deac wrote:
Given this function
f :: MyMonad String f = do liftIO $ putStrLn "Please enter a file name: " name ← liftIO getLine hFile ← liftIO $ openFile name ReadMode content ← liftIO $ hGetContents hFile return $ content
I would like to catch the error thrown by openFile and translate it to my own exception type.
So first I tried this: hFile ← (liftIO $ openFile name ReadMode) `catch` λ_ → throwError (KnownErr "open failed")
catch has type (IO a -> (e -> IO a) -> IO a) [where e = IOError if you're using Prelude.catch, and e can be any instance of Exception if you're using Control.Exception.catch (recommended)]. But (liftIO ioAction) has type (MyMonad a), so that can't be used as an argument to catch. Nor can (\_ -> throwError (KnownErr "...")).
..but it fails because the error handler is returning a different type then the "try block". Right?
No, it fails because both have the wrong type for catch. It would typecheck with catchError instead of catch, but that wouldn't catch the error :(
Then I tried this: f = do liftIO $ putStrLn "Please enter a file name: " name ← liftIO getLine hFile ← (liftIO $ openFile name ReadMode ↠ return.Right) `catch` λ_ → return (Left $ KnownErr "open failed")
Close. If you replace liftIO with ErrorT in that line and move the first open parenthesis past the $, then it works and does what you want. Without changing the following two lines (or, better, do change them; do x <- act return x is the same as just act ). liftIO adds a layer of Either, ErrorT doesn't. hFile <- ErrorT $ liftM Right (openFile name ReadMode) `catch` (\_ -> return $ Left (KnownErr "open failed")) liftIO $ hGetContents hFile