> We tried to get a trait hierarchy for numerical right four or five times; it's really hard.
I'm clearly not an expert in Rust, and as such I realize my opinion isn't worth much, but it seems to me any attempt to build a numerical hierarchy will result in something fragile. Scheme has a well thought out minimal hierarchy, and even without all the complexity from all the fixed sizes I don't like theirs either. However, it's alright because they have dynamic typing as a release valve. C++ templates dodge the problem because they act more like duck typing, and I think I understand why Rust won't go that way.
Instead, I think you should abandon the goal of trying to create a hierarchy and just treat each small concept as a trait. Rather than creating Signed for things with a sign, and Float for things that are floating point and so on as in the num crate, create Abs as a small concept in its own. Then anything on which you can call .abs() implements the Abs trait. This includes things which aren't "signed", such as unsigned numbers and complex numbers - all of those have a useful (even if sometimes trivial) definition of .abs()
Trying to build a hierarchy reminds me of the stereotypical examples of bad object oriented design - I'm sure you know the cliches where Human derives from Ape, then Mammal, then Animal, etc... A hierarchy is like trying to build a directory tree, but numeric traits are more like labels on your gmail messages. Traits seem more like mixins or interfaces.
You'll end up with a lot of one or two method traits, but you'll also allow people to implement those traits for their own types and implement generic functions over those traits. The Float trait is a really good example of why categorization/hierarchy is unlikely to work well - That trait requires methods like .min_value() which don't make sense for complex numbers, but it is also the interface for .sqrt(), .exp(), and .ln() which have very good definitions for complex numbers. The Float trait in the num crate does not allow me to make generic functions over f32 and Complex<f64> if I want to call .exp().
> For just abs it should only be a few lines of code to make your own
Yeah, the IRC people helped me find a different workaround, but I agree that I'll just make my own trait instead. However, the .abs() methods already exist on all of the signed built in types and so I'll probably call mine .mag() on a Mag trait to avoid confusion (I'm not sure if there would be a collision/compilation error if I used the same names?). This also allows me to implement Mag for unsigned types which is useful for generic code (for examples L1 norms of vectors).
Btw, the docs for i64 have some cut-and-paste errors where they indicate i32 but almost certainly meant i64.
This is already the approach taken with the operator overloading methods with single-method traits like Add, Sub, and so on. Arguably things like .abs() and .exp() also logically belong in the same category and therefore should be implemented as small traits of approximately one method each.
Yeah, that might work, but then it leads to an explosion of traits...
The examples are cut and paste errors in a sense; as it says at the top of the page, the examples are going to often use the wrong number type, because all that code is generated by macros, so for now all the number docs have to use the same type. It's a bug that needs to be fixed, but it's not a trivial one.
I'll think about it, but honestly, that sounds super unpleasant. I've seen the bike shedding that happens in those kinds of discussions, and I think it would be difficult to convince any decision makers that there is even a problem if they haven't stumbled upon it already. I don't think many of them do numerical computing, and the language and libraries already scratch their non-numerical itches.
That's fair; I will say that numeric computing is an area we care about. Still working on some of the precursor stuff to make it great, though.
There's a _lot_ of ways you can go in a general purpose programming language; I don't think it's something that "nobody has hit", they just use the workaround of making their own trait(s) and it hasn't been enough of a pain to try and spearhead a change.
I'm clearly not an expert in Rust, and as such I realize my opinion isn't worth much, but it seems to me any attempt to build a numerical hierarchy will result in something fragile. Scheme has a well thought out minimal hierarchy, and even without all the complexity from all the fixed sizes I don't like theirs either. However, it's alright because they have dynamic typing as a release valve. C++ templates dodge the problem because they act more like duck typing, and I think I understand why Rust won't go that way.
Instead, I think you should abandon the goal of trying to create a hierarchy and just treat each small concept as a trait. Rather than creating Signed for things with a sign, and Float for things that are floating point and so on as in the num crate, create Abs as a small concept in its own. Then anything on which you can call .abs() implements the Abs trait. This includes things which aren't "signed", such as unsigned numbers and complex numbers - all of those have a useful (even if sometimes trivial) definition of .abs()
Trying to build a hierarchy reminds me of the stereotypical examples of bad object oriented design - I'm sure you know the cliches where Human derives from Ape, then Mammal, then Animal, etc... A hierarchy is like trying to build a directory tree, but numeric traits are more like labels on your gmail messages. Traits seem more like mixins or interfaces.
You'll end up with a lot of one or two method traits, but you'll also allow people to implement those traits for their own types and implement generic functions over those traits. The Float trait is a really good example of why categorization/hierarchy is unlikely to work well - That trait requires methods like .min_value() which don't make sense for complex numbers, but it is also the interface for .sqrt(), .exp(), and .ln() which have very good definitions for complex numbers. The Float trait in the num crate does not allow me to make generic functions over f32 and Complex<f64> if I want to call .exp().
> For just abs it should only be a few lines of code to make your own
Yeah, the IRC people helped me find a different workaround, but I agree that I'll just make my own trait instead. However, the .abs() methods already exist on all of the signed built in types and so I'll probably call mine .mag() on a Mag trait to avoid confusion (I'm not sure if there would be a collision/compilation error if I used the same names?). This also allows me to implement Mag for unsigned types which is useful for generic code (for examples L1 norms of vectors).
Btw, the docs for i64 have some cut-and-paste errors where they indicate i32 but almost certainly meant i64.