Personal tools

Generic number type

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(extracted from the Haskell-Cafe discussion)
 
m
Line 17: Line 17:
 
Floating point numbers are imprecise - a/b*b=a does not hold in general.
 
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.
 
Rationals are precise but pi and sqrt 2 are not rational.
That is, when using GenericNumbers you will encounter exactly the problems
+
That is, when using <hask>GenericNumber<hask>s you will encounter exactly the problems
 
that all scripting language users have encountered so far (or ignored :-).
 
that all scripting language users have encountered so far (or ignored :-).
   

Revision as of 12:16, 20 June 2007

1 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<hask>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
<haskell>
average :: Fractional a => [a] -> a
average xs = sum xs / fromIntegral (length xs)
</haskell>
and you may prefer
<haskell>
average :: [GenericNumber] -> GenericNumber
average xs = sum xs / genericNumberLength xs
</haskell>
with an appropriate implementation of <hask>genericNumberLength
. However, there is already
Data.List.genericLength
and you can write
average :: Fractional a => [a] -> a
average xs = sum xs / genericlength xs

1.1 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


2 See also