mixedtypesnum
Alternative Prelude with numeric and logic expressions typed bottomup
https://github.com/michalkonecny/mixedtypesnum#readme
LTS Haskell 19.8:  0.5.9.1 
Stackage Nightly 20220520:  0.5.9.1 
Latest on Hackage:  0.5.9.1 
mixedtypesnum0.5.9.1@sha256:dcc59957090fcbe3560bc6d729f653b967b69778d741228ad14a50d7fb63895c,3257
Module documentation for 0.5.9.1
 Data
 Data.Convertible
 Data.Convertible.Base
 Data.Convertible.Instances
 Data.Convertible.Utils
 Data.Convertible
 MixedTypesNumPrelude
 Numeric
 Numeric.MixedTypes
 Numeric.MixedTypes.AddSub
 Numeric.MixedTypes.Bool
 Numeric.MixedTypes.Complex
 Numeric.MixedTypes.Div
 Numeric.MixedTypes.Elementary
 Numeric.MixedTypes.Eq
 Numeric.MixedTypes.Field
 Numeric.MixedTypes.Literals
 Numeric.MixedTypes.MinMaxAbs
 Numeric.MixedTypes.Mul
 Numeric.MixedTypes.Ord
 Numeric.MixedTypes.Power
 Numeric.MixedTypes.PreludeHiding
 Numeric.MixedTypes.Reduce
 Numeric.MixedTypes.Ring
 Numeric.MixedTypes.Round
 Numeric.OrdGenericBool
 Numeric.MixedTypes
 Utils
 Utils.TH
 Utils.Test
