3 ms·
I mean, I feel this particular example is really more of standard library design wart that occurs with any kind of syntactic overloading mechanism, e.g. Haskell
by aseipp 5y ago
I mean, I feel this particular example is really more of standard library design wart that occurs with any kind of syntactic overloading mechanism, e.g. Haskell has the exact same problem as C++ does here even though the overloading mechanism is rather different (Eq is defined for Float and there is no notion of a PartialEq in the standard library, even though there really should be), and there are numerous more advanced examples of that (every few years someone writes a fun library that violates the Monad laws, for instance). The fundamental question is: does an instance of an overloading mechanism respect the "properties" you expect of it, algebraic ones or not? Your program will often behave weirdly if the answer is "no" but the extent of the weirdness permissible is a spectrum. Basically no languages actually allow you to validate those semantics; it's expected that the "validity" of those instances is proven or known a priori, and bad ones don't exist.
IMO, the bigger problem with this particular example is just that the numeric and algebraic hierarchies in most languages are littered with various edge cases just like this one, and that makes it an easy and convenient nitpick to point out. But I think the parent comment is largely correct in that the goal of a concept (or trait, or typeclass) is roughly to reflect a property or action of some object or type or what have you, even if C++ wasn't as originally as forward thinking as other languages today are with invariant safety.
- tialaramex 5y agoThe practical difference between C++ Concepts and Rust Traits is that Rust Traits get to have semantics by declaration and you can't do that with C++ Concepts. A Rust type gets to choose whether to be Eq or just PartialEq. I can wrap floats up in a new type, MyFloat, provide an implementation of == to qualify it as PartialEq and then I can choose whether to just declare that it's Eq. The compiler cannot check I told the truth, but as implementer I can decide whether to promise that or not. In C++ you do not get a choice. C++ concepts refer to syntax and only that. I can write a concept named hates_balloons and have it depend on types implementing a zero argument method named "pop". But if your stack class with a pop method doesn't in fact hate balloons too bad, my concept says that since the syntax matches you have no choice, your stack hates_balloons and there is nothing you can do about it. I understand exactly why C++ did this. There is a lot of legacy C++ code out there. If you make every new C++ iterator say it implements the iterator concept and don't grandfather the existing ones, nobody uses the feature. But, even though they had a good reason to do this, it's still a considerable weakness.