Thoughts about redesigning "Num" type class

The class Num defines the method functions like (+), (-), (*), negate, ... But they are actually different concepts. We often meet situations that we want an addition for some type, but not a multiplication. For example: *data* Vec3 = Vec3 Float Float Float The additive (and subtractive) operation is obvious, but not for a multiplication. * Note* (*) :: a -> a -> a -- not avaliable for a Vec3 scale :: Float -> Vec3 -> Vec3 dot :: Vec3 -> Vec3 -> Float We cannot define a (+) alone for a type, so we got many different functions from different libs, like mappend, mplus, plus, (<+>), (.+.), (+.), which all reads "plus". My opinion is that the methods of Num should be seperated into different type classes, so we don't need to invent a new symbol for a new lib. A feasible design is: *import* Prelude *hiding* ( Num (..), Monoid (..), sum ) *class* SemiGroup a *where* (+) :: a -> a -> a *class* SemiGroup a => Monoid a *where* zero :: a *class* Monoid a => Group a *where* negate :: a -> a (-) :: a -> a -> a x - y = x + negate y negate x = zero - x *class* Group a => Ring a *where *-- not sure about the name ... (*) :: a -> a -> a *class* Ring a => Num a *where* abs :: a -> a signum :: a -> a fromInteger :: Integer -> a sum :: (Foldable t, Monoid a) => t a -> a sum = foldl' (+) zero -- for compatibility mempty = zero mappend = (+) mconcat = sum

sorry, didn't realize
the subjects prefixed with a "[Haskell-Cafe]"
2015-09-07 22:08 GMT+08:00 DreamApart AtHaskells
The class Num defines the method functions like (+), (-), (*), negate, ... But they are actually different concepts.
We often meet situations that we want an addition for some type, but not a multiplication. For example:
*data* Vec3 = Vec3 Float Float Float
The additive (and subtractive) operation is obvious, but not for a multiplication.
* Note*
(*) :: a -> a -> a -- not avaliable for a Vec3
scale :: Float -> Vec3 -> Vec3 dot :: Vec3 -> Vec3 -> Float
We cannot define a (+) alone for a type, so we got many different functions from different libs, like mappend, mplus, plus, (<+>), (.+.), (+.), which all reads "plus".
My opinion is that the methods of Num should be seperated into different type classes, so we don't need to invent a new symbol for a new lib.
A feasible design is:
*import* Prelude *hiding* ( Num (..), Monoid (..), sum )
*class* SemiGroup a *where* (+) :: a -> a -> a
*class* SemiGroup a => Monoid a *where* zero :: a
*class* Monoid a => Group a *where* negate :: a -> a (-) :: a -> a -> a
x - y = x + negate y negate x = zero - x
*class* Group a => Ring a *where *-- not sure about the name ... (*) :: a -> a -> a
*class* Ring a => Num a *where* abs :: a -> a signum :: a -> a fromInteger :: Integer -> a
sum :: (Foldable t, Monoid a) => t a -> a sum = foldl' (+) zero
-- for compatibility mempty = zero mappend = (+) mconcat = sum

On Mon, Sep 7, 2015 at 10:08 AM, DreamApart AtHaskells
The class Num defines the method functions like (+), (-), (*), negate, ... But they are actually different concepts.
We often meet situations that we want an addition for some type, but not a multiplication. For example:
I think the concept you're reaching for here is Monoid. However, there is a(t least one) complication you did not consider: (+) is assuredly a monoid. But so is (*)! (Try looking at it through logarithms.) Unfortunately, a given type can have only a single instance of a given typeclass. There is in fact a way to design Num "rationally", via number theory; I suggest you at least familiarize yourself with that. (This will bring you face to face with the above issue, as it turns out that Num requires two monoids.) It turns out that this is not necessarily a good idea. But see http://hackage.haskell.org/package/numeric-prelude for an implementation of it. -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

(+) is assuredly a monoid. But so is (*)! (Try looking at it through logarithms.) Unfortunately, a given type can have only a single instance of a given typeclass.
There is in fact a way to design Num "rationally", via number theory; I suggest you at least familiarize yourself with that. (This will bring you face to face with the above issue, as it turns out that Num requires two monoids.) It turns out that this is not necessarily a good idea. But see http://hackage.haskell.org/package/numeric-prelude for an implementation of it.
Well, at some point you will have to make concessions. I for one, would love to use (+) without Num or going through an entire semester of Algebra. And DreamApart's proposal seams a reasonable compromise. I don't see it being included into base any time soon however, as it would break an enormous amount of code. As for the Maybe instances. From afar, if you think of Nothing as 0 and Just x as (not 0), the Monoid instance looks like (*) and the MonadPlus instance looks like (+). Also, I would prefer. class (Monoid m) => Ring m where ... class (Group a, Ring a) => Num a where ... Cheers Silvio

