-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Champion "Support for == and != on tuple types" (15.7) #190
Comments
Should just support extension operator on |
@Thaina No, |
Retracting my up-vote for this proposal. As I read "No, Yes, for historical reasons, we have ended up with multiple ways of testing for equality and this is one of those "we are stuck with it" things that can't be undone. But perpetuating this messy situation by having new types like We would be better off with the team just leaving this topic well alone and implementing the "extension everything" proposal instead. Then 3rd party libraries can implement |
so, |
Tuples being just a loosely bound sequence of values I think that it makes more sense to be able to compare those values irrespective of the container. (int, int) tuple1 = ...;
(int, int) tuple2 = ...;
if (tuple1 == tuple2) { ... }
// the same as
if ((tuple1.Item1 == tuple2.Item1 && tuple1.Item2 == tuple2.Item2)) { ... } |
In the following code, var x = double.Nan;
var y = double.Nan;
var result = (x == y) == x.Equals(y); But it's You appear to be proposing that, just because the CLR and language teams couldn't agree on whether |
struct S { }
(S, S) tuple1 = ...;
(S, S) tuple2 = ...;
if (tuple1.Item1 == tuple2.Item1 &&
tuple1.Item2 == tuple2.Item2) // oh dear: compiler error
|
Yes, and I have no problem with that. I don't think that the fact that the two values are contained within a tuple should matter. |
@DavidArno Did you know that comparison operators and |
@orthoxerox, @HaloFour, @ig-sinicyn, So you have all done a lot of "no, no, can't be |
The solution is that the developer has to use |
@DavidArno the compiler has to rewrite the |
The following would have to be a compiler error therefore, as bool F<T1,T2>((T1, T2) x, (T1, T2) y) => x == y; |
@DavidArno it's an error now and I don't see how it is related to equality of tuples. |
Apologies: the code was wrong. I've updated it to what I meant to say. |
So, how about the compiler first attempts to resolve I don't know that I like that, though. If you needed the container of those two values to matter, enough to define their equality, then you probably shouldn't be using tuples and you should define a proper type instead. |
@DavidArno Yes, it looks like it will have to be a compiler error, just like this function is right now: bool F<T1,T2>(T1 x1, T2 x2, T1 y1, T2 y2) => x1 == y1 && x2 == y2; P.S. I've just spent an hour enumerating all the equality mechanisms of the CLR and I've yet to cover the interfaces. |
That's not what's happening. == implement IEEE754 semantics. .Equals impelments .Net equals semantics. IEEE requires that == not be reflexive for NaN. However, .Equals is required to be reflexive. i.e. object.Equals states that "a.Equals(a)" must be true for all instances. If we broke IEEE semantics that would be a problem for tons of customers. If we broke .net semantics that would be a problem for tons of .net use cases (for example, you could not use a double effectively as a key inside a hashtable). This approach gives both sets of customers the semantics they require. |
No, it really isn't. For example, today, if i have: byte b = 1;
int i = 1;
Console.WriteLine(b == i);
Console.WriteLine(b.Equals(i)); I will print "True, False".
The problem compounds with tuples. Tuples just aggregate data. If you aggregate data that uses to compare the same with == but now compares differently (or vice versa), then tuples no longer act properly a composition concept. |
Using hte above example, if we do not support == on tuples then you can get the following badness: byte b;
string s;
(byte, string) t1 = (b, s);
(int, string) t2 = (b, s);
Console.WriteLine(t1.Item1 == t2.Item1 && t2.Item2 == t2.Item2);
Console.WriteLine(t1 == t2); // doesn't currently compile. But we would like it to. Can't be implemented through .Equals
Console.WriteLine(t1.Equals(t2)); The top and bottom lines will print 'true', and 'false'. I strongly believe the second line shoudl print 'true' and that it should just be exactly transformed as: t1 == t2
// becomes
t1.Item1 == t2.Item1 && ... && t1.ItemN == t2.ItemN This, of course, then means that nested tuples work properly (something that definitely does not work with .Equals). |
Ha, ha, thanks @CyrusNajmabadi, the idea of someone trying to use a double as a key in a hashtable is going to keep me chuckling all day! 😁 Even JavaScript, where it'll let black equal white every other Thursday except on leap years doesn't treat NaN as equal. The idea that the .NET team's dogma was so strong that they'd break an international standard just to enable doubles to be used as Dictionary keys is mind-boggling. Anyway, that aside, you didn't address the important point from above. If a value tuples' types are structs, they may not implement |
Using a double as a key is completely fine to do.
It's interesting that you complain about things being inconsistent, but then complain when .net insisted that .Equals behave consistently :)
The exact way that == works for types that don't have an available == operator. I'm stating that == for tuples is defined as: (x1, ..., xn) == (y1, ..., yn) <==> x1 == y1 && ... && xn == yn == on the tuple will work in precisely all the circumstances that == works for the constituent elements.
I never said that. Please do not put words in my mouth. I simply gave an example of how the .Equals behavior is desirable and consistent. I did not say that it was done "just" for this reason. -- Also, to be clear, you never addressed the other points i made. For example, that == and .Equals are not equivalent today for ordinary numeric types other than double. Today == will return true when .Equals does not. == follows the rules of C# while .Equals follows the rules of .Net. Making it so that we have a type in C# which composes other C# types, but which does not compose == appropriately just leads to the composition not actually happening fully. |
Note: having .Equals be symmetric is nice for lots of reasons. After all, while it would be very weird to have "a.Equals(a)" be false, it would be even more confusing and difficult to use .net effectively if doubles didn't support this concept. After all, the following could then be false: list.Add(d);
Console.WriteLine(list.Contains(d)) Effectively you would make it so that doubles could not actually work effectively as any sort of object in any sort of container. You could not use them effectively in linq. They would always be something that never meshed well with the rest of .Net. So .Net implements .Net semantics. If you want IEEE semantics, you can use the simple operators. |
I'd like the table the Double discussion as the interesting and esoteric behaviors of Double are not what are important here. All the reasons for tuples needing == support can be explained with simpler types like int/long. For people who don't think == should be lifted to tuples, can i ask why you think the following should behave differently: int i = 0;
long j = 0;
Console.WriteLine(i == j); // what do you think this should print?
(int, int) t1 = (i, i);
(long, long) t2 == (j, j);
Console.WriteLine(t1 == t2); // what do you think this shoudl print? If you think the answer should be 'true' then 'false', can you explain why you think that two values which previous were |
int i = 0;
long j = 0;
Console.WriteLine(i == j); // what do you think this should print? I think it should print exactly the same as for: int i = 0;
long j = 0;
Console.WriteLine(i.Equals(j)); Whether it's As Eric Lippert says in his Sharp Regrets: Top 10 Worst C# Features article, equality in C# is one of its top ten worst features. But what is done is done and we must live with it. However, watching you try to defend it, rather than just admitting it was a mistake that we can all learn from, is more than a little frustrating. I'm still confused as to how you think
As per my previous example, var x = (new S(), new S());
var y = (new S(), new S());
if (x == y) ... |
@DavidArno What you proposed might be ideal but it could be breaking change so that's a problem. Sometimes we need to deal with legacy |
Ok. Given that that's not how C# has behaved since v1, that seems bad. You are complaining about inconsistency, but would not like C# vNext to be inconsistent with C# v1-v7.
var x = (new S(), new S());
var y = (new S(), new S());
if (x == y) ... it's exactly as i stated, it would be equivalent to: if (x.Item1 == y.Item1 && x.Item2 == y.Item2) If S has no == operator then i would expect an error. precisely as i would if i wrote: var v1 = new S();
var v2 = new S();
Console.WriteLine(v1 == v2); |
e.g. In those cases it might make sense, but I think it might not always be so obvious how an operation should be distributed, if it should be at all. For the most appropriate behavior it might be necessary for the compiler to special-case the operations in question. |
That directly depends on the shape implementation. I'm not sure why "special-casing" would make that more "appropriate". I think this would be the most sensible starting point for designing shapes. Once it's possible to model various operations through shapes there is no reason to special-case such features. IMO it'd be worth it to invest on the general solution rather than special-casing all these into the compiler. |
I would normally agree with you, except I do think that it might be appropriate in the case of tuples. Because they should really be such a basic and integral feature I'm all in favour of the compiler 'inlining' the body of the '==' method for tuples, even if the method exists (at least notionally) as the operator '==' on a ValueTuple where T1, T2 etc are members of an Eq typeclass. |
The case for inlininig was discussed at length in other threads before and concluded that roslyn is not the place for these optimizations (even though there were some good arguments against it, for instance, dotnet/roslyn#15644). In fact, that was my first concern regarding how shapes are implemented (#164) but as others have said, invocations of shape methods get specialized by the JIT. |
How is this feature different from tuple patterns? (1, 2, 3) is (1, 2, 3)
(1, 2, 3) == (1, 2, 3) I think it's not, other than that you could compare variables with |
@alrz This feature allows comparing two tuple variables ( |
That doesn't sound right to me. Surely positional patterns for tuples will support recursive patterns, not just constants?
@alrz,
|
Yes, but the only actual values that you can compare must be themselves constants. So if you're comparing to other variables via pattern matching you'd have to use pattern variables and guards: public void Foo((int, int) x, (int, int) y) {
switch (x) {
case (var a, var b) when a == y.Item1 && b == y.Item2: ...
}
// vs
if (x == y) { ... }
} I assume that you could also use it as shorthand for comparing equality of multiple items: public void Foo(int a, int b, int c, int d, int e, int f) {
if (a == d && b == e && c == f) { ... }
// vs
if ((a, b, c) == (d, e, f)) { ... }
} |
I absolutely love the idea of: public void Foo(int a, int b, int c, int d, int e, int f) {
if ((a, b, c) == (d, e, f)) { ... }
} Hopefully, @jcouv can work the same magic as he did for the immodestly named Arno Assignment Pattern so that the tuples get optimised away here too. |
@DavidArno |
@jcouv Couldn't the ValueTuple requirement be fixed the same way it was for deconstruction? |
@jnm2 It's not obvious. Still looking into it ;-) |
Summary
Support == and != on tuples.
For example
(x, y) == (1, 2)
(which would be equivalent tox == 1 && y == 2
).See also dotnet/roslyn#13155
LDM history:
Started prototype at tuple-equality
The text was updated successfully, but these errors were encountered: