# Difference between revisions of "Generic number type"

(added isSquare example) |
m |
||

Line 77: | Line 77: | ||

isSquare n = (floor . sqrt $ n) ^ 2 == n |
isSquare n = (floor . sqrt $ n) ^ 2 == n |
||

</haskell> |
</haskell> |
||

− | but there is a subtle problem here: if the input happens to be represented internally by a non-integral type, this function will probably not work properly. This could be fixed by wrapping all occurrences of <hask>n</hask> by calls to <hask>round</hask>, but that's no easier (and less type-safe) than just including the call to <hask>fromIntegral</hask> in the first place. The point is that by using GenericNumber here, all opportunities for the type checker to warn you of problems is lost; now you, the programmer, must ensure that the underlying numeric types are always used correctly, which is made even harder by the fact that you can't inspect them. |
+ | but there is a subtle problem here: if the input happens to be represented internally by a non-integral type, this function will probably not work properly. This could be fixed by wrapping all occurrences of <hask>n</hask> by calls to <hask>round</hask>, but that's no easier (and less type-safe) than just including the call to <hask>fromIntegral</hask> in the first place. The point is that by using <hask>GenericNumber</hask> here, all opportunities for the type checker to warn you of problems is lost; now you, the programmer, must ensure that the underlying numeric types are always used correctly, which is made even harder by the fact that you can't inspect them. |

== See also == |
== See also == |
||

− | * |
+ | * The discussion on haskell-cafe which provided the impetus for this page: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html |

[[Category:FAQ]] |
[[Category:FAQ]] |

## Revision as of 16:11, 20 June 2007

## 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 :-).

A `GenericNumber`

type would also negate the type safety that strongly typed numbers provide, putting the burden back on the programmer to make sure they are using numbers in a type-safe way. This can lead to subtle and hard-to-find bugs, for example, if some code ends up comparing two floating-point values for equality (usually a bad idea) without the programmer realizing it.

## 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 manually convert integers to fractional number types like in

```
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
```

### isSquare

It may seem irksome that `fromIntegral`

is required in the function

```
isSquare :: (Integral a) => a -> Bool
isSquare n = (floor . sqrt $ fromIntegral n) ^ 2 == n
```

With a `GenericNumber`

type, one could instead write

```
isSquare :: GenericNumber -> Bool
isSquare n = (floor . sqrt $ n) ^ 2 == n
```

but there is a subtle problem here: if the input happens to be represented internally by a non-integral type, this function will probably not work properly. This could be fixed by wrapping all occurrences of `n`

by calls to `round`

, but that's no easier (and less type-safe) than just including the call to `fromIntegral`

in the first place. The point is that by using `GenericNumber`

here, all opportunities for the type checker to warn you of problems is lost; now you, the programmer, must ensure that the underlying numeric types are always used correctly, which is made even harder by the fact that you can't inspect them.

## See also

- The discussion on haskell-cafe which provided the impetus for this page: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html