# Generic number type

### From HaskellWiki

(link to Converting numbers) |
(added isSquare example) |
||

Line 20: | Line 20: | ||

that all scripting language users have encountered so far (or ignored :-). | that all scripting language users have encountered so far (or ignored :-). | ||

+ | A <hask>GenericNumber</hask> 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 == | == Solutions == | ||

Line 42: | Line 43: | ||

<haskell> | <haskell> | ||

average :: Fractional a => [a] -> a | average :: Fractional a => [a] -> a | ||

− | average xs = sum xs / | + | average xs = sum xs / genericLength xs |

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

Line 64: | Line 65: | ||

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

+ | === isSquare === | ||

+ | |||

+ | It may seem irksome that <hask>fromIntegral</hask> is required in the function | ||

+ | <haskell> | ||

+ | isSquare :: (Integral a) => a -> Bool | ||

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

+ | </haskell> | ||

+ | With a <hask>GenericNumber</hask> type, one could instead write | ||

+ | <haskell> | ||

+ | isSquare :: GenericNumber -> Bool | ||

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

+ | </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. | ||

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

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

## Contents |

## 1 Problem

Question:

Can I have a generic numeric data type in Haskell which coversAnswer: In principle you can define a type like

data GenericNumber = Integer Integer | Rational Rational | Double Double

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 usingthat all scripting language users have encountered so far (or ignored :-).

A## 2 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.

### 2.1 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

average :: Fractional a => [a] -> a average xs = sum xs / genericLength xs

### 2.2 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 literals1 % 3 :: Rational 1 % floor pi :: Rational

### 2.3 isSquare

It may seem irksome thatisSquare :: (Integral a) => a -> Bool isSquare n = (floor . sqrt $ fromIntegral n) ^ 2 == n

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

## 3 See also

- Suggestions for implementing a generic number type: http://www.haskell.org/pipermail/haskell-cafe/2007-June/027092.html