alastair at reid-consulting-uk.ltd.uk
Fri Oct 1 12:51:47 EDT 2004
On Friday 01 October 2004 16:43, John Goerzen wrote:
> One of the most important features of a modern language is, to me,
> exceptions. I have found very little coverage of exceptions in Haskell,
> what what coverage there was seemed to be centered on I/O.
There's three things one might call exceptions in Haskell:
1) IOErrors result from IO operations and can be caught in the IO monad.
2) Exceptions result from pure or IO code and can be caught in the IO monad.
They are raised by calling raise (in pure code) or raiseIO (in IO code).
They are also raised by pattern match failure, calls to error and
things like division by zero.
They are non-deterministic in the following sense:
The expression '(raise exn1) + (raise exn2) :: Int' may raise
either exception 'exn1' or exception 'exn2' depending on
choice of compiler, optimization options, environment settings,
current time of day, phase of moon, etc.
It is because of this non-determinism that exceptions can _only_
be caught in the IO monad.
Another consequence is that exceptions are not very useful for
reporting errors in user input where determinism, reporting
errors before starting a long computation, being able
to report multiple errors, etc are useful.
3) Error values can be distinguished using the Maybe or Either types
and/or using monads. For example, parsing command line arguments
Left "Error: can't parse '42.0' - integer expected"
The advantage of this approach is that the type system tracks where
errors can propagate to so you can be confident that error checking
is being performed _before_ spending 3 days computing a result and
not when you go to write the answer.
The disadvantage is that it is a bit tedious - but monads, lifting, etc.
help a lot with this.
In the following, I'll tak exclusively about the 2nd type (since the other two
> 1. Can exceptions be used in "pure" functions (outside of monads?)
They can be raised in pure functions but can only be caught in IO functions.
(If you simply want to change what exception is reported, that can be done in
> 2. How are these exceptions caught and handled aside from using bracket?
There is a catch function with type:
catch :: IO a -> (Exception -> IO a) -> IO a
Built on top of this, are functions to do things like try-finally. One of my
favourites is 'bracket' which takes an initial action, a final action and a
middle action (in that order) and makes sure that the final action is
performed even if the middle action fails. For example:
bracket (open "/etc/passwd") hClose $ \ h -> do
cs <- hGetContents h
mapM crack (lines cs)
The strange order of the arguments is to put the initial action (opening the
file) next to the final action (closing the file) - which is often a good
> 3. Can I define my own exception types?
Sadly, no. There is only one 'exception type'.
You can use dynamics and encode new kinds of exceptions as Strings
but that isn't very satisfactory.
> 4. Can I write code that can catch and handle multiple different
> exception types from a single block of code?
You use standard Haskell pattern matching so, yes, you can catch multiple
kinds of exceptions. They're not different types though.
> 5. Is there anything different about working with exceptions in monads?
Use raiseIO instead of raise to raise exceptions.
> 6. Can I get a stack trace from ghc or hugs if an exception is never
> caught and thus causes the program to terminate?
More information about the Haskell-Cafe