I've been recently working on yet another Haskell server, and when
testing its safety limits on numerical examples, I realized that
there are several quite annoying problems with Haskell numerical
types and classes.
+ Many library functions, which operate on Int, return value 0
or faulty negative values on overflow condition. Since there
is no exception mechanism built into Haskell, such functions
do not have much choice here: they can either return faulty
values or cause runtime errors. The latter solution might
be acceptable in Hugs, where recovery is smooth, but could
not be accepted in other environments, such as a multiuser
server - where one user could bring down the entire server
by sending a bad request.
+ Hugs is supplied with the type Number. According to its
author: "Fixed width integers with overflow detection.. Unlike
Int, overflows are detected and cause a run-time error...
A fairly messy hack, but at least it works...".
My comment is that the chosen name "Number" is here a very
unfortunate choice: it suggests generality, but in fact this
is just only a better Int. But an error is still an error, and
if a recovery mechanism does not exist then how this type
could help us with our daily programming?
+ Haskell extension libraries define a bunch of Word types.
An attempt to show that Haskell can be numerically efficient?
+ Although Prelude numeric classes are well designed from point
of view of classification, the abundance of numeric classes
makes them difficult to use - once the benefit of a silent
coercion by Hugs has been removed: suddenly a user learns that
only certain special classes know how to divide, take logarithms
or do the trigonometry.
+ Yet again - as it has happened with other languages, someone
has decided that complex numbers are somehow not as important
as other numbers. At least Haskell acknowledges that they exist,
yet they are relegated to a periferial library - an icing on
a cake.
But anyone working in physics or engineering would tell you
otherwise: complex numbers are very important in daily usage.
However, class Complex in Haskell is tied up to Floating numbers,
just beacuse other classes, such as Rational, do not know how to
take square roots or deal with trigonometry, exponentials or
hyperbolics.
But where on Earth it is said that the domain of Complex numbers
should be restricted to Float or Double? Why not Rationals as
well?
For true mathematicians the main numbers of interest are Naturals,
Integers, Rationals, Irrationals and Complex - classes Int, Float
and Double are the artifacts of Computer Science and we use them
only because they are cheap to implement. But if they stand in a way
of progress their role should be revised, don't you think? Especially
in modern languages, such as Haskell claims to be?
Why is it that Maple, Matlab, Mathematica and other bunch of
symbolic languages and also the calculators from the field of number
theory can handle numbers smoothly without pestering the user with
the messages of the kind "Int is not an instance of class 'Fractional'"?
Are we becoming the slaves of the type system that you guys invented
for some other, otherwise undoubtely important, reasons?
I sometimes wonder what are benefits of types when weighing their pros
and contras. And then good all Smalltalk comes often to mind, where
an overflow on SmallInteger operation silently causes a coercion to
LargePositiveInteger (or LargeNegativeInteger) and processing
continues on large integers until a number can be again coerced
back to the efficient SmallInteger.
I am not a number theorist, my background is in mathematical physics,
and because of that I have a somehow skewed view of this issues:
more practical than theoretical. I do not claim that I know how
the numbers should be organized in Haskell, I am just pointing out
that "something is wrong in the Kingdom of Denmark". But I am sure
there are plenty of Haskell afficionados with their background in
Number Theory that could made a serious revision of numbers in Haskell.
Time is probably right - considering that Haskell 2 is in its
incubations stage.
My point is that the current number system in Haskell is inadequate
for many applications, and severily impaired in Hugs implementation,
where Double as such does not exist and is just aliased to Float.
But if you ever tried to solve a simple example of a loaded beam with
its two ends fixed, then you will end up with hyperbolics as base
vectors - and for this Float is completely useless here because of
its limited accuracy, and Double is often not accurate enough.
What we need is an exact rational arithmetic - be it via linear maps,
continued fractions, or Mobius transformations.
I just want to point out that some serious papers on modern number
representation exist. You do not have to look very far: see for
example works of Peter John Potts and Abbas Edalat from Imperial
College, UK. Protoptypes in C++, Java and Miranda also exist.
If I am not mistaken, Simon Peyton Jones was playing with an idea of
continued fractions some time ago too. Valerie Menissier-Morain from
INRIA has published few interesting papers too, and Ocaml is using
its BigNumber type system based on some of her works.
Ideally, complex numbers should be incorporated somehow into
basic numbers. I know, some purists would disagree, because
Complex does not fit well into Ord class, but for me this is
just a question of a definition or reorganization of classes.
You can still somehow compare complex numbers - be it via
magnitude or some other means. I would love to see sqrt (-1)
producing complex answer, and not failing on me. I would like
to have logaritms of negative numbers, arcsin's of x > 1, etc., etc.
If one can put an efficiency or storage issues aside, some
applications would greatly benefit from Rational arithmetic that
would do be able to handle square roots, logarithms, and such.
This is perfectly doable! My experiments with my yet another
Haskell server proved that the safety issue is paramount in
such an environment, while an efficiency is not as important.
In such an environment a Rational aritmetic, which is based
on continued fractions, is perfectly adequate, reasonably
fast, and first of all - safe! I am working on it now, and I
am willing to publish it, when done, for the benefits of others.
And I am sure that much better solutions - better than continued
fractions, to the efficiency of Rational algebra exist. This is
an appeal to Haskell community: let's do something about the
shortcomings of the numerical status quo, or prove to me that
I am very wrong on this issue!
Jan Skibinski,
Numeric Quest Inc.,
Huntsville, Ontario, Canada