Dear Kevin Hammond
If you think this flame not too intemperate, please put it out on
the haskell mailing list.
Now that Mark Jones has produced Hugs, which tries harder than Gofer
to keep to Haskell standards, I have begun to revise some of my Gofer
scripts to run with the Haskell standard prelude. It is this exercise that
has provoked me to flame against the standard Haskell prelude, which strikes
me (please excuse my language) as being nothing short of mathematically
illiterate - or at best mathematically narrow-minded.
This is not the first time that I have raised this point, but maybe I
should explain in greater detail.
I know that Haskell has been developped for programmers rather than
mathematicians, but it seems to me a terrible waste of opportunity when the
programming language, which of all programming languages comes closest to
answering a mathematician's prayers, should stumble at the last, quite
minor, hurdle. The prelude as it stands is fine for numerical analysis;
but numerical analysts have got FORTRAN (which they seem unwilling to be
tempted from). It is to pure mathematics, algebra in particular, that
Haskell shows unique promise, and where the standard prelude proves a
disappointment. Remember that a part of the original motivation for
Haskell was to provide a public domain teaching tool. Too many people
are under the impression that the only educational benefit of programming
(for mathematics) is for number-crunching. Haskell is potentially good for
symbol-crunching too, where traditional languages are hopeless.
Suppose, for example, that I want to write a Haskell script to handle
Gaussian integers, complex numbers with integer real and imaginary part,
say to factorize them into irreducibles. This is not an arcane application.
What do I find? PreludeComplex insists that Complex a has to be qualified
by (RealFloat a) -- Aargh! Why, for heaven's sake?
Suppose that I want to use (+) to add vectors, or elements of an abelian
group. Since (+) is a class-variable for Num I have to define (*), abs
and signum as well, so again -- Aargh!. Additive and multiplicative notation
must be split cleanly to exploit overloading properly. This is done with
care for rationals (for / and %) and for floats, so why not get the
fundamentals right first?
The impression I get from the standard prelude is that it was formulated
by programmers totally imbued with the traditional number-crunching culture
of the past, and totally ignorant of the wider applications that a
language like Haskell can offer. I am not knocking number-crunching -
it is a crucial aspect - but the prelude could be formulated so that pure
mathematical users were happy too. It seems daft to make a standard that
excludes them. So for the moment they will have to stick with Gofer.
What would I have? If we have to keep to single parameter classes then
something like:
class Additive a where
(+), (-) :: a -> a -> a
neg :: a -> a
zero :: a
class Multiplicative a where
(*) :: a -> a -> a
one :: a
{- there is an argument for putting (^) :: a -> Int -> a
in here, but properly this should only be used for associative
instances of (*). With multiparameter types one should have
class LeftAction a b where
(*) :: a -> b -> b
instance Additive a => LeftAction Int a where
0 * _ = zero
1 * x = x
n * x | n < 0 = (-n) * (neg x)
| even n= (n `div` 2) * (x + x)
| otherwise = x + (n `div` 2) * (x + x)
-}
class (Additive a, Multiplicative a) => Ring a
data Ring a => Complex a = a :+ a
instance Ring a => Additive (Complex a) where
(x :+ y) + (x' :+ y') = (x+x') :+ (y+y')
(x :+ y) - (x' := y') = (x-x') :+ (y-y')
neg (x :+ y) = (neg x) :+ (neg y)
zero = zero :+ zero
instance Ring a => Multiplicative (Complex a) where
(x :+ y) * (x' :+ y') = (x*x'-y*y') :+ (x*y'+y*x')
unit = unit :+ zero
A basis of this simplicity is what you need, say for algebraic number
fields or for modular arithmetic. Anybody with any feel for maths should
appreciate that (+),(-) and zero must stand by themselves. Certainly
signum and abs should not be bundled with Num --- perhaps with Real,
because they require an ordering.
If Haskell is going to make it big in the world, the prelude has to be
cleaned up now, before inertia and the qwerty-phenomenon freeze it
solid, or else an awful lot of potential users in the mathematical
world are going to laugh it out of their court.
On a different subject, but still flaming about the standard prelude,
is it unfair to ask why zip, zipWith and unzip only stop at zip7, zipWith7
and unzip7 rather than, say, zip137? I can only shake my head in wonder
and suggest some squiggols instead.
-- Gavin Wraith