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 :-).
A2 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
Categories: FAQ | Mathematics | Idioms