On Mon, Sep 7, 2015 at 10:08 AM, DreamApart AtHaskells
We cannot define a (+) alone for a type, so we got many different functions from different libs, like mappend, mplus, plus, (<+>), (.+.), (+.), which all reads "plus".
I should mention one other difficulty: MonadPlus and Monoid are not quite the same thing. mplus and mappend can't really be unified into a single conceptual structure (mostly because of the Maybe instance). -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net

You saw this, right? https://wiki.haskell.org/Nitpicks#Base-related_nitpicks (Just in case, maybe it was you who included this topic.) Alexey. On Monday, September 7, 2015 at 4:08:54 PM UTC+2, DreamApart AtHaskells wrote:
The class Num defines the method functions like (+), (-), (*), negate, ... But they are actually different concepts.
We often meet situations that we want an addition for some type, but not a multiplication. For example:
*data* Vec3 = Vec3 Float Float Float
The additive (and subtractive) operation is obvious, but not for a multiplication.
* Note*
(*) :: a -> a -> a -- not avaliable for a Vec3
scale :: Float -> Vec3 -> Vec3 dot :: Vec3 -> Vec3 -> Float
We cannot define a (+) alone for a type, so we got many different functions from different libs, like mappend, mplus, plus, (<+>), (.+.), (+.), which all reads "plus".
My opinion is that the methods of Num should be seperated into different type classes, so we don't need to invent a new symbol for a new lib.
A feasible design is:
*import* Prelude *hiding* ( Num (..), Monoid (..), sum )
*class* SemiGroup a *where* (+) :: a -> a -> a
*class* SemiGroup a => Monoid a *where* zero :: a
*class* Monoid a => Group a *where* negate :: a -> a (-) :: a -> a -> a
x - y = x + negate y negate x = zero - x
*class* Group a => Ring a *where *-- not sure about the name ... (*) :: a -> a -> a
*class* Ring a => Num a *where* abs :: a -> a signum :: a -> a fromInteger :: Integer -> a
sum :: (Foldable t, Monoid a) => t a -> a sum = foldl' (+) zero
-- for compatibility mempty = zero mappend = (+) mconcat = sum

No, I didn't know there such wiki page. Thank you.
2015-09-08 4:05 GMT+08:00 Alexey Muranov
You saw this, right?
https://wiki.haskell.org/Nitpicks#Base-related_nitpicks
(Just in case, maybe it was you who included this topic.)
Alexey.
On Monday, September 7, 2015 at 4:08:54 PM UTC+2, DreamApart AtHaskells wrote:
The class Num defines the method functions like (+), (-), (*), negate, ... But they are actually different concepts.
We often meet situations that we want an addition for some type, but not a multiplication. For example:
*data* Vec3 = Vec3 Float Float Float
The additive (and subtractive) operation is obvious, but not for a multiplication.
* Note*
(*) :: a -> a -> a -- not avaliable for a Vec3
scale :: Float -> Vec3 -> Vec3 dot :: Vec3 -> Vec3 -> Float
We cannot define a (+) alone for a type, so we got many different functions from different libs, like mappend, mplus, plus, (<+>), (.+.), (+.), which all reads "plus".
My opinion is that the methods of Num should be seperated into different type classes, so we don't need to invent a new symbol for a new lib.
A feasible design is:
*import* Prelude *hiding* ( Num (..), Monoid (..), sum )
*class* SemiGroup a *where* (+) :: a -> a -> a
*class* SemiGroup a => Monoid a *where* zero :: a
*class* Monoid a => Group a *where* negate :: a -> a (-) :: a -> a -> a
x - y = x + negate y negate x = zero - x
*class* Group a => Ring a *where *-- not sure about the name ... (*) :: a -> a -> a
*class* Ring a => Num a *where* abs :: a -> a signum :: a -> a fromInteger :: Integer -> a
sum :: (Foldable t, Monoid a) => t a -> a sum = foldl' (+) zero
-- for compatibility mempty = zero mappend = (+) mconcat = sum

