Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 329
Dmitry Bespalov Avatar asked Jan 05 '12 23:01

Dmitry Bespalov


2 Answers

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.

like image 135
hammar Avatar answered Oct 01 '22 03:10

hammar


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.

like image 23
luqui Avatar answered Oct 01 '22 03:10

luqui