I have seen or participated in a few different conversations about whether
Haskell (or Clean) needs some exception handling semantics.

I just read the Hutton/Meijer Monadic Parser Combinator paper (really
good!) and I would like to propose an exception handling mechanims that
works w/o any changes to Haskell.  Please tell me if this makes sense:

----
Programmers want an exception handling mechanism that is brader than
Maybe.  Ideally they want functions to return something like:

> data RetVal a b = Result a | Exception b 

A function may decide that its pre/post conditions are violated and throw
an exception:

> throw a = Exception a

A function returns either a result of some class, an exception of some
class, or an exception without explanation.  

With this datatype we can define an exception type like:

> data FooExceptions a = InfiniteBlahException a a | SomeOtherException |
>                        DivisionByZeroException a

Now, we can write a function that throws exceptions:

> foo x y | x==y   = throw $ InfiniteBlahException x y
>         | y==0   = throw $ DivisionByZeroException x
>         | x==0   = throw SomeOtherException
>         | True   = ereturn $ x/y + log x + log (x-y)

Here we have to use "ereturn" instead of "return" because Haskell does not
allow instances of the Monad class to take two type parameters.

As a result we can define an extended notion of Monad that takes two type
parameters

> class  EMonad m  where
>    ebind   :: m a b -> (a -> m c b) -> m c b
>    ereturn :: a -> m a b
>    eseq    :: m a b -> m c b -> m c b
>    eseq m  k =  m `ebind` \_ -> k

We now instantiate EMonad with RetVal:
> instance EMonad RetVal where
>  (Result x) `ebind` f = f x
>  (Exception x)`ebind` f = Exception x
>  ereturn = Result

With these operators in hand we can treat function evaluation the way 
Hutton and Meijer treat parsers.  (except since functions only return one
value we don't have to worry about non-determinism):

> eval1 :: (a->c) -> RetVal a b -> RetVal c b
> eval1 f x = x `ebind` (ereturn.f)

> eval2 :: (a->c->d) -> RetVal a b -> RetVal c b -> RetVal d b
> eval2 _ (Exception x) y = Exception x
> eval2 f (Result x) y = eval1 (f x) y

> eval3 _ (Exception x) _ _ = Exception x
> eval3 f (Result x) y z = eval2 (f x) y z

My guess is that there is a way to use partial application to create a
single eval function, but I don't know how to do that yet.  So I am
just defining an eval for each function arity. 

Now we are only missing an notion of catching exceptions or returning a
result that works with RetVal

> ecatch (Result x) handler = x
> ecatch (Exception x) handler = handler x

> ecatch' (Result x) handler = Result x
> ecatch' (Exception x) handler = handler x

The reason for the two notions of ecatch is that we want to handle the
case when a function both catches and throws exceptions as well as the
case where the function catches an exception and returns a value.  
The reason the function is called ecatch is that the "catch" name is owned
by IO.

And, we can use foo like this:

> goo' x y = res `ecatch` handler
>  where 
>    res= eval1 show (eval3 addThree (ereturn 1.0) (foo x y) (foo 1 y))
>    addThree x y z = x + y + z
>    handler (InfiniteBlahException x y) =  "Your x arg was bad"
>    handler (SomeOtherException) = "live with it"

Does this make sense?  Does it violate laziness in some subtle way?  Is
there a better way to represent this syntactically?  How do I generalize
eval?  Is there a way to get Monad to work with a two parameter datatype?

-Alex-
___________________________________________________________________
S. Alexander Jacobson                   i2x Media  
1-212-697-0184 voice                    1-212-697-1427 fax







Reply via email to