rust-num / num-traits

Numeric traits for generic mathematics in Rust
Apache License 2.0
732 stars 135 forks source link

Added impl for Zero on Cow<'_, str> #143

Closed jsmith628 closed 5 years ago

jsmith628 commented 5 years ago

Implements Zero as the empty string, Cow::Borrowed(""), for Cow<'_,str>

Since Cow<'_,str> implements Add, AddAssign, and Clone, and since string addition does actually constitute a proper mathematical Monoid, I felt it made since to implement the Zero trait on Cow to complete that connection.

Of course... strings aren't really numbers, so feel free to reject the changes if you guys want to, but it would definitely make the Zero trait decently more viable as a general additive identity for more abstract mathematical crates.

cuviper commented 5 years ago

string addition does actually constitute a proper mathematical Monoid

It's not commutative though, which is a basic property of addition.

Of course... strings aren't really numbers, so feel free to reject the changes if you guys want to,

That's my inclination.

but it would definitely make the Zero trait decently more viable as a general additive identity for more abstract mathematical crates.

Do you have any real use case for strings in such a crate? This seems like a hypothetical "because we can" kind of feature, rather than something anyone would really need.

jsmith628 commented 5 years ago

While yes, the addition is not commutative, the standard library does already have impls on Cow for both Add and AddAssign in stable, and my implementation does fit both the num-traits docs and standard mathematical definition of zero. So honestly, adding this would probably be worth it if only for the sake of consistency with std.

Moreover, I'd say that it's just a generally good idea to implement a trait on as many things fit its properties as possible, as this can only increase the possible things you can do with it. In fact, believe it or not, I actually have run into cases where not implementing Zero on Cow has restricted something in a crate of mine and cases where I've had to specifically design around the fact that Cow implements a non-commutative addition.

cuviper commented 5 years ago

So honestly, adding this would probably be worth it if only for the sake of consistency with std.

IMO this is a wart in std, but core::ops::Add is at least defined to support the language operator +, not necessarily mathematical. The num crates are meant to be numeric.

Moreover, I'd say that it's just a generally good idea to implement a trait on as many things fit its properties as possible, as this can only increase the possible things you can do with it.

That's debatable, but there's at least an implicit property of being numeric here, which of course strings don't satisfy.

In fact, believe it or not, I actually have run into cases where not implementing Zero on Cow has restricted something in a crate of mine

Is that code public? I still find it hard to imagine wanting Zero for Cow, unless you're just trying to be super generic with anything that implements Add. Maybe Default would be a better non-numeric trait for your case?

jsmith628 commented 5 years ago

IMO this is a wart in std, but core::ops::Add is at least defined to support the language operator +, not necessarily mathematical. The num crates are meant to be numeric.

I would more-or-less concur that it is a wart in std, but IMHO I wouldn't actually say that the implementation isn't mathematical. It very much fits within algebra as a monoid. In fact, it actually specifically represents a free-monoid over characters.

That's debatable, but there's at least an implicit property of being numeric here, which of course strings don't satisfy.

As for whether it's numeric, I'd honestly say that the term is incredibly ambiguous. What exactly counts? Does it include only real-number subsets? If so, then we shouldn't have num-complex. If it's only complex-numbers, then what about Polynomials? They definitely contain a useful 0. Or what about modular arithmetic? Or vectors? Or matrices?

So, frankly, I feel as though the only way to decide this is with precise mathematical definitions (which string addition does satisfy).

Is that code public? I still find it hard to imagine wanting Zero for Cow, unless you're just trying to be super generic with anything that implements Add. Maybe Default would be a better non-numeric trait for your case?

There is definitely an element of my wanting to be super generic, but I do have a couple of precise examples:

cuviper commented 5 years ago

I would more-or-less concur that it is a wart in std, but IMHO I wouldn't actually say that the implementation isn't mathematical.

I think it's also telling that we're only talking about Cow<'_, str> here, and not String because it doesn't implement Add<Self>.

As for whether it's numeric, I'd honestly say that the term is incredibly ambiguous. What exactly counts? Does it include only real-number subsets? If so, then we shouldn't have num-complex. If it's only complex-numbers, then what about Polynomials? They definitely contain a useful 0. Or what about modular arithmetic? Or vectors? Or matrices?

Every one of your further examples has a numeric zero in a way that does not apply to strings, and they can also implement a useful One. It's a bad sign for strings that Zero is the only trait here that they could maybe possibly kinda-sorta implement. I think it's just not a good fit.

  • Since string addition is effectively just a free-monoid, there is a very real possibility that I would have simply not included them in my free-algebra crate and used Cow instead.

It seems to me that having custom types is actually a good thing, rather than trying to force the std string types into a more abstract role.

jsmith628 commented 5 years ago

I think it's also telling that we're only talking about Cow<'_, str> here, and not String because it doesn't implement Add<Self>.

Yes, exactly. My assumption was that Zero was for types that implement Add<Self> and have an additive identity. Does it bother me that Cow<'_, str> implements Add<Self> and String doesn't? Yes. Do I think that this should mean that Zero is inconsistent in its implementation? No.

Every one of your further examples has a numeric zero in a way that does not apply to strings, and they can also implement a useful One. It's a bad sign for strings that Zero is the only trait here that they could maybe possibly kinda-sorta implement. I think it's just not a good fit.

If matrices and vectors are considered "numeric", then the term literally means nothing. If vectors don't have multiplication or division or a multiplicative identity either, then why should strings have to? Heck, if you need a numeric type, then why not just use num? That's what it's there for.

Moreover, if a struct shouldn't implement Zero unless it implements One, then why aren't they combined into a single Identities trait? If you're going to make two traits separate, then the implementations should be expected to be.

It seems to me that having custom types is actually a good thing, rather than trying to force the std string types into a more abstract role.

Take that up with rust-lang, then. I just think that if a type is going to implement some algebraic traits, it should implement all that fit.

cuviper commented 5 years ago

Rather than arguing about what is a number / numeric, I'm going back to your first concession:

Of course... strings aren't really numbers, so feel free to reject the changes if you guys want to,

I think strings are a bad fit for num-traits, but of course you should feel free to define a more abstract Zero in your crates if that broader concept makes sense for you.