collect-errors 0.1.3.0 → 0.1.4.0
raw patch · 7 files changed
+114/−10 lines, 7 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Numeric.CollectErrors: clearPotentialErrors :: CN t -> CN t
+ Numeric.CollectErrors.Type: clearPotentialErrors :: CN t -> CN t
Files
- ChangeLog.md +4/−0
- README.md +89/−3
- collect-errors.cabal +2/−2
- src/Control/CollectErrors/PreludeInstances.hs +3/−2
- src/Numeric/CollectErrors.hs +1/−0
- src/Numeric/CollectErrors/PreludeInstances.hs +3/−2
- src/Numeric/CollectErrors/Type.hs +12/−1
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for collect-errors +## v0.1.4.0++* Add clearPotentialErrors+ ## v0.1.3.0 * Add CanTakeCNErrors type shortcut
README.md view
@@ -6,11 +6,20 @@ The wrapper `CN t` is a special case of `CollectErrors es t` with `es` = `NumErrors`. -The `CN` wrapper also propagates instances of `Floating`,+## Generated API documentation++See the [Hackage page](https://hackage.haskell.org/package/collect-errors).++## Feature highlights++### 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}}@@ -21,6 +30,8 @@ *Prelude> (1/(a-1))+(sqrt (a-2)) NaN +### Error investigation+ Dealing with the errors can be moved outside the expression: *Numeric.CollectErrors> a = 1 :: CN Double@@ -30,6 +41,81 @@ *Numeric.CollectErrors> toEither $ 1/a+(sqrt a) Right 2.0 -The `CN` wrapper has support for **potential errors** so that it can be applied to a set arithmetic such as **interval arithmetic**.+An alternative way to branch based on errors is provided by the function `withErrorOrValue`: -The `Floating` instance cannot be used with a set arithmetic since the instance relies on true/false comparisons but a set arithmetic has only **three-valued (true/false/undecided) comparisons**. Package [mixed-types-num](https://hackage.haskell.org/package/mixed-types-num) provides alternative numerical type classes in which three-valued (ie Kleenean) comparisons are available.+ ...> 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}++### Undecided comparisons++The following examples require the interval arithmetic package [aern2-mp](https://github.com/michalkonecny/aern2) and its dependency [mixed-types-num](https://hackage.haskell.org/package/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](https://hackage.haskell.org/package/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](https://hackage.haskell.org/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}}++### 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)]+
collect-errors.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: bf4e63bbbe03485de658b13bfce7db0642c6c9b74b8c5714f559d7b509c4506f+-- hash: f06d023c94a4b6215cd2d20c3a3ba16388f36ee65e84b34ebfe8a0b0007b9ce8 name: collect-errors-version: 0.1.3.0+version: 0.1.4.0 synopsis: Error monad with a Float instance description: Please see the README on GitHub at <https://github.com/michalkonecny/collect-errors#readme> category: Math
src/Control/CollectErrors/PreludeInstances.hs view
@@ -1,3 +1,4 @@+{-# OPTIONS_GHC -Wno-orphans #-} module Control.CollectErrors.PreludeInstances where import Prelude@@ -54,11 +55,11 @@ liftGotValue :: (CanBeErrors es) => String -> (t1 -> t) -> CollectErrors es t1 -> t liftGotValue _ op (CollectErrors (Just v1) _) = op v1-liftGotValue label op (CollectErrors _ es1) = +liftGotValue label _op (CollectErrors _ es1) = errorMissingValue label es1 liftGotValues2 :: (CanBeErrors es) => String -> (t1 -> t2 -> t) -> CollectErrors es t1 -> CollectErrors es t2 -> t liftGotValues2 _ op (CollectErrors (Just v1) _) (CollectErrors (Just v2) _) = op v1 v2-liftGotValues2 label op (CollectErrors _ es1) (CollectErrors _ es2) = +liftGotValues2 label _op (CollectErrors _ es1) (CollectErrors _ es2) = errorMissingValues label [es1, es2]
src/Numeric/CollectErrors.hs view
@@ -6,6 +6,7 @@ , noValueNumErrorCertain, noValueNumErrorPotential , removeValueErrorCertain, removeValueErrorPotential , prependErrorCertain, prependErrorPotential+, clearPotentialErrors , CanTakeCNErrors -- ** Applicable general collect-error utilities , noValue
src/Numeric/CollectErrors/PreludeInstances.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-}+{-# OPTIONS_GHC -Wno-orphans #-} module Numeric.CollectErrors.PreludeInstances where import Prelude@@ -48,7 +49,7 @@ (a -> Bool) -> (a -> NumError) -> (a -> v) -> CN a -> CN v-liftAcheck check err op aCN@(CollectErrors (Just a) _)+liftAcheck check err _op (CollectErrors (Just a) _) | check a = noValueNumErrorCertain (err a) liftAcheck _ _ op aCN = lift op aCN @@ -69,6 +70,6 @@ (b -> NumError) -> (a -> b -> v) -> CN a -> CN b -> CN v-liftA2checkB checkB errB op a bCN@(CollectErrors (Just b) _)+liftA2checkB checkB errB _op _a (CollectErrors (Just b) _) | checkB b = noValueNumErrorCertain (errB b) liftA2checkB _ _ op a bCN = lift2 op a bCN
src/Numeric/CollectErrors/Type.hs view
@@ -14,7 +14,7 @@ import qualified Data.Set as Set import Control.CollectErrors- ( CanTestErrorsCertain(..), CollectErrors, noValue, removeValue, prependErrors, liftCE, lift2CE, lift1TCE, liftT1CE, unCollectErrors, CanTestErrorsPresent, CanTakeErrors )+ ( CanTestErrorsCertain(..), CollectErrors (CollectErrors), noValue, removeValue, prependErrors, liftCE, lift2CE, lift1TCE, liftT1CE, unCollectErrors, CanTestErrorsPresent, CanTakeErrors ) cn :: v -> CN v cn = pure@@ -79,6 +79,17 @@ prependErrorPotential :: NumError -> CN t -> CN t prependErrorPotential e = prependErrors $ NumErrors $ Set.singleton (e, ErrorPotential)++{-|+ If there is a value, remove any potential errors that are associated with it.+-}+clearPotentialErrors :: CN t -> CN t+clearPotentialErrors (CollectErrors (Just v) (NumErrors es)) =+ CollectErrors (Just v) (NumErrors $ Set.filter notPotential es)+ where+ notPotential (_, ErrorPotential) = False+ notPotential _ = True+clearPotentialErrors ce = ce liftCN :: (a -> (CN c)) -> (CN a) -> (CN c) liftCN = liftCE