# collect-errors

This package provides an error collecting mechanism. Using `CN Double`

instead of `Double`

replaces NaNs and infinities with more informative error descriptions.

API documentation available on the Hackage page.

## Table of contents

## 1. Feature highlights

`CollectErrors es t`

is a monad wrapper around values of type `t`

which can accommodate (a list of)
(potential) errors of type `es`

that have (maybe) occurred during the computation
of a value. A value may be missing, leaving only the error(s).

The wrapper `CN t`

is a special case of `CollectErrors es t`

with `es`

= `NumErrors`

.

### 1.1. Error collecting

The `CN`

wrapper propagates instances of `Floating`

,
allowing us to write expressions with partial
functions (ie functions that fail for some inputs) instead of
branching after each application of such function:

```
$ stack ghci collect-errors:lib --no-load --ghci-options Numeric.CollectErrors
*Numeric.CollectErrors> a = 1 :: CN Double
*Numeric.CollectErrors> (1/(a-1))+(sqrt (a-2))
{{ERROR: division by 0; ERROR: out of domain: sqrt for negative arg -1.0}}
```

as opposed to:

```
*Prelude> a = 1 :: Double
*Prelude> (1/(a-1))+(sqrt (a-2))
NaN
```

### 1.2. Error investigation

Dealing with the errors can be moved outside the expression:

```
*Numeric.CollectErrors> a = 1 :: CN Double
*Numeric.CollectErrors> toEither $ 1/(a-1)
Left {ERROR: division by 0}
*Numeric.CollectErrors> toEither $ 1/a+(sqrt a)
Right 2.0
```

An alternative way to branch based on errors is provided by the function `withErrorOrValue`

:

```
...> a = 2 :: CN Double
...> withErrorOrValue (const 0) id (1/a)
0.5
```

The `CN`

wrapper can be forcibly removed as follows:

```
...> :t unCN (1/a)
... :: Double
...> unCN (1/a)
0.5
...> unCN (1/(a-2))
*** Exception: CollectErrors: {ERROR: division by 0}
```

### 1.3. Undecided comparisons

The following examples require the interval arithmetic package aern2-mp and its dependency mixed-types-num:

```
$ stack ghci aern2-mp:lib --no-load --ghci-options AERN2.MP
*AERN2.MP> import MixedTypesNumPrelude
*AERN2.MP MixedTypesNumPrelude>
```

Comparisons involving sets (such as intervals) are undecided when the intervals overlap:

```
...> pi100 = piBallP (prec 100)
...> :t pi100
pi100 :: MPBall
...> pi100
[3.1415926535897932384626433832793333156439620213... ± ~7.8886e-31 ~2^(-100)]
...> 0 < pi100
CertainTrue
...> pi100 == pi100
TrueOrFalse
```

The values `CertainTrue`

and `TrueOrFalse`

are part of the three-valued type `Kleenean`

provided by

mixed-types-num.

The above equality cannot be decided since `pi100`

is not a single number but a set of numbers spanning the interval and the comparison operator cannot tell if the two operands sets represent the same number or a different number.

The Prelude `Floating`

instance for `CN`

cannot be used reliably with interval arithmetic because the instance relies on true/false comparisons:

```
...> import qualified Prelude as P
... P> (cn pi100) P./ (cn pi100 - cn pi100)
*** Exception: Failed to decide equality of MPBalls. If you switch to MixedTypesNumPrelude instead of Prelude, comparison of MPBalls returns Kleenean instead of Bool.
```

Using its Kleenean comparisons, package mixed-types-num provides alternative numerical type classes in which errors are detected and collected correctly when using the `CN`

wrapper:

```
...> (cn pi100) / (cn pi100 - cn pi100)
{{POTENTIAL ERROR: division by 0}}
```

### 1.4. Potential errors

As we see in the above example, the `CN`

wrapper supports potential errors that sometimes arise as a consequence of undecided comparisons.

When an error is present (which can be checked using `hasError`

), the function `hasCertainError`

can be used to further distinguish cases where the error is certain or potential:

```
...> import qualified Numeric.CollectErrors as CN
...> CN.hasCertainError $ (cn pi100) / (cn 0)
True
...> CN.hasCertainError $ (cn pi100) / (cn pi100 - cn pi100)
False
```

Sometimes the potential errors are *harmless*:

```
...> sqrt (cn pi100 - cn pi100)
[0.0000000000000006280369834735100420368561502297... ± ~6.2804e-16 ~2^(-50)]{{POTENTIAL ERROR: out of domain: negative sqrt argument}}
```

Such harmless potential errors can be ignored using `clearPotentialErrors`

:

```
...> clearPotentialErrors $ sqrt (cn pi100 - cn pi100)
[0.0000000000000006280369834735100420368561502297... ± ~6.2804e-16 ~2^(-50)]
```