Reputation: 1161
What is the meaning of exceptions in Haskell? The only usage I see is to put in undefined
or error
in my code to stop programs from running. Otherwise I consider programming with exceptions as a logical design flaw. But Haskell has an advanced exception module Control.Exception
which is used by Prelude
. I read that the reason for exceptions in C++ was to prevent a lot of "call function, then check status"-lines in code. But such things can be abstracted away in Haskell. The only other reason I can see for exception handling in Haskell is with FFI, to handle foreign exceptions, but only for internal use in a Haskell function wrapping the call.
Upvotes: 28
Views: 1480
Reputation: 1161
It seems that this question actually was discussed here: http://haskell.org/haskellwiki/Exception I don't know if this question was actually answerable, as pointed out.
Upvotes: 0
Reputation: 62818
The "error" function is for when a function receives invalid input, or when something internal happens that is supposed to never happen (i.e., a bug). In short, calling "error" represents a bug - either in the caller or callee.
The "undefined" constant is more for values which aren't supposed to be used - generally because they're going to be replaced with something else, or because they're phantom values used to get a specific type. (It's actually implemented as a call to "error".)
So why do we have Control.Exception
with all its fanciness then?
Basically, "because I/O operations can throw exceptions". You could be happily talking to an FTP server over a TCP socket, and suddenly the connection breaks. The result? Your program throws an exception. Or you could run out of RAM, or the disk might fill up, or whatever.
Notice that almost all of these things are not your fault. If you can anticipate a specific thing going wrong, you should use things like Maybe
and Either
to handle it in a pure way. (E.g., if you're going to invert a matrix, well, the matrix could be non-invertible, so you'd better return a Maybe Matrix
as the result.) For things that you can't reasonably anticipate (e.g., some other program just deleted the file you're trying to work on), exceptions are the way.
Note that Control.Exception
contains lots of stuff for handling exceptions as well as just defining lots of different types. I don't care if the code I called did something which is incorrect and therefore a bug; I'd still like to be able to tell the client I was just talking to that the connection is about to be closed, log a description to a log file somewhere, and do other cleanup stuff, rather than just have my program suddenly, you know, stop.
Upvotes: 5
Reputation: 11
Exceptions are a legitimate form of flow control. It's not clear to me why, when given a tool, programmers insist that it is "only for" certain cases and rule out other possible uses.
For example, if you are performing a backtracking computation, you can use exceptions to backtrack. In Haskell it would probably be more common to use the list monad for this, but exceptions are a legitimate way to go.
Upvotes: 1
Reputation: 53665
In my humble opinion, exceptions mean "you broke the contract of a function". I'm not talking about the type contract, I'm talking about the stuff you generally find in comments.
-- The reciprocal is not defined for the value 0
myRecip :: Fractional a => a -> a
myRecip x | x == 0 = throw DivideByZero
| otherwise = 1 / x
Of course you could always provide this functions the "safe" way:
safeRecip :: Fractional a => a -> Maybe a
safeRecip x | x == 0 = Nothing
| otherwise = Just $ 1 / x
Perhaps we should even abstract this pattern
restrictInput :: (a -> Bool) -> (a -> b) -> (a -> Maybe b)
restrictInput pred f x = if pred x then Just (f x) else Nothing
safeRecip' = restrictInput (/= 0) myRecip
You could imagine similar combinators for using Either
instead of Maybe
to report failure. So since we have the ability to handle these things with pure functions, why bother with the impure exception model? Well, most Haskellers will tell you to stick with purity. But the dark truth is, it's just a pain to add that extra layer. You can no longer write
prop_recip x = x == (recip . recip) x
Because now the result of recip x
is in a Maybe
. There are useful things you can do with (a -> a)
functions that you can no longer do. Now you have to think about composing Maybes. This, of course, is trivial if you are comfortable with monads:
prop_recip 0 = (safeRecip >=> safeRecip) 0 == Nothing
prop_recip x = (safeRecip >=> safeRecip) x == Just x
But therein lies the rub. Newbies generally know next to nothing when it comes to monadic composition. The Haskell Committee, as many on the #haskell irc channel will quickly tell you*, has made some rather wonky decisions regarding language design in order to cater to newbies. We want to be able to say "you don't need to know monads in order to start making useful things in Haskell". And I generally agree with that sentiment.
tl;dr A few quick answers to the question: What is an exception?
There may be other explanations.
See also Haskell Report > Basic Input/Output > Exception Handling in the IO Monad
*I actually asked on #haskell irc if they approved of this statement. The only response I got was "wonky" :)
so it is obviously proven true by absence of objection.
[Edit] Note that error
and undefined
are defined in terms of throw
:
error :: [Char] -> a
error s = throw (ErrorCall s)
undefined :: a
undefined = error "Prelude.undefined"
Upvotes: 27