[prev in list] [next in list] [prev in thread] [next in thread] 

List:       haskell
Subject:    Re: Floating point
From:       Fergus Henderson <fjh () cs ! mu ! OZ ! AU>
Date:       1998-06-17 23:01:05
Message-ID: 19980618041844.50038 () mundook ! cs ! mu ! OZ ! AU
[Download RAW message or body]

On 15-Jun-1998, S. Alexander Jacobson <alex@i2x.com> wrote:
 | Is it possible to define exception handling to allow continued execution
 | of a function? E.g. define an exception handler to return a value
 | where the exception was thrown rather than at the top level calling
 | function.

This would not be hard to implement, but difficult to find a nice
semantics for.  The problem is that the value of an expression
may now depend on the context in which it is used -- which violates
referential transparency.

P.S.
In fact Haskell's type class system means that the value of
an expression can depend on the expression's type, and for polymorphically
typed expressions that type may depend on the context in which the
expression is used.  (This has in fact been criticised because it does
in a certain sense violate referential transparency.  But referential
transparency is preserved so long as you take the types into account,
and it increases expressiveness, so I'm in favour of it.)
It is therefore possible use Haskell's type classes to provide
an exception handling mechanism that supports resumption
of the failed computation, with the restriction that the
continuation value can depend only on the type and on the
exception (or set of exceptions), not on the context.

I should probably have stopped here, but I got a bit carried away --
so following this is a sample implementation.

>	class Resume t where
>		-- `signal' is used to signal an exception.
>		-- It should either return a continuation value
>		-- or raise that exception.
>		signal :: Exception -> t

Polymorphic operations should then signal exceptions rather than
throwing them; it would be up to the signal method for that type
to decide whether to to actually throw the exception or whether
to instead return a continuation value. 

To avoid the need to mention the `Resume' class too often,
`Resume' should be a superclass of `Num' (just as `Eval' is).

However, I'm not sure how useful this would be, or how well it
would work out in practice.  If exceptions are nondeterministic,
it will be necessary to extend the `Resume' class with
an `NDSet' version of the `signal' method:

>		ndset_signal :: NDSet Exception -> t

This would basically require that either there be some generic continuation
value that is independent of the exception thrown, or that `t'
itself be an NDSet type (or at least contain an NDSet type).

By the way, the default for both of these signal methods would be to
just throw the specified exception(s):

>		signal = throw 
>		ndset_signal = ndset_throw

We can then easily generate instances for all our favourite types
that just throw:

>	instance Resume Int

Defining instances of the `Resume' class that actually resume with
a continuation value rather than throwing could be tedious if done manually,
but this can be generalized.  The idea is to have a generic
type `Resumable' which parameterised not only on the element
type `t' but also on a signal handler type `handler_t'. 
The signal handler type has no data associated with it;
it is just used to select the appropriate signal handler function.

>	data Resumable t handler_t = MkResumable t
>
>	class (SignalHandler t handler_t) where
>		handle_signal :: Exception -> Resumable t handler_t
>		ndset_handle_signal :: NDSet Exception -> Resumable t handler_t
>
>	instance (SignalHandler t handler_t) =>
>	         Resume (Resumable t handler_t) where
>		 	signal = handle_signal
>		 	ndset_signal = ndset_handle_signal

First we need some utility functions for converting
from `t' to `Resumable t handler' and vice versa.

>	resume expr = case (ndset_catch expr) of
>		OK val -> MkResumable val
>		GotException es -> ndset_signal es
>	unresume (MkResumable expr) = expr

Then it's easy to provide generic resumable versions of operators,

>	resume_unop op x = resume (op (unresume x))
>	resume_binop op x y = resume (op (unresume x) (unresume y))
>	resume_binop2 op x y = (resume a, resume b) where
>		(a, b) = op (unresume x) (unresume y)
>	resume_ctor ctor x = resume (ctor x)
>	resume_dtor dtor x = dtor (unresume x)

and to use these to derive instances for the resumable types
given instances for the element type:

>	instance (Eq t, SignalHandler t handler_t) =>
>		Eq (Resumable t handler_t) where
>			x == y = (unresume x) == (unresume y)

>	instance (Text t, SignalHandler t handler_t) =>
>		Text (Resumable t handler_t) where
>			showsPrec prec x = showsPrec prec (unresume x)

>	instance (Num t, SignalHandler t handler_t,
>		Resume (Resumable t handler_t)) =>
>		 Num (Resumable t handler_t) where
>			-- (+) = resume_binop (+)
>			x + y = resume ((unresume x) + (unresume y))
>			(-) = resume_binop (-)
>			(*) = resume_binop (*)
>			negate = resume_unop negate
>			-- signum = resume_unop signum
>			fromInteger = resume_ctor fromInteger

>	instance (Integral t, SignalHandler t handler_t) =>
>		 Integral (Resumable t handler_t) where
>			negate = resume_unop negate
>			quot = resume_binop quot
>			rem = resume_binop rem
>			div = resume_binop div
>			mod = resume_binop mod
>			quotRem = resume_binop2 quotRem
>			divMod = resume_binop2 divMod
>			toInteger = resume_dtor toInteger

S. Alexander Jacobson <alex@i2x.com> continues:

 | If exception handlers were treated as providing "continuation
 | values", then you could implement IEEE or not at the exception handling
 | level. e.g.
 | 
 | > main = do
 | >               foo <- myFun 1 0 `catchFP` max_int `catch` (return "bogus")
 | >               putStr foo
 | > myFun x y = show $ x + (x `div` y)
 |         
 | The output of main would be "1" rather than "bogus". (Bogus would occur in
 | the case of pattern match failure.)

Given the above framework (which can all be tucked away in some library
module), this could be done as follows:

>	data MyIntSignalHandler = MkMyIntSignalHandler
>
>	instance SignalHandler Int MyIntSignalHandler where
>		handle_signal _ = MkResumable maxint
>		ndset_handle_signal _ = MkResumable maxint

>	main = (return (myFun one zero) `catch` (const (return "bogus"))) >>=
>		\ foo -> putStr foo where
>			one, zero :: Resumable Int MyIntSignalHandler
>			one = fromInteger 1
>			zero = fromInteger 0
>	myFun x y = show $ x + (x `div` y)

Note that this stuff uses multi-parameter type classes.
It may not be hard to avoid that -- I didn't really try.
I Gofer to type check this, since we haven't got hugs 1.3c
installed here yet.  But I don't have an implementation for
the exception handling primitives I proposed, so I haven't
actually tested it.  Thus it is quite possible that it may
contain the odd bug or two.

Cheers,
	Fergus.

-- 
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3        |     -- the last words of T. S. Garp.



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic