Generic number type
Problem
Question:
Can I have a generic numeric data type in Haskell which covers Integer
, Rational
, Double
and so on, like it is done in scripting languages like Perl and MatLab?
Answer: In principle you can define a type like
data GenericNumber =
Integer Integer
| Rational Rational
| Double Double
and define appropriate instances for Num
class et. al.
However you will find that it is difficult to implement these methods in a way that is appropriate for each use case.
There is simply no type that can emulate the others.
Floating point numbers are imprecise - a/b*b=a does not hold in general.
Rationals are precise but pi and sqrt 2 are not rational.
That is, when using GenericNumber
s you will encounter exactly the problems
that all scripting language users have encountered so far (or ignored :-).
Solutions
It is strongly advised to carefully check whether a GenericNumber is indeed useful for your application. So let's revisit some examples and their idiomatic solutions in plain Haskell 98.
average
You may find it cumbersome to write
average :: Fractional a => [a] -> a
average xs = sum xs / fromIntegral (length xs)
and you may prefer
average :: [GenericNumber] -> GenericNumber
average xs = sum xs / genericNumberLength xs
with an appropriate implementation of genericNumberLength
.
However, there is already Data.List.genericLength
and you can write
average :: Fractional a => [a] -> a
average xs = sum xs / genericlength xs
ratios
You find it easy to write
1 / 3 :: Rational
but uncomfortable that
1 / floor pi :: Rational
does not work.
The first example works, because the numeric literals 1
and 3
are interpreted as rationals itself.
The second example fails, because floor
always returns an Integral
number type, where Rational
is not an instance.
You should use %
instead. This constructs a fraction out of two integers:
1 % 3 :: Rational
1 % floor pi :: Rational
See also
- Suggestions for implementing a generic number type: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html