-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use correct naming for the comparison traits and add trait inheritance #12517
Comments
We had some discussion about this on IRC. I think this option is probably the best compromise. (For my own reference, and to summarize other bugs, iiuc there are 3 fundamental options:
I think Option 3 is probably a good compromise between total principle (option 1) and total pragmatism (option 2), and I think that Option 3 is what this proposal is about.) |
@thestinger So you decided not to use the name |
Might it be worth adding an explicit
to For symmetry, you could also mirror this structure on the The motivation in either case is that it's way more obvious from the types that it's a partial ordering/equality (or rather, what is meant by "partial") if there's a method that returns an |
@pnkfelix: I couldn't think of a corresponding name for @glaebhoerl: It might be worth adding something like that, but for now I just want to land the simplifications and then figure out how to even approach renaming due to deriving. |
* `Ord` inherits from `Eq` * `TotalOrd` inherits from `TotalEq` * `TotalOrd` inherits from `Ord` * `TotalEq` inherits from `Eq` This is a partial implementation of #12517.
Assigning 1.0, P-backcompat-libs. What is outlined here is better than what we have now, so it is fine to continue down this path. (I just want to add the caveat that for 1.0, we will not necessarily adopt this particular strategy for resolving this problem in the end, but we do need to resolve how we are dealing with this problem before 1.0.) |
copying this etherpad link over here from #12579 for posterity: https://etherpad.mozilla.org/uaxtsKwv8w. It contains an audit of which code is using which traits, and some notes on use cases. |
I also added a basic description to the issue, since the discussions leading to this plan took place over many issues. |
I was thinking about this tonight, and I wanted to jot down my ideas before I forget. I found a very useful tactic for thinking about this to start from the goal and go back to the implementation. From all the discussions I've heard around this, it sounds like "total equality" is the correct default, but the idea of "partial equality" must be a property of the type, hence there should be a distinction in the traits. I thought of these examples for how I expect usage of // Struct that has all the comparison operators
// Fails if anything is a float
#[deriving(Eq, Ord)]
struct Foo { ... }
// Manual implementation of the == operator (you get != for free)
impl Eq for Foo {
fn eq(&self, other: &Foo) -> bool { ... }
}
// Manual implementation of all comparison operators
impl Ord for Foo {
fn cmp(&self, other: &Foo) -> Ordering { ... }
}
// This function can use the == operator (same for Ord and comparison operators)
fn foo<T: Eq>() {}
// This function can also use the == operator (same for PartialOrd and comparison operators)
fn foo<T: PartialEq>() {} With this in mind, the current plan of action (the checklist at the top) achieves almost all of these, except for the manual implementation ones. We have made explicit decision in the past to keep Essentially, this proposed system makes it quite nice to use the comparison traits, but it makes it unwieldy to define them manually. This musing isn't very useful unless something good comes of it, so here's a todo list which may be able to address my concerns:
With this checklist, I believe that both my constraints and the constraints at the top of this issue are all satisfied. The only snag is that the current version of the compiler will reject any manual implementations of We were talking at the work week about lifting this restriction, and if others are amenable to this strategy, I would like to rekindle discussion about lifting the restriction. It looks that with the traits not linked to one another that you almost never have to see that the partial orderings exist, but you get them all for free. Additionally, |
For many types, it's going to be inefficient to define the methods returning In my opinion, there's a language flaw we should fix here and I don't think this is the only place it's going to come up. A numeric hierarchy or container hierarchy will run into these same issues, as some types can provide more guarantees than others. There will be cases where there's something to gain from splitting up the trait hierarchy a bit more, but it currently adds noise for every single implementation. While adding two more hooks to the compiler for the total ordering traits can sidestep most of the issues here, I think a more general solution would be nicer. It should be possible to make use of trait inheritance for fine-grained trait hierarchies without placing a large burden on every implementation. I really liked @sfackler's proposal about this on IRC and have been thinking about it some more. If the language allowed you to implement methods from supertraits in the same implementation block, the syntactic noise would go away. It would also mean that deriving would still only be outputting a single implementation block, but I don't think it's an incredibly important concern. With a generic implementation, you're just as unable to define the |
If performance is the only concern, I think that it can be circumvented where necessary. You can always create a newtype wrapper and implement It's tough to consider this in a world where you can implement multiple traits in one block as I'm not quite sure how that would all work out. It's certainly RFC-worthy, and I'd like to see that before going down the rabbit hole of figuring out how it would interact with everything else. |
The performance issue here is sometimes a large one. If the methods are not implemented directly on some types, it can perform 2 units of work (< then ==) instead of one. This sometimes involves more than one pass over a sequence, etc. if there's not an efficient single-pass |
Another thought to address your concern on performance, how about: trait Ord {
/* four comparison methods (default methods) */
fn cmp(&self, other: &Self) -> Ordering;
} That should allow overriding where necessary for performance and the I also think that RFC 48 would enable the solution I outlined above. |
This is what I'm thinking: https://gist.github.com/alexcrichton/10945968 |
@alexcrichton: Assuming the trait changes allow doing it that way, I fully support it. I haven't yet read the RFC though... there are a lot of them. |
@alexcrichton Could the trait that has method |
I think |
(perhaps another way to satisfy @liigo would be to name the method in question |
I'm OK for Ord/ord
|
This is a transitionary step towards completing rust-lang#12517. This change modifies the compiler to accept Partial{Ord,Eq} as deriving modes which will currently expand to implementations of PartialOrd and PartialEq (synonyms for Eq/Ord). After a snapshot, all of deriving(Eq, Ord) will be removed, and after a snapshot of that, TotalEq/TotalOrd will be renamed to Eq/Ord.
This is a transitionary step towards completing #12517. This change modifies the compiler to accept Partial{Ord,Eq} as deriving modes which will currently expand to implementations of PartialOrd and PartialEq (synonyms for Eq/Ord). After a snapshot, all of deriving(Eq, Ord) will be removed, and after a snapshot of that, TotalEq/TotalOrd will be renamed to Eq/Ord.
This is part of the ongoing renaming of the equality traits. See rust-lang#12517 for more details. All code using Eq/Ord will temporarily need to move to Partial{Eq,Ord} or the Total{Eq,Ord} traits. The Total traits will soon be renamed to {Eq,Ord}. cc rust-lang#12517 [breaking-change]
Note that all of the default implementations in @alexcrichton's gist above need to be removed for |
@alexcrichton would types implementing EDIT: Nevermind, I totally misread that. |
How would the automatic implementation setup work with wrapper types that want to implement whichever of the |
This completes the last stage of the renaming of the comparison hierarchy of traits. This change renames TotalEq to Eq and TotalOrd to Ord. In the future the new Eq/Ord will be filled out with their appropriate methods, but for now this change is purely a renaming change. This continues the work of #12517, continuing the work in #14534. This patch accomplishes the final rename of `TotalEq` to `TotalOrd`. I wanted to get this patch landed ASAP so we don't have to deal much with "where did `Eq` and `Ord` go?" I have yet to do another pruning pass over the compiler to change all usage of `PartialEq` to `Eq` where appropriate. I will do this soon as well.
Here's an updated version of the gist with the default methods removed: https://gist.github.com/sfackler/80c6d544701937ad4ce6 |
…ang/rust/issues/12517 and adding in 0.11-pre references to transmute and expected Show impl of String struct
@alexcrichton What's the state of this? Is it really just deriving? Do we really need that or can we declare victory? |
This is mainly waiting on trait reform to land first. With trait reform it will be possible to say just |
I think this will need "negative bounds" to avoid "conflicting implementations" in some instances: impl<T: Eq> PartialEq for T { //~ error: conflicting implementations for trait `PartialEq`
fn eq(&self, other: &T) -> bool { Eq::eq(self, other) }
fn ne(&self, other: &T) -> bool { !Eq::eq(self, other) }
}
impl<T: Eq> Eq for [T, ..2] {
fn eq(&self, other: &[T, ..2]) -> bool { /* */ }
}
impl<T: PartialEq> PartialEq for [T, ..2] { // note: note conflicting implementation here
fn eq(&self, other: &[T, ..2]) -> bool { /* */ }
} You can see why this is the case, with the following example:
The "proper" solution is to add a negative bound to third impl<T: PartialEq + !Eq> PartialEq for [T, ..2] {
fn eq(&self, other: &[T, ..2]) -> bool { /* */ }
} That way (5) won't happen, and no implementation conflict will arise. As a work-around (until we get negative bounds) we can just drop the third |
@japaric Unfortunately, similar problems crop up for other types, like tuples. Negative bounds would indeed solve this problem, but they are very unlikely to happen for 1.0. We need a different plan here. |
See also #17884 which has the same issue. |
@alexcrichton I believe this is as done as it's going to be. Shall we close? |
I believe so as well. Woohoo! |
fix methods in pub trait generated by macro cannot be completed Fix rust-lang#12483 Check if the container is trait and inherit the visibility to associate items during collection.
Updated description
The target end point: https://gist.github.com/alexcrichton/10945968
Original issue
Ord
->PartialOrd
TotalOrd
->Ord
Eq
->PartialEq
TotalEq
->Eq
PartialOrd
inherits fromPartialEq
Ord
inherits fromEq
Ord
inherits fromPartialOrd
Eq
inherits fromPartialEq
equals
method (TotalEq
deriving needs to be redone)Ord
also derivesPartialOrd
Eq
also derivesPartialEq
An implementation of
Eq
will simply mean that thePartialEq
implementation provides equivalence rather than partial equivalence. AnOrd
implementation will add a singlecmp
method and the guarantee of thePartialOrd
implementation providing a total order.This allows code generic over
Float
to use the operators, since it will inherit fromPartialEq
andPartialOrd
. Code generic overEq
andOrd
will also be able to use the operators, despite not being designed to handle a partial order.The text was updated successfully, but these errors were encountered: