telephone
telephone

Reputation: 1161

Meaning of Haskell exceptions

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

Answers (5)

telephone
telephone

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

MathematicalOrchid
MathematicalOrchid

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

zack112358
zack112358

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

Dan Burton
Dan Burton

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?

  • a dirty hack so we don't have to make our functions entirely safe
  • a "try/catch" control flow mechanism for the IO monad (IO is a sin bin, so why not throw try/catch in the list of sins as well?)

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

Related Questions