On 8/09/2015, at 2:08 am, DreamApart AtHaskells
class SemiGroup a where (+) :: a -> a -> a
class Group a => Ring a where -- not sure about the name ... (*) :: a -> a -> a
class Ring a => Num a where abs :: a -> a signum :: a -> a fromInteger :: Integer -> a
sum :: (Foldable t, Monoid a) => t a -> a sum = foldl' (+) zero
Traditionally, + is used only for Abelian groups, with a non-commutative binary operation being thought of as a "product", not a sum. I am not very happy at the idea of being told to use the wrong operator for a group operation. Sounds like you want NumericPrelude. (Or possibly the AXIOM computer algebra language...)

Richard A. O'Keefe comments :
Sounds like you want NumericPrelude. (Or possibly the AXIOM computer algebra language...)
Just a word. This is a quite old subject, "reinvented" many times. A neverending story... Already twenty (yes!) years ago, in: /Functional programming languages in education : first international symposium, FPLE '95, Nijmegen, the Netherlands, December 4-6, 1995/ you may find two talks about it, one by Jeroen Fokker, and other, mine. Then, Sergei Mechveliani worked some years on it, and constructed DoCon, the Algebraic Domain Constructor (accessible here: http://homepages.inf.ed.ac.uk/wadler/realworld/docon2.html ) Presumably there are other implementations as well. I know, but I lost the references, that some people worked on such constructs in Clean, where the type class structure is a bit different than in Haskell, some generalizations are easier to implement. Jerzy Karczmarczuk

My, maybe naive, impression is that there should always be one class per operation, as this is obviously the most flexible approach. Then maybe some classes can be grouped together by introducing other classes. Alexey.

On Mon, Sep 7, 2015 at 10:08 AM, DreamApart AtHaskells
A feasible design is: [...]
I'm all for redesigning the numeric hierarchy, but I must (once again) raise my standard complaint against this sort of work, namely: there must be a class for semirings. Abelian groups are nice and all, but they are not the principle origin for rings. There are far more objects which have addition/multiplication without subtraction than there are objects with addition/subtraction without multiplication. Everyone I've seen suggest redesigning Num keeps overlooking this fact, since they're all so hung up on module/vector spaces. -- Live well, ~wren

I think we definitely want to keep subtraction separate, and maybe
even allow for a subtraction operation that returns a different
type...
On Fri, Sep 11, 2015 at 7:05 PM, wren romano
On Mon, Sep 7, 2015 at 10:08 AM, DreamApart AtHaskells
wrote: A feasible design is: [...]
I'm all for redesigning the numeric hierarchy, but I must (once again) raise my standard complaint against this sort of work, namely: there must be a class for semirings.
Abelian groups are nice and all, but they are not the principle origin for rings. There are far more objects which have addition/multiplication without subtraction than there are objects with addition/subtraction without multiplication. Everyone I've seen suggest redesigning Num keeps overlooking this fact, since they're all so hung up on module/vector spaces.
-- Live well, ~wren _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

My 2 cents: the difference of two points in an affine space is a vector, the sum of a point of an affine space and a vector is another point. On Saturday, September 12, 2015 at 8:50:58 AM UTC+2, David Thomas wrote:
I think we definitely want to keep subtraction separate, and maybe even allow for a subtraction operation that returns a different type...

On 11/09/2015, David Thomas
a subtraction operation that returns a different type
On 12/09/2015, Alexey Muranov
My 2 cents: the difference of two points in an affine space is a vector, the sum of a point of an affine space and a vector is another point.
This would mean multi-parametre type classes or type families, so we'd need to canonicalize them first, which seems unlikely in near future at least.

Maybe it is unreasonable to look for maximal generality in use of each operator. A common use of "+" is for the operation of an abelian semigroup or monoid (any semigroup can be converted into a monoid anyway). Alexey. P.S. I was tempted to suggest "++" for cancellative monoids and "**" for arbitrary monoids, but "++" on lists is not quite cancellative if one of the lists is infinite. On Saturday, September 12, 2015 at 10:52:01 AM UTC+2, M Farkas-Dyck wrote:
On 11/09/2015, David Thomas
javascript:> wrote: a subtraction operation that returns a different type
On 12/09/2015, Alexey Muranov
javascript:> wrote: My 2 cents: the difference of two points in an affine space is a vector, the sum of a point of an affine space and a vector is another point.
This would mean multi-parametre type classes or type families, so we'd need to canonicalize them first, which seems unlikely in near future at least.