mixedtypesnum
This package provides a version of Prelude where
unary and binary operations such as not
, +
, ==
have their result type derived from the parameter type(s)
and thus supports mixedtype arithmetic and comparisons such as:
> a = [1..10]; b = [1..11]
> length a > 2^((length b)/3)
{?(prec 36): CertainFalse}
Partial operations such as division, sqrt and power do not throw exceptions even when errors such as division by zero occur. Instead, these errors are propagated bottomup in a bespoke erroraccumulating functor from package collecterrors.
This library (as well as collecterrors) arose while developing the AERN2 library for interval and exact real computation. Certain aspects are specifically tailored for interval or exact real arithmetics, including threevalued numerical comparisons and distinguishing potential and certain errors.
API documentation available on the Hackage page.
Table of contents
1. Examples
To replicate the examples included below, start ghci as follows:
$ stack ghci mixedtypesnum:lib noload ghcioptions MixedTypesNumPrelude
*MixedTypesNumPrelude>
1.1. Main idea
Literals have a fixed type:
...> :t 1
... Integer
...> :t 1.0
... Rational
...> 1 :: Rational
... Couldn't match type ‘Integer’ with ‘GHC.Real.Ratio Integer’ ...
Operations permit operands of mixed types, types inferred bottomup:
...> :t 1/2
... :: Rational
...> :t 1.5 * (length [[]])
... :: Rational
1.2. Dealing with numerical errors
To avoid runtime exceptions, it is recommended to use the CN errorcollecting wrapper from package collecterrors.
All arithmetic operations have been extended so that it is possible to have expressions that operate exclusively on CNwrapped types:
...> f (n :: CN Integer) = 1/(1/(n1) + 1/n) :: CN Rational
...> f (cn 0)
{{ERROR: division by 0}}
...> f (cn 1)
{{ERROR: division by 0}}
...> f (cn 2)
2 % 3
Note that the errors printed above are not exceptions, but special values. See the collecterrors documentation for more details.
1.3. The generalised power operator
...> :t 2^(2)
... :: Rational
...> :t 2^2
... :: Rational
...> :t round (2^2)
... :: Integer
...> :t (double 2)^(1/2)
... :: Double
The following examples require also package aern2real. To get access to this via stack, you can start ghci eg as follows:
$ stack ghci aern2real:lib noload ghcioptions AERN2.Real
AERN2.Real> import MixedTypesNumPrelude
...> :t pi
... :: CReal
...> :t sqrt 2
... :: CReal
...> :t 2^(1/2)
... :: CReal
1.4. Undecided comparisons
Comparisons involving intervals are undecided when the intervals overlap:
> pi10 = pi ? (bits 10)
> pi10
[3.1416015625 ± ~9.7656e4 ~2^(10)]
> pi10 > 0
CertainTrue
> pi10 == pi10
TrueOrFalse
The above equality cannot be decided since pi10
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.
Comparison involving real numbers are semidecidable. The result of such a comparison is a lazy Kleenean, ie an infinite sequence of Kleeneans. Please see package aern2real for further details.
1.5. Fuzzy ifthenelse
This package generalises the Haskell ifthenelse statement so that it admits Kleenean and lazy Kleenean conditions:
...> abs1 x = max 0 (if x < 0 then x else x)
...> abs1 (pi10  pi10)
[0.0009765625 ± ~9.7656e4 ~2^(10)]
Although the condition x < 0
cannot be decided for the interval
pi10pi10 = [0 ± ~1.9531e3 ~2^(9)]
, the ifthenelse statement is resolved by computing both branches and unifying the resulting intervals. This makes sense only if both branches compute the same number whenever the condition cannot be decided, ie when x = 0
in this case, making the function continuous.
If we try to define a discontinuous function this way, we get an error as soon as it is detected:
...> bad1 x = if x < 0 then 1x else x
...> bad1 (pi10  pi10)
[0.5 ± ~0.5020 ~2^(1)]{{ERROR: numeric error: union of enclosures: not enclosing the same value}}
The generalised ifthenelse works also for real numbers with lazy Kleenean comparisons:
...> abs1 (pi  pi)
{?(prec 36): [0.00000000001455191522836685... ± ~1.4552e11 ~2^(36)]}
2. Type classes
Mixedtype arithmetic operations are provided via multiparameter type classes and the result type is given by associated type families. For example:
(+) :: (CanAddAsymmetric t1 t2) => t1 > t2 > AddType t1 t2
The constraint CanAdd t1 t2
is a shortcut for both
CanAddAsymmetric t1 t2
and CanAddAsymmetric t2 t1
.
For convenience there are other aggregate type constraints such as
CanAddThis t1 t2
, which implies that the result is of type t1
,
and CanAddSameType t
, which is a shortcut for CanAddThis t t
.
Notably, there are convenience classes Ring
and Field
as well as OrderedRing
and OrderedField
.
For types that instantiate Prelude classes such as Num
, one can
define instances of the new classes using the default implementation, eg:
{# LANGUAGE GeneralizedNewtypeDeriving #}
import MixedTypesPrelude
import qualified Prelude as P
newtype II = II Integer deriving (P.Eq, P.Ord, P.Num)
instance CanAddAsymmetric II II
Conversely, if one defines instances such as CanAddAsymmetric T T
,
one can then trivially define also instances Num T
etc:
instance P.Num T where
(+) = (+)
...
3. Testable specifications
The arithmetic type classes are accompanied by generic hspec test suites, which are specialised to concrete instance types for their testing. These test suites include the expected algebraic properties of operations, such as commutativity and associativity of addition.
4. Limitations

Not all numerical operations are supported yet. Eg
tan
,atan
are missing at the moment. 
Not all Prelude numerical types are supported yet. Eg
Natural
andFloat
are not supported at present, butDouble
is supported. 
Many common operations such as
fromEnum
,threadDelay
give or require anInt
value, which means we sometimes need to convert:threadDelay (int 1000000) integer (fromEnum True)
Prelude functions such as
take
,!!
andlength
that useInt
in Prelude are shadowed in MixedTypesNumPrelude with more compatible/flexible versions. Beware thatData.List.length
clashes withlength
in MixedTypesNumPrelude. 
Inferred types can be very large. Eg for
f a b c = sqrt (a + b * c + 1)
the inferred type is:f :: (CanSqrt (AddType (AddType t2 (MulType t3 t4)) Integer), CanAddAsymmetric (AddType t2 (MulType t3 t4)) Integer, CanAddAsymmetric t2 (MulType t3 t4), CanMulAsymmetric t3 t4) => t2 > t3 > t4 > SqrtType (AddType (AddType t2 (MulType t3 t4)) Integer)
5. Credits
The idea of having numeric expressions in Haskell with types derived bottomup was initially suggested and implemented by Pieter Collins. This version is a fresh rewrite by Michal Konečný.
Changes
mixedtypesnum change log

v 0.5.9 20210804
 compatible with ghc 9.0.1
 separated module Mul from Ring

v 0.5.8 20210602
 add HasRationals to Field

v 0.5.7 20210528
 before: n^m is rational, now: n^m is integer, n^^m is rational

v 0.5.6 20210527
 add instances: mixed min/max Double $t
 add instance: CanGiveUpIfVeryInaccurate Double

v 0.5.5 20210526
 powUsingMulRecip etc with custom multiply and recip operations

v 0.5.4 20210521
 remove Kleeneans (move them to aern2mp)

v 0.5.3 20210515
 export clearPotentialErrors (from collecterrors)

v 0.5.2 20210514
 add OrdGenericBool

v 0.5.1 20210512
 ifthenelse for CNwrapped (see collecterrors) condition
 Documentation now in README

v 0.5.0 20210413
 use package collecterrors with a much simpler CN wrapper
 replace Maybe Bool by Kleenean (a new type)
 remove very long type constraints in specifications using PartialTypeSignatures

v 0.4.1 20210121
 add hasErrorCE and hasErrorCN for testing if CE/CN values contain errors

v 0.4.0.2 20200802
 remove smallcheck version upper bound
 update to cabalversion >= 1.10

v 0.4.0.1 20190411
 fix infinite loop in some conversions

v 0.4.0 20190410
 eliminated dependency on convertible, improving ghcjs compatibility

v 0.3.2 20190108
 added divI and mod
 added enforceRange
 used enforceRange in exp tests

v 0.3.1.5 20181114
 improved documentation

v 0.3.1.4 20171206
 removed upper bounds for dependencies

v 0.3.1.3 20170822
 fixed bound on base in test suite

v 0.3.1.2 20170815
 provided compatible versions of take, drop, length, replicate, splitAt
 added missing mixedtype subtraction combination to Ring etc.

v 0.3.0.1 20170801
 improve package documentation in module MixedTypesNumPrelude

v 0.3 20170801
 renamed the main module to MixedTypesNumPrelude
 much faster compilation
 Ring and Field are now classes, not synonyms for large sets of constraints
 many fixes in collecterror framework and its use in division and power
 Overloaded ifthenelse via XRebindableSyntax
 compiling with ghc 8.2.1

v 0.2.0.1
 fix compilation bug in test suite
 minor doc improvements
 fix Complex instances of errorthrowing division (/!)

v 0.2
 new CollectErrors wrapper
 CN, specialisation of CollectErrors to NumErrors
 numerical partial operators (eg division) return a CN type
 instances for Data.Complex

v 0.1
 first release