
Patrik Jansson wrote:
[I am not sure a more mathematically correct numeric class system is suitable for inclusion in the language specification of Haskell (a library would certainly be useful though)....]
I think it should be done at the language level. Previously Brian Boutel wrote: ...
Haskell was intended for use by programmers who may not be mathematicians, as a general purpose language. Changes to make keep mathematicians happy tend to make it less understandable and attractive to everyone else.
Specifically:
* most usage of (+), (-), (*) is on numbers which support all of them.
* Haskell equality is a defined operation, not a primitive, and may not be decidable. It does not always define equivalence classes, because a==a may be Bottom, so what's the problem? It would be a problem, though, to have to explain to a beginner why they can't print the result of a computation.
==== Some people here might recall that I cried loudly and in despair (OK, I am exaggerating a bit...) about the inadequacy of the Num hierarchy much before Sergey Mechveliani's proposal. Finally I implemented my own home-brewed hierarchy of Rings, AdditiveGroups, Modules, etc. in order to play with differential structures and graphical objects. And arithmetic on functions. I AM NOT A MATHEMATICIAN, and still, I see very strongly the need for a sane math layer in Haskell on behalf of 'general purpose' programming. Trying to explain to comp. sci students (who, at least here, don't like formal mathematics too much...) WHY the Haskell Num hierarchy is as it is, is simply hopeless, because some historical accidents were never very rational. * I don't care about "most usage of (+), (-), (*) is on numbers which support all of them" if this produces a chaos if you want to use Haskell for geometry, or graphics, needing vectors.
From this point of view a slightly simpler (in this context) type system of Clean seems to be better. And I appreciate also the possibility to define arithmetic operations on *functions*, which is impossible in Haskell because of this Eq/Show superclass constraints.
In the uncoupled case the users have the choice to define Eq and Show instances that make sense to them. A library designer could provide the Eq and Show instances in two separate modules to give the users maximum flexibility.
/Patrik Jansson
Yes. I don't want to be too acrimonious nor sarcastic, but those people who claim that Haskell as a "universal" language should not follow too closely a decent mathematical discipline, serve the devil. When math is taught at school at the elementary level, with full knowledge of the fact that almost nobody will follow the mathematical career afterwards, the rational, logical side of all constructions is methodologically essential. 10 years old pupils learn that you can add two dollars to 7 dollars, but multiplying dollars has not too much sense (a priori), and adding dollars to watermelons is dubious. Numbers are delicate abstractions, and treating them in a cavalière manner in a supposedly "universal" language, harms not only mathematicians. As you see, treating (*) together with (+) is silly not only to vector spaces, but also for dimensional quantities, useful outside math (if only for debugging). "Ch. A. Herrmann" wrote:
the problem is that the --majority, I suppose?-- of mathematicians tend to overload operators. They use "*" for matrix-matrix multiplication as well as for matrix-vector multiplication etc.
Therefore, a quick solution that implements groups, monoids, Abelian groups, rings, Euclidean rings, fields, etc. will not be sufficient.
I don't think that it is acceptable for a language like Haskell to permit the user to overload predefined operators, like "*".
Wha do you mean "predefined" operators? Predefined where? Forbid what? Using the standard notation even to multiply rationals or complexes? And leave this possibility open to C++ programmers who can overload anything without respecting mathematical congruity? Why? A serious mathematician who sees the signature (*) :: a -> a -> a won't try to use it for multiplying a matrix by a vector. But using it as a basic operator within a monoid is perfectly respectable. No need to "lift" or "promote" scalars into vectors/matrices, etc. For "scaling" I use personally an operation (*>) defined within the Module constructor class, but I am unhappy, because (*>) :: a -> (t a) -> (t a) declared in a Module instance of the constructor t prevents from using it in the case where (t a) in reality is a. (By default (*>) maps (x*) through the elements of (t ...), and kinds "*" are not constructors... Jerzy Karczmarczuk Caen, France