google / tuple.dart

A library providing a tuple data structure
https://pub.dev/packages/tuple
BSD 2-Clause "Simplified" License
194 stars 26 forks source link

operator== only checks the generic type #15

Open enetor opened 4 years ago

enetor commented 4 years ago

Current implementation of operator== checks the generic type instead of the parameterized type. For example:

class Tuple2<T1, T2> {
  @override
  bool operator ==(other) =>
      other is Tuple2 &&
      other.item1 == item1 &&
      other.item2 == item2;

What is the reason for not checking the parameterized type?

other is Tuple2<T1, T2>
lrhn commented 1 year ago

Just for posterity: I Cannot answer for the original authors, but most likely it's because the desired equality is actually element equality. The typles are used as simple value types, and it would probably be surprising if you did:

var pair = Tuple2(1, 2);
Set<Tuple2<num, num>> set = {pair};
print(set.contains(Tuple2(1, 2)));

and it printed false. Which it would, becasue var pair = Tuple2(1, 2); has no context type, so it infers Tuple2<int, int>, which contains(Tuple2(1, 2)) has a context type of Tuple<num, num> and would therefore create an instance of that.

Tuples are immutable, so the optimal runtime type would be type arguments that were the runtime type of the elements. A Tuple2<num, num>(1, 2) is strictly less useful than a Tuple2<int, int>(1, 2), because the latter can be assigned to Tuple2<num, num> and then works fine at that type, but the former cannot be used as a Tuple2<int, int> at all. (And that's what Dart 3.0's records do: Their runtime type is the record type of their fields' objects runtime type.)

So the class will allow you to give every tuple its optimal type arguments, but not punish you if it fails (not until you try a cast which won't work). Using only the values for equality is allowing precisely that

Secondarily, if the type parameters were part of equality, they should probably also be part of hashCode, which means taking the hash code of reified Type objects. It'd not be wrong to not include the types in the hash code, after all it's not incorrect for non-equal values to have the same hash code, but it would risk hash code conflicts, if a Tuple2<int, int>(1, 2) and Tuple2<num, num>(1, 2) ended up in the same hash set/map.