John Goerzen wrote:

>On Tue, Nov 23, 2004 at 05:20:19PM +0000, Ben Rudiak-Gould wrote:
>
>>In any case, mapException is pure, and it's good enough for most of the cases where one might want to catch exceptions outside the IO monad.
>
>Well, I'm maving trouble wrapping my head around how I could use it in a
>pure enviroment. It's defined as:
>
>mapException :: (Exception -> Exception) -> a -> a
>
>I *think*, from reading the source, that it is returning the value 'a'
>if there is no exception, or the mapped exception (or set of them?) if
>there is.


Basically, in the paper, exceptions are modeled as extra values which inhabit every type. Bool, for example, contains the following values: True, False, _|_, and all nonempty /sets/ of values of type Exception. (The paper actually says that _|_ is the set of all exceptions, but I think that's an error. _|_ is quantitatively different from any set of exceptions.) My expression (error "x" + error "y") has the value { Control.Exception.ErrorCall "x", Control.Exception.ErrorCall "y" }. The exception-catching functions choose a single exception from the set by some unknown means; since they're in the IO monad it needn't be predictable which one they choose.

If its second argument is a set of exceptions, mapException passes each exception through the supplied function to get a new set, and returns that. Otherwise it returns its second argument unchanged.

>That doesn't really help me, though, because I still have an exception
>that I must catch in the IO monad.

It's useful for adding information to an exception as it propagates, or translating exceptions at abstraction boundaries. (Except it doesn't quite work, because the exceptions can hide from the handler -- see below.)

>>will catch any exceptions that are thrown during the parsing of the
>>configuration file. Depending on how readConfigFile is written, those
>>exceptions may be hiding inside the returned list, where they won't be
>>noticed until some other part of the program inspects that part of the
>>list, at which point your exception handler is no longer in scope. The
>
>I lost you here.  Isn't the exception handler just another function?
>That is, closures would handle this just like anything else?

I didn't phrase it well. Denotationally the problem is that, e.g.,

   catch (return [1,2,undefined])
         (\e -> return [4,5,6])         === return [1,2,undefined]

whereas often (usually!) I'd prefer it be equivalent to (return [4,5,6]). Every exception-handling function, including mapException, behaves this way:

   myMapException = mapException (\e -> error "internal error")

   myMapException (throwDyn MyError)  ===  error "internal error"
   myMapException [throwDyn MyError]  ===  [throwDyn MyError]

If the first element of the list returned by that last example is ever demanded, MyError will be thrown, and it will /not/ be passed through (\e -> error "internal error"). This means that I can't (straightforwardly) wrap an exception handler around my function to catch exceptions that I don't want the user, or another part of the program, to see.

Of course, this isn't a bug and can't exactly be "fixed". It's just an interaction between the traditional throw/catch exception model and non-strictness that I consider to be unfortunate. Most of the cases where I might otherwise have used exceptions in Haskell have fallen afoul of this problem.

>>A question I don't know the answer to is whether (catchJust :: a ->
>>Maybe a), which lumps all exceptions into a Nothing return, is pure.
>
>Are you proposing a hypothetical here?  (GHC's catchJust returns an
>IO a).

Sorry, that was a braino. It's a totally different function. I should have called it something like exceptionToMaybe. The intended semantics is

                        / Nothing   if x is a set of exceptions
   exceptionToMaybe x = | _|_       if x is _|_
                        \ Just x    otherwise

-- Ben

_______________________________________________
Haskell mailing list
[EMAIL PROTECTED]
http://www.haskell.org/mailman/listinfo/haskell

Reply via email to