Exception handling in numeric computations (was Re: [Haskell-cafe] Use unsafePerformIO to catch Exception?)

Xiao-Yong Jin xj2106 at columbia.edu
Tue Mar 24 12:27:33 EDT 2009


Henning Thielemann <lemming at henning-thielemann.de> writes:

> Try to never use exception handling for catching programming errors!
> Division by zero is undefined, thus a programming error when it
> occurs.
>  http://www.haskell.org/haskellwiki/Error
>  http://www.haskell.org/haskellwiki/Exception
>   I'm afraid, a Maybe or Either or Exceptional (see explicit-exception
> package) return value is the only way to handle exceptional return
> values properly. Maybe in the larger context of your problem zero
> denominators can be avoided? Then go this way.

Using div is just an example I'm testing with what I read in
the book Real World Haskell.  The real thing I'm trying to
do is inverting a matrix.  Say, I want to write

> invMat :: Matrix -> Matrix

You won't be able to invert all the matrix, mathematically.
And computationally, even a larger set of matrix might fail
to be inverted because of the finite precision.  It is
relatively easier and more efficient to spot such a problem
within this 'invMat' function.  Because testing the
singularity of a matrix is equally hard as invert it.  So
all I can do when 'invMat' spot a singular matrix are

  a) Return Either/Maybe to signal an error.
  b) Wrap it in a monad.
  c) Define a dynamic exception and throw it.

The problem is that there will be many functions using such
a function to invert a matrix, making this inversion
function return Either/Maybe or packing it in a monad is
just a big headache.  It is impractical to use method (a),
because not every function that uses 'invMat' knows how to
deal with 'invMat' not giving an answer.  So we need to use
method (b), to use monad to parse our matrix around.

> invMat :: Matrix -> NumericCancerMonad Matrix

It hides the exceptional nature of numerical computations
very well, but it is cancer in the code.  Whenever any
function wants to use invMat, it is mutated.  This is just
madness.  You don't want to make all the code to be monadic
just because of singularities in numeric calculation.
Therefore, in my opinion, method (c) is my only option.  And
because I don't always want to deal with such problem in the
IO monad, I create this beast 'unsafePerformIO . try
. evaluate' to convert some potential disastrous result to
'Either Disaster Result'.

You might argue that Haskell actually deals with such
numerical problem with 'NaN', 'Infinite'.  But, some
numerical operations behave weird with these special values,
and using 'isNan', 'isInfinite' alike is just another big
mess.  They are going to be all over the place and not
actually better than 'case ... of' and 'fromMaybe'.

I can't really think of another option for this kind of
situation, apart from letting my code to be infected by
NumericCancerMonad.

If anyone on this list has some thoughts on this matter,
please share them.  Many thanks.
-- 
    c/*    __o/*
    <\     * (__
    */\      <


More information about the Haskell-Cafe mailing list