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

Daniel Yokomizo daniel.yokomizo at gmail.com
Tue Mar 24 13:16:24 EDT 2009


On Tue, Mar 24, 2009 at 1:27 PM, Xiao-Yong Jin <xj2106 at columbia.edu> wrote:
> 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.

In general if a function is partial we can either make it total by
extending its range or restricting its domain. Also we can signal it
using runtime or compile-time mechanisms. Options a & b are equivalent
(i.e. extend the range, compile-time notification) and option c is
also another way of extending the range, but using runtime
notification.

If we try the other approach, we need to express the totality of
invMat by restricting its domain, so we can add, for example, a
phantom type to Matrix to signal it is invertible. As you need to
construct the Matrix before trying to invert it you can always make
the constructors smart enough to bundle the Matrix with such
properties. Of course there's need to do some runtime verifications
earlier, but the clients of invMat are required to do the verification
earlier or pass it to their clients (up to the level that can handle
with this issue):

data Invertible
tryInvertible :: Matrix a -> Maybe (Matrix Invertible)
invMat :: Matrix Invertible -> Matrix Invertible


You could use different forms of evidence (e.g. phantom types, type
classes) but the idea is the same.

> 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/*
>    <\     * (__
>    */\      <
>

Best regards,
Daniel Yokomizo


More information about the Haskell-Cafe mailing list