For something like file not found the basic structure of the code below will work, but for this example of division by zero the exception is not caught. How does one catch a divide by zero?
import Control.Exception.Base
import Data.Array
main = toTry `catch` handler
toTry = do
print "hi"
print (show (3 `div` 0))
print "hi"
handler :: IOError -> IO ()
handler e = putStrLn "bad"
You need a handler that catches an ArithException, and matches on DivideByZero.
import Control.Exception.Base
import Data.Array
main = toTry `catch` handler
toTry = do
print "hi"
print (show (3 `div` 0))
print "hi"
handler :: ArithException -> IO ()
handler DivideByZero = putStrLn "Divide by Zero!"
handler _ = putStrLn "Some other error..."
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'm working on a student project in Haskell and i'm having a problem with getLine's behaviour.
Here's the code (simplified) :
main :: IO()
main = do
str <- getLine
putStrLn str
What i'd like to do is, when the user presses Ctrl+D, be able to exitWith (ExitFailure 84).
getLine simply prints an error and exit the program (and returns 1)
deBruijn: <stdin>: hGetLine: end of file
How to change this behaviour ? I only want to change the exit value to 84.
Your program never sees Control-D. What it does see is the fact that standard input has been closed, in this case by your terminal in response to Control-D being typed. This means you want to catch the EOF condition before getLine tries to read a line from a closed file.
import System.IO
import System.Exit
main :: IO ()
main = do
isClosed <- isEOF
if isClosed
then exitWith (ExitFailure 84)
else getLine >>= putStrLn
Instead of manually checking for isEof you could just catch the IO exception as it happens:
import Control.Exception (catch)
import System.IO.Error(isEOFError)
import System.Exit
tryMain :: IO ()
tryMain = getLine >>= putStrLn
main :: IO ()
main = tryMain `catch` (\e ->
if isEOFError e
then exitWith (ExitFailure 84)
else exitWith (ExitFailure 99))
As you can in general not rule out IO exceptions in advance, this is the approach I'd recommend.
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
I'm trying catch the exception that happens when the user writes something different from than expected.
I'm doing this:
main = do
{catch (take_number) fix_error;}
where
take_number = do
{
take_number "Give me a number";
n <- readLn;
}
fix_error e = if Exception e then do
{
putStrLn "Invalid number! Try again";
main;
}
else ioError e
I don't the exception type that I need catch in Execption
The type of exception you get when doing readLn is IOError which is just an alias for IOException.
Here is how you could handle the error, I've added a type to readLn to enforce it to be read as an Int.
import Control.Exception
main :: IO ()
main = catch takeNumber handleError
takeNumber :: IO ()
takeNumber = do
putStrLn "Give me a number:"
n <- readLn :: IO Int
putStrLn ("Success, your number is: " ++ show n)
handleError :: IOError -> IO ()
handleError e = do
putStrLn ("Invalid number! Try again. The error was: " ++ show e)
main
I have a function that accepts some arguments and returns IO (Either String String), say
testEither :: Int -> IO (Either String String)
testEither 0 = return (Left "we got an error")
testEither _ = return (Right "everything is ok")
(Real function fetches some stuff from real world and might fail)
I want to send output from that function to writeFile fileName. Expected behavior: if I bind testEither 0 to writeFile "test.txt", I fail with Left ..., and if I call it with testEither 1, I get everything is ok in file test.txt.
I guess the type of the whole expression should be something like IO (Either String ()), but I may be wrong.
You can use the ErrorT1 monad transformer to give you pure error handling on top of the IO monad:
import Control.Monad.Error
testEither :: Int -> IO (Either String String)
testEither 0 = return (Left "we got an error")
testEither _ = return (Right "everything is ok")
main = runErrorT $ do
result <- ErrorT $ testEither 0
lift $ writeFile "test.txt" result
1 ErrorT appears to have been replaced with ExceptT in the newest version of mtl, but the functionality should be similar.
This is easy using Either's Traversable instance:
import Data.Traversable
main = do
traverse (writeFile "test.txt") (Left "we got an error")
traverse (writeFile "test.txt") (Right "everything is ok")
This won't happen automatically, but you can easily use pattern matching to perform this action:
writeFileEither :: FilePath -> Either a String -> IO ()
writeFileEither _ (Left _) = return ()
writeFileEither fp (Right text) = writeFile fp text
Then you can bind them together with
main = testEither 1 >>= writeFileEither "test.txt"
Or with do notation:
main = do
result <- testEither 1
writeFileEither "test.txt" result
The main function below is an example that show how to use testEither in IO: if testEither returns an error then the error is written to stderr else the correct result is extracted from Right and written to the file test.txt:
import System.IO
testEither :: Int → IO (Either String String)
testEither 0 = return (Left "we got an error")
testEither _ = return (Right "everything is ok")
main :: IO 𐌏
main = do
res ← testEither 0
case res of
Left err → hPutStrLn stderr ("Error: " ++ err)
Right s → writeFile "test.txt" s