Skip to content

TypedDataList comparison #56409

Closed
Closed

Description

@curt-weber

Typed data (Float32x2, Float64x4, etc.) doesn't override operator == which makes comparisons only possible by adding a separate equals comparison.

But it requires all calling code to use the specialized equals call and ignore == or the equivalency will silently fail.

import 'dart:typed_data';

extension type Number._(int _number) {
  Number(int number) : _number = number;
}

extension type Point._(Float64x2 _point) {
  Point(double x, double y) : _point = Float64x2(x, y);
}

void main() {
  print(Number(0) == Number(0)); // true
  print(Number(0) == Number(1)); // false
  print(Point(0, 0) == Point(0, 0)); // false
  print(Point(0, 0) == Point(1, 1)); // false
  print(Float64x2(0, 0) == Float64x2(0, 0)); // false
}

Couldn't TypedDataList<E> override == and perform list comparison?

Activity

  1. dart-github-bot commented on Aug 8, 2024

    @dart-github-bot
    Collaborator

    Summary: The TypedDataList class does not override the operator == method, forcing users to rely on a separate equals method for comparisons. This leads to potential inconsistencies and silent failures if users inadvertently use == instead of equals. The user suggests overriding == to perform list comparison for consistency.

  2. added
    area-core-librarySDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.
    triage-automationSee https://github.com/dart-lang/ecosystem/tree/main/pkgs/sdk_triage_bot.
    type-enhancementA request for a change that isn't a bug
    on Aug 8, 2024
  3. added and removed
    triage-automationSee https://github.com/dart-lang/ecosystem/tree/main/pkgs/sdk_triage_bot.
    on Aug 12, 2024
  4. lrhn commented on Aug 12, 2024

    @lrhn
    Member

    Plain Dart Lists also do not override Object.==. It's a design choice to not have == on mutable objects, because being equal by content may be a fleeting property that can change at any time.

    Also, it requires having a non-trivial hashCode that agrees with it.

    It's unclear whether Uint8List.fromList([1, 2]) should equals Uint32List.fromList([1, 2]).
    They are lists with the same length and equal elements, but they are also clearly different in very significant ways.
    How about Float32List.fromList([1.0, 2.0]) which also contains elements that are equal with the other two?

    If we have that kind of equality, then it's not going to be efficient. It won't be using memcmp on any typed-data lists that have different number types.
    Being very efficient would be a reason to have special equality, but if it's only for two Uint8Lists, meaning that the equality will have to compare the types first to see if it can use the efficient branch, then there is little point. (And that would still not work for a Float32List or Float64List that contains NaN values, which should not be compared as bits).

    I don't see us changing this design at the current time.
    If we did it for typed-data lists, why not for normal lists (which is definitely going to be list length + element equality), but then type-data lists are Lists, so they should have the same inefficient equality.
    And doing something more efficient for typed-data lists could block adding equality to plain Lists. (If we wanted to.)

    I'll recommend just using ListEquality from package:collection.

  5. added
    closed-not-plannedClosed as we don't intend to take action on the reported issue
    on Aug 12, 2024
  6. curt-weber commented on Aug 12, 2024

    @curt-weber
    Author

    Thanks for the detailed response. I had noticed other issues being raised about the possibility of const typed data and wondered how equality would be handled in that scenario. That makes sense that it would need a special case from plain List - but since some of the typed data are immutable (Float64x2) it seemed like a good candidate. But I can see how special casing it would be inefficient. If extension types could override this behavior (and delegate to ListEquality), that would be preferable, but not being able to override Object members prevents this. Not being able to override Object (for extensions), combined with trivial equality on typed data makes it difficult to implement a 2d vector backed by typed data. Both Offset (Flutter) and Point (dart:math) implement comparison.

  7. lrhn commented on Aug 13, 2024

    @lrhn
    Member

    If two constant Uint8List values with the same value would likely be canonicalized to the same object instance, at which point == would work, since the Object.== implementation is just => identical(this, other);.

    I would have no problem with Float64x2 having ==, it's not a list and it's not mutable, it really is a data object.
    It probably still wouldn't be able to be done as a memcmp, since we'd want to consider NaN values not equal to themselves.

  8. curt-weber commented on Aug 13, 2024

    @curt-weber
    Author

    Good point about const, the equality would work there. IMO the way equality works is best now for Uint8List as length + element equality could be a performance cliff if the comparison is made implicitly as part of a larger data object. Not being able to override this behavior though is the limiting factor as an application can't decide if that comparison is worthwhile. Float64x2 == comparison would be great, and it would solve 2d vector, but a Rect would need to be backed by a Float64List and wouldn't be able to benefit here - there is Float32x4 but not Float64x4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-core-librarySDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.closed-not-plannedClosed as we don't intend to take action on the reported issuelibrary-typed-datatype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions