Dmitry
Dmitry

Reputation: 5279

Exceptions in Haskell

If I understood rightly, exceptions in Haskell basically intended to deal within IO monad. At least exception could be caught inside IO monad.

But sometimes even pure functions may throw an exception, e.g. read "..." :: Int (when reading string does not represent integer), operator (!!) (when we trying to get item out of range of the list), and so forth. And this is true behavior, I don't deny. However, I would not want to change signature of function just to get it chance to catch possible exception, because in this case I have to change signature all the functions by call stack before.

Is there some pattern to deal with exceptions more comfortable in Haskell, out of IO monad? May be I should use unsafePerformIO in this case? How "safe" to use unsafePerformIO just for catching exceptions in pure functions?

Upvotes: 6

Views: 1141

Answers (4)

amindfv
amindfv

Reputation: 8448

I'm going to go out on a limb and disagree strongly with the opinions of people who say that the solution is to "just avoid having bugs in the first place." I would structure your code around handling your errors within one of these monads.

One of the main strengths of pure functions (and FP) is the ability to reason about your code, along the lines of "if a function has the type [a] -> a, then for all lists of a type a, I'll get back a value of type a." Exceptions like these cut the legs out from under that.

A good reason for head being the way that it is, is that it's much simpler for beginners to learn list manipulations before Maybe and friends. But if you can understand a better way to do it, I'd avoid these risks.

Upvotes: 3

luqui
luqui

Reputation: 60513

That's what monads are for! (Well, not just that, but exception handling is one use of monadic idioms)

You do change the signature of functions that can fail (because they change semantics, and you want to reflect as much of the semantics as possible in the type, as a rule of thumb). But code that uses these functions does not have to pattern match on every failable function; they can bind if they don't care:

head :: [a] -> Maybe a

eqHead :: (Eq a) => [a] -> Maybe [a]
eqHead xs = do
    h <- head xs
    return $ filter (== h) xs

So eqHead cannot be written "purely" (a syntactic choice whose alternatives I would like to see explored), but it also doesn't really have to know about the Maybe-ness of head, it only has to know that head can fail in some way.

It's not perfect, but the idea is that functions in Haskell do not have the same style as Java. In typical Haskell design, an exception does not usually occur deep in the call chain. Instead, the deep call chain is all pure, when we know that all arguments are fully defined and well-behaved, and validation occurs at the outermost layers. So an exception bubbling up from the deep is not really something that needs to be supported in practice.

Whether the difficulty of doing so causes the design patterns, or the design patterns cause the lack of features supporting this is a matter of debate.

Upvotes: 5

hammar
hammar

Reputation: 139910

In pure code, it's usually best to avoid exceptions from happening in the first place. That is, don't use head unless you're absolutely positive that the list isn't empty, and use reads and pattern matching instead of read to check for parse errors.

I think a good rule of thumb is that exceptions in pure code should only come from programming errors, i.e. calls to error, and these should be treated as bugs, not something an exception handler can deal with.

Note that I'm only talking about pure code here, exceptions in IO have their uses when dealing with the exceptional cases that sometimes happen when interfacing with the "real world". However, pure mechanisms like Maybe and ErrorT are easier to work with, so they are often preferred.

Upvotes: 10

Daniel Pratt
Daniel Pratt

Reputation: 12077

If you anticipate that a function such as read may cause an exception, then why not simply restructure your code to avoid the possibility of the exception occurring?

As a more direct answer to your question, there is spoon.

Upvotes: 3

Related Questions