Skip to content

Typed equality operator #4176

Open
Open
@leafpetersen

Description

@leafpetersen

There has been a long-standing tension in Dart between the desire to have operator== be typed tightly to take the type of the receiver, and to have it typed loosely to avoid runtime errors when calling operator== on a receiver whose static type is more general than the runtime type. Examples:

class A {
  int x;
  A(this.x);
  bool operator ==(Object other) {
    if (other is! A) return false;
    return x == other.x;
  }
}
class B {
  int x;
  B(this.x);
  bool operator ==(covariant B other) {
    return x == other.x;
  }
}


void main() {
  Object a = A(3);
  Object b = B(3);
  assert(a == a);
  assert(a != b);
  assert(b == b);
  try {
    assert(b == a);
  } on TypeError {
    print("Oopsie");
  }
}

The above program, when run with asserts enabled, prints "Oopsie". Making the type of the argument to operator== on B be covariant gives nice static typing behavior, but interacts poorly with subsumption.

The current solution to this is to use the "unrelated_type_equality_checks" lint which is included in the core set of lints recommended by the Dart team.

Recently, @eernstg suggested adding a typed variant of equality: e1 =<T>= e2 which gives a static error if either e1 or e2 are not a subtype of T, and then treating the standard e1 == e2 syntax as syntactic sugar for e1 =<Object?> e2. It occurred to me that we could essentially get the same functionality using an extension method if we added a new equality operator. That is, if we added something like === (to pick a random example), then we could also add to the core libraries the following extension:

extension TypedEquals<T> on T {
  bool operator===(T x) => this == x;
}

Statically, this enforces that the argument has a type which is a subtype of the type of the receiver, without changing anything about the runtime behavior (essentially encoding the above mentioned lint into the type system). Unlike the lint above, this is an actual static type, and so it can interact with inference and other language mechanisms (e.g. #4149).

@dart-lang/language-team thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions