On 11-Jun-1998, Karlsson Kent - keka <[EMAIL PROTECTED]> wrote:
> ...
> > Then the code which draws the graph just needs to do some pattern
> > matching on the `MaybeException y' at the appropriate place:
> > 
> >     plot_point x (OK y) = ...
> >     plot_point x (GotException exception) = ...
> 
> Better than the IO monad suggestion, but it still partially misses my
> point.  And quite a big portion of it.
> 
> 1. One part that is missed is that there still may be a value to
> consider, in this example, to plot (assuming that the computations of
> the 'y's went on uninterrupted!).  At least in this example you loose
> the value.

No, I didn't miss that.  If you got an exception, there can't be a
value to plot, there's only an exception to plot.  The computation of
the *other* ys may go on uninterrupted, but for the y that got an
exception, the value returned *is* the NDSet of exceptions.

If you want to think IEEE float, an exception corresponds to a NaN.

> 2. Related to 1, is that it may be quite reasonable to let the
> computation, of "a y" here, to go on and produce a value (too!).  In
> IEEE f.p., this value would often, but not always, come out as +0, -0,
> +inf, -inf, or NaN; or indeed *any other (f.p.) value*  (e.g.
> arctan(1/0), which is arctan(+inf), which is pi/2; the computation can
> go on, remembering that there was a divide-by-zero).  The continuation
> values (for IEEE floating point) have been carefully chosen so that the
> result can be reasonable, though often, but not always, inexact.  The
> main point here is that the (f.p.) result *need not* be a NaN, even if
> an exception occurred!

Infinities are probably best treated as a seperate issue. 
That is, infinities should not correspond to exceptions.
If you have a type which supports infinities, then 1/0 should return
infinity, not raise an exception.  Conversely, if you want 1/0 to not
raise an exception, then your type should support infinities.

Regarding operations which can return a non-NaN result given NaN arguments,
normally this is taken care of by laziness.  If a function is passed an
argument such as 0/0 whose evaluation would cause an exception to be thrown,
but the function doesn't evaluate the argument, then it won't get the
exception.

If there are cases where you want a function to return a non-NaN result
given NaN arguments even when it does evaluate those arguments, then
you can use an explicit `catch' inside that function.

For example, suppose you want a version of integer multiplication
that returns zero if the second argument is zero, even if the
first argument throws an exception.
Then you can write this fairly easily:

        times x y | ndset_catch x == OK 0  = 0
                  | ndset_catch y == OK 0  = 0
                  | otherwise              = x * y

[Probably `ndset_catch' is not a great name -- plain `catch' would
be better.  But we can argue about the exact names later.]

Now, you could argue that this behaviour should be the
default behaviour of integer '*', but I would be inclined
to disagree, on efficiency grounds.  The version using catch
is likely to be less efficient than the default version.
And even then, it won't avoid problems if evaluation of the
first argument doesn't terminate.

> 3. During the course of continued evaluation other exceptions may occur
> as well, but in the 'NDset' scheme you can have only one of them,
> allegedly non-deterministically chosen.

Yes.  This is for efficiency and ease of implementation reasons.

Floating point has only a finite set of possible exceptions,
and hardware support, which makes things a bit different.

> Sorry for concentrating so much on floating point, but for floating
> point there is a widely accepted specification on how to handle the
> exceptions that may occur.

Well, the IEEE floating point specification sucks rocks.
The rules for NaNs and signed zeros fail to obey basic laws of mathematics.

For example, in C with IEEE floating point, assuming if I write
        
        #define TIMES(x, y) (((x) == 0 || (y) == 0) ? 0 : __mult(x, y))

then TIMES(NaN, 0) returns 0, but if I merely invert the sense
of the condition and write

        #define TIMES(x, y) (((x) != 0 && (y) != 0 ? __mult(x, y) : 0)

then TIMES(NaN, 0) returns __mult(NaN, 0).

This is sort of defensible, 

Also 0 == -0, but for certain functions f(0) != f(-0).

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


Reply via email to