In case you haven't seen it, "vector-space" provides a good representation of affine spaces, additive groups, etc
El Sep 12, 2015, a las 4:51, M Farkas-Dyck
On 11/09/2015, David Thomas
wrote: a subtraction operation that returns a different type
On 12/09/2015, Alexey Muranov
wrote: My 2 cents: the difference of two points in an affine space is a vector, the sum of a point of an affine space and a vector is another point.
This would mean multi-parametre type classes or type families, so we'd need to canonicalize them first, which seems unlikely in near future at least. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe

Le 12/09/2015 10:15, Alexey Muranov a écrit :
the difference of two points in an affine space is a vector, the sum of a point of an affine space and a vector is another point.
We can give several similar examples, but it might not be so useful for the implementation of whatever. In the affine space we can interpolate between points: p0 + 1/2*(p1-p0) is the middle point. Writing it just as 1/2*(p0+p1) might be considered as an abomination, since you should not add points... Who should allow the first and forbid the second? Programming languages is not mathematics. Types are not mathematical domains. Classes are not categories... Whatever we/you propose, there will be many unhappy people around. Math is full of subsumptions, whose automatic implementation might be difficult, inefficient or ambiguous. If you have an additive group, you have automatically a module over integers. Sorry, *positive* integers. So, we might second David Thomas, we should have Rigs (semi-rings). But why a *class*?? And, if you define such object by hand, you may be accused of introducing redundancies, that it should be inferred... And whatever people say, e.g., Wren Romano:
There are far more objects which have addition/multiplication without subtraction than there are objects with addition/subtraction without multiplication. ... several simple-minded people (myself included) will find not so useful and/or clumsy the introduction of specific structures just to forbid the subtraction. Nobody will fight against these purists, it is simply a difficult issue.
All this is a can of worms, and physicists live with it already for centuries [Time : add please today to yesterday... Subtract works. They were happy, because there were no computer formalisators around, who could bother them...]. Please read the *easy* article of John Baez (2009) : http://math.ucr.edu/home/baez/torsors.html And then look upon torsors in general, and recognize that is is simply horrible. == All the best. Jerzy Karczmarczuk /Caen, France/

On 12 sept. 2015, at 11:12, Jerzy Karczmarczuk
Le 12/09/2015 10:15, Alexey Muranov a écrit :
the difference of two points in an affine space is a vector, the sum of a point of an affine space and a vector is another point.
We can give several similar examples, but it might not be so useful for the implementation of whatever. In the affine space we can interpolate between points: p0 + 1/2*(p1-p0) is the middle point. Writing it just as 1/2*(p0+p1) might be considered as an abomination, since you should not add points... Who should allow the first and forbid the second?
I agree, it gets complicated. A more general type of "weighted sums of points" would probably be needed, but impractical...
If you have an additive group, you have automatically a module over integers. Sorry, *positive* integers.
Well, over all integers as well.
Please read the *easy* article of John Baez (2009) : http://math.ucr.edu/home/baez/torsors.html And then look upon torsors in general, and recognize that is is simply horrible.
I didn't get this (i do not know much about torsors). Alexey.

On Sat, Sep 12, 2015 at 5:12 AM, Jerzy Karczmarczuk
And whatever people say, e.g., Wren Romano:
There are far more objects which have addition/multiplication without subtraction than there are objects with addition/subtraction without multiplication.
... several simple-minded people (myself included) will find not so useful and/or clumsy the introduction of specific structures just to forbid the subtraction. Nobody will fight against these purists, it is simply a difficult issue.
I can assure you that there are numerous examples that even the most "simple-minded" programmers are already intimately familiar with; cf., http://winterkoninkje.dreamwidth.org/80410.html. People use tropical, arctic, and Viterbi semirings all over the place: e.g., almost[1] every minimization/maximization problem where there's some notion of gluing together subproblems. People use bounded distributed lattices all the time: e.g., most everything in naive set theory. People use regular languages/regexes all the time. All of these are semirings, and none of them are rings. Semirings are perfectly banal everyday things. The only difficulty is in realizing how ubiquitous they are. This has nothing to do with being a purist. I want semirings encoded as a type class because —as a workaday programmer— I find myself almost daily wanting to parameterize some algorithm to use a different semiring. All the structure of linear algebra is far more complicated than what's going on here. Most programmers ("simple-minded" or otherwise) do not actually work with linear algebra; but almost every programmer has to deal with sets, parsing/languages, minimizing/maximizing, not screwing up array indices by going negative, etc etc. [1] The salient min/max problems that don't fit that characterization are the ones where we have two notions of gluing together sub problems, in which case we generally want an ordered semiring— which is just a generalization. -- Live well, ~wren

