Home
Reading
Searching
Subscribe
Sponsors
Statistics
Posting
Contact
Spam
Lists
Links
About
Hosting
Filtering
Features Download
Marketing
Archives
FAQ
Blog
 
Gmane
From: Gregory Crosswhite <gcross <at> phys.washington.edu>
Subject: ANNOUNCE: error-message
Newsgroups: gmane.comp.lang.haskell.cafe
Date: Thursday 3rd December 2009 21:50:06 UTC (over 6 years ago)
If there is one thing that we really don't have enough of in Haskell, it is
*ways to handle errors*!  Thus, I am pleased to announce the release of the
"error-message" package to help in filling this, erm, gap.

This philosophy behind this package is that it is often better to find out
all of the errors that have occured in a computation and report them
simultaneously, rather than aborting as soon as the first error is
encountered.  Towards this end, this package supplies a type of /combinable
error messages/ (ErrorMessage in the Data.ErrorMessage module) so that all
of the errors from subcomputations can be gathered and presented together.

The following provides an example of how these can be used:

==================================================
   sqrtWithError :: Float -> Either ErrorMessage Float
   sqrtWithError x
        | x < 0
            = leftErrorMessageText
                ("Error computing the square root of " ++ (show x) ++ ":")
                "Square roots cannot be taken of negative numbers."
        | otherwise
            = Right (sqrt x)

    sumWithError :: Either ErrorMessage Float -> Either ErrorMessage Float
-> Either ErrorMessage Float
    sumWithError (Left error1) (Left error2) = Left (error1 `mappend`
error2)
    sumWithError (Left error) _ = Left error
    sumWithError _ (Left error) = Left error
    sumWithError (Right value1) (Right value2) = Right (value1 + value2)

    showSumOrErrorOf :: Float -> Float -> String
    showSumOrErrorOf x y =
        case sumWithError (sqrtWithError x) (sqrtWithError y) of
            Right value -> "The value is " ++ show value
            Left error -> show . formatErrorMessage $ error
==================================================

The result of @showSumOrErrorOf (-1) (-2)@ is the string,

    Error computing the square root of -1:
        Square roots cannot be taken of negative numbers.
    Error computing the square root of -2:
        Square roots cannot be taken of negative numbers.

whereas the result of @showSumOrErrorOf (-1) (-1)@ is the string,

    Error computing the square root of -1:
        Square roots cannot be taken of negative numbers.

Note how the error message only appears once;  this is because the process
of combining the error messages automatically eliminates all identical
headings under the assumption that they came from the same original
computation, as was the case here.

Currently, the definition of @sumWithError@ is largely boilerplate. 
Happily, the Haskell community has done a lot of work to identify patterns
such as these and to write libraries that allow us to express them
concisely.  In particular, a standard trick when working with errors like
this is to express the calculation as a 'Monad', such as by using the
following definition:

==================================================
    sumWithError_2 argument1 argument2 = do
        value1 <- argument1
        value2 <- argument2
        return (value1 + value2)
==================================================

Or, even more concisely:

==================================================
    sumWithError_3 = liftM2 (+)
==================================================

Unfortunately though, neither of these definitions have the same semantics
as the original @sumWithError@, as using both we get the following error
message for @showSumOrErrorOf (-1) (-2)@:

==================================================
    Error computing the square root of -1:
        Square roots cannot be taken of negative numbers.
==================================================

That is, we have lost the second of the two error messages.  The reason for
this is that 'Monad'-style error processing expresses the computation as a
sequence, and gives up as soon as it sees any error.  In this case of
@sumWithError@, however, the evaluation of the second argument can proceed
even if there was an error in the first argument.  Thus, rather than using
a 'Monad' pattern, we use an 'Applicative' pattern:

==================================================
    sumWithError_4 = liftA2 (+)
==================================================

Now both error messages are displayed.

Anyway, I hope that someone other than myself finds this pattern to be
helpful.  :-)

Cheers,
Greg
 
CD: 3ms