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
Related
I have following demo code from here:
import System.Environment
import System.IO
import System.IO.Error
main = toTry `catch` handler
toTry :: IO ()
toTry = do (fileName:_) <- getArgs
contents <- readFile fileName
putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
handler :: IOError -> IO ()
handler e = putStrLn "Whoops, had some trouble!"
But it is giving me error:
runghc trycatch2.hs
trycatch2.hs:5:14: error:
Variable not in scope: catch :: IO () -> (IOError -> IO ()) -> t
Where is the problem and how can it be solved? Thanks.
The example in Learn You a Haskell for the Greater Good is outdated, the catch :: Exception e => IO a -> (e -> IO a) -> IO a function is part of Control.Exception.
System.IO.Error however still has a catch function that is here applicable: catchIOError :: IO a -> (IOError -> IO a) -> IO a, but as the documentation says:
The catchIOError function establishes a handler that receives any IOException raised in the action protected by catchIOError. An IOException is caught by the most recent handler established by one of the exception handling functions. These handlers are not selective: all IOExceptions are caught.
(...)
Non-I/O exceptions are not caught by this variant; to catch all exceptions, use catch from Control.Exception.
So you can fix the problem here by using catchIOError (since you are dealing with an IOError, but as specified in the documentation, this only covers a limited set of exceptions), or you can import catch from Control.Exception:
import Control.Exception(catch)
import System.Environment
import System.IO
import System.IO.Error
main :: IO ()
main = toTry `catch` handler
toTry :: IO ()
toTry = do (fileName:_) <- getArgs
contents <- readFile fileName
putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
handler :: IOError -> IO ()
handler e = putStrLn "Whoops, had some trouble!"
I have a usual haskell code which reads file and handle any exception:
handle ((\_->return "ERROR") :: SomeException -> IO String) $ readFile "file.txt"
When I try to read bad encoded file I always get error:
*** Exception: file.txt: hGetContents: invalid argument (invalid byte sequence)
and program is not going to enter to the my exception handle function. I also tried to use IOError and IOException types instead of SomeException but it changes nothing.
If I open similar file with by handle and read it with code:
handle ((\_->return "ERROR") :: SomeException -> IO String) (hGetContents myHandle)
works fine.
How to catch exceptions thrown by hGetContents passed by readFile right way?
You can force the entire string to be read within the scope of the catch:
Control.Exception.handle
((\_ -> return "ERR") :: Control.Exception.SomeException -> IO String)
(Prelude.readFile "file.txt" >>= \res -> res `deepseq` (pure res))
"ERR"
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
It seems to me that exceptions in Haskell can be caught only immediately after they're thrown and are not propagated as in Java or Python. A short example illustrating this is below:
{-# LANGUAGE DeriveDataTypeable #-}
import System.IO
import Control.Monad
import Control.Exception
import Data.Typeable
data MyException = NoParseException String deriving (Show, Typeable)
instance Exception MyException
-- Prompt consists of two functions:
-- The first converts an output paramter to String being printed to the screen.
-- The second parses user's input.
data Prompt o i = Prompt (o -> String) (String -> i)
-- runPrompt accepts a Prompt and an output parameter. It converts the latter
-- to an output string using the first function passed in Prompt, then runs
-- getline and returns user's input parsed with the second function passed
-- in Prompt.
runPrompt :: Prompt o i -> o -> IO i
runPrompt (Prompt ofun ifun) o = do
putStr (ofun o)
hFlush stdout
liftM ifun getLine
myPrompt = Prompt (const "> ") (\s -> if s == ""
then throw $ NoParseException s
else s)
handleEx :: MyException -> IO String
handleEx (NoParseException s) = return ("Illegal string: " ++ s)
main = catch (runPrompt myPrompt ()) handleEx >>= putStrLn
After running the program, when you just press [Enter] whithout typing anything, I supposed to see: Illegal string: in the output. Instead there appears: prog: NoParseException "". Suppose now that Prompt type and runPrompt function are defined in common library outside the module and cannot be changed to handle the exception in functions passed to Prompt constructor. How can I handle the exception without changing the runPrompt?
I thought about adding the third field to Prompt to inject exception-handling function this way, but it seems ugly to me. Is there a better choice?
The problem you're having is because you're throwing your exception in pure code: the type of throw is Exception e => e -> a. Exceptions in pure code are imprecise, and do not guarantee ordering with respect to IO operations. So the catch doesn't see the pure throw. To fix that, you can use evaluate :: a -> IO a, which "can be used to order evaluation with respect to other IO operations" (from the docs). evaluate is like return, but it forces evaluation at the same time. Thus, you can replace liftM ifun getLine with evaluate . ifun =<< getline, which forces ifun to have been evaluated during runPrompt IO action. (Recall that liftM f mx = return . f =<< mx, so this is the same but with more control over evaluation.) And without changing anything else, you'll get the right answer:
*Main> :main
>
Illegal string:
Really, though, this isn't where I'd use exceptions. People don't use exceptions all that much in Haskell code, and particularly not in pure code. I'd much rather write Prompt so that the input function's potential failure would be encoded in the type:
data Prompt o i = Prompt (o -> String) (String -> Either MyException i)
Then, running the prompt would just return an Either:
runPrompt :: Prompt o i -> o -> IO (Either MyException i)
runPrompt (Prompt ofun ifun) o = do putStr $ ofun o
hFlush stdout
ifun `liftM` getLine
We'd tweak myPrompt to use Left and Right instead of throw:
myPrompt :: Prompt a String
myPrompt = Prompt (const "> ") $ \s ->
if null s
then Left $ NoParseException s
else Right s
And then we use either :: (a -> c) -> (b -> c) -> Either a b -> c to handle the exception.
handleEx :: MyException -> IO String
handleEx (NoParseException s) = return $ "Illegal string: " ++ s
main :: IO ()
main = putStrLn =<< either handleEx return =<< runPrompt myPrompt ()
(Additional, unrelated, note: you'll notice I made some stylistic changes here. The only one I'd say is truly important is to use null s, not s == "".)
If you really want the old behavior back at the top level, you can write runPromptException :: Prompt o i -> o -> IO i which throws the Left case as an exception:
runPromptException :: Prompt o i -> o -> IO i
runPromptException p o = either throwIO return =<< runPrompt p o
We don't need to use evaluate here because we're using throwIO, which is for throwing precise exceptions inside IO computations. With this, your old main function will work fine.
If you look at the type of myPrompt, you’ll see that it’s Prompt o String, i.e. not in IO. For the smallest fix:
{-# LANGUAGE DeriveDataTypeable #-}
import System.IO
import Control.Monad
import Control.Exception
import Data.Typeable
data MyException = NoParseException String deriving (Show, Typeable)
instance Exception MyException
-- Prompt consists of two functions:
-- The first converts an output paramter to String being printed to the screen.
-- The second parses user's input.
data Prompt o i = Prompt (o -> String) (String -> IO i)
-- runPrompt accepts a Prompt and an output parameter. It converts the latter
-- to an output string using the first function passed in Prompt, then runs
-- getline and returns user's input parsed with the second function passed
-- in Prompt.
runPrompt :: Prompt o i -> o -> IO i
runPrompt (Prompt ofun ifun) o = do
putStr (ofun o)
hFlush stdout
getLine >>= ifun
myPrompt :: Prompt o String
myPrompt = Prompt (const "> ") (\s -> if s == ""
then throw $ NoParseException s
else return s)
handleEx :: MyException -> IO String
handleEx (NoParseException s) = return ("Illegal string: " ++ s)
main = catch (runPrompt myPrompt ()) handleEx >>= putStrLn
Though it might be more appropriate it to be Prompt o i e = Prompt (o -> String) (String -> Either i e).
I have three functions I map over, I wish to stop evaluation upon catching an exception.
I can catch the exception, but am not getting the behavior I want. It could be that I'm thinking about this problem in the incorrect way (maybe I shouldn't be map a list of functions in this case), and would appreciate this being pointed out. Here's what I think is the relevant code.
import qualified Control.Exception as C
data JobException = PreProcessFail
| JobFail
| ChartFail
deriving (Show, Typeable)
instance C.Exception JobException
type ProcessState = MVar ProcessConfig
data ProcessConfig = PConfig { model :: ServerModel
, ipAddress :: String
, cookie :: Cookie
} deriving Show
exceptionHandler :: JobException -> IO ()
exceptionHandler exception = do
writeFile "testException.txt" ("caught exception " ++ (show exception))
-- much more functionality will be put here once I get the logic correct
preProcess :: ProcessState -> IO ()
preProcess sModel = do
putStrLn ("preProcessing" )
initiateJob :: ProcessState -> IO ()
initiateJob sModel = do
C.throw JobFail
putStrLn ("in progress")
makeChart :: ProcessState -> IO ()
makeChart sModel = do
putStrLn ("chart making")
So now, when I test this out in ghci, this is what happens.
a <- mapM (flip Control.Exception.catch exceptionHandler) [preProcess world, initiateJob world, makeChart world]
Loading package filepath-1.2.0.0 ... linking ... done.
Loading package unix-2.4.2.0 ... linking ... done.
preProcessing
chart making
I should not be seeing the string "chart making". How do I abort the evaluation of the list upon throwing an exception?
mapM maps the function then sequences the list. So you've got a catch around each action in the list separately. What you want is to sequence the list into a single action, and then catch the exception once so interrupting everything else in the list. The following works:
(flip Control.Exception.catch exceptionHandler) $ sequence_ [preProcess world, initiateJob world, makeChart world]