Without taking a stand, I note that Clean has one class per "arithmetic" function. I also note that Money / Money -> Number (not Money) and if Money * Money -> something, it certainly isn't Money. Similarly, DateAndTime ± Duration -> DateAndTime, and DateAndTime - DateAndTime -> Duration, but DateAndTime + DateAndTime does not make sense. (DateAndTime and Duration come from Smalltalk.) I've chosen these examples because they are not "metaphorical" overloadings of the operators. They do obey natural axioms. E.g., now + (now - now) = now, now + (dur*2) = (now+dur)+dur. (Assuming exact numbers underneath.) Sounds like multi-parameter typeclasses are going to be vital for a restructuring of the numeric classes.

The NeverEnding story never ends. Good! Le 14/09/2015 04:17, Richard A. O'Keefe a écrit :
Without taking a stand, I note that Clean has one class per "arithmetic" function. This should not be understood too rigidly, we can combine various sectors under one name, e.g.,
class Determinant a | *, -, fromInt a -- as exemplified in the Clean book on FP, page 85. This is a shortcut, but replaces Haskell classes with many members.
I also note that Money / Money -> Number (not Money) and if Money * Money -> something, it certainly isn't Money. Similarly, DateAndTime ± Duration -> DateAndTime, and DateAndTime - DateAndTime -> Duration, but DateAndTime + DateAndTime does not make sense. (DateAndTime and Duration come from Smalltalk.)
I've chosen these examples because they are not "metaphorical" overloadings of the operators. They do obey natural axioms. E.g., now + (now - now) = now, now + (dur*2) = (now+dur)+dur. (Assuming exact numbers underneath.)
Sounds like multi-parameter typeclasses are going to be vital for a restructuring of the numeric classes.
I am afraid that multi-parameter classes will not "save" anything without some precision concerning types themselves. As I say from time to time, physicists live with similar questions for centuries, in the context of physical units. Money*Money is strange. But what about time*time? mass*mass? The gravitational constant G has the dimension m^3 kg^(-1) s^(-2). But only in SI, and related system. You find all possible powers, of dimensional quantities, depending on your choice of *fundamental* unities. I learnt in school the MKS/electrostat system, where an electric charge had the dimension of the square root of force, m^(3/2) kg^(1/2) s^(-1). Later, studying quantum electrodynamics, I assimilated the "truth", that electric charge is just a number, dimensionless. Why? Because physicists have no reluctance to use UNIVERSAL constants as pure numbers, which redefines meters, seconds, etc. : the speed of light is 1. The Planck constant is 1 as well. And the electric charge becomes dimensionless. Cosmologists add G=1, and all dynamical quantities become dimensionless, no more meters, seconds, kilograms. Just numbers. They write formulae, where some quantity is proportional to the logarithm of time. This may disturb even those who accept non-integral powers of length. Is it "cheating", implying log(t/t_0), where the denominator reduces the time unit? What about fractal objects with abominable dimensions, say m^(1.2654132089) ? And such things exist in physics, they are called, for example, anomalous dimensions, measurable. If we decide that there exists universally One Currency to Rule Them All, say, Renminbi, then money + money*money + 1/money + exp(-money) becomes computable. Of course, you will not measure your waist in Planck length units (although for myself, year after year, it becomes less crazy), but all this is not a trivial issue. == This is another reason (the first was torsors) why I don't believe in too simplistic playing with straitjackets, forbidding some operations in the name of "coherence". DateTime vs Duration is another version of the point/vector discussion, or vector vs. affine spaces. Thousands of texts covered all that... Jerzy Karczmarczuk ^
participants (11)
-
Alexey Muranov
-
amindfv@gmail.com
-
Brandon Allbery
-
David Thomas
-
DreamApart AtHaskells
-
Jerzy Karczmarczuk
-
M Farkas-Dyck
-
Richard A. O'Keefe
-
RnMss ForHaskellMailList
-
Silvio Frischknecht
-
wren romano