Skip to content

classes with structural identity #2246

Open
@mraleph

Description

@mraleph

I would like to suggest splitting changes to identity() rules from the records proposal into a separate proposal and allow defining classes with deep structural identity.

My proposal would be as follows: any class can be marked as having structural identity. For the purposes if this proposal we can think that this can be done by annotating a class with @pragma('value-type'), though a more user-friendly syntax can also be considered.

If identical(x, y) is called with x and y being instances of the same class C and this class uses structural identity then identical(x, y) is defined recursively as

identical(x, y) := identical(x.runtimeType, y.runtimeType) ∧ ∀ f ∈ fieldsOf(C) . identical(x.f, y.f);

Motivation

I would like to make this change to:

Current state

Currently int and double values have no discernible identity in Dart (neither natively or when compiled to JS).

String has distinct and preserved identity in native Dart, but no discernible identity when compiled to JS.

Dart VM does not preserve identity of SIMD values (in violation of the spec), but dart2js does.

Implementation Concerns

  • There is a concern that introducing special treatment into identical would degrade its performance too much. Potential mitigation could be to restrict which classes can be marked as having structural identity, e.g. requiring such classes to extend Object and not have any superinterfaces would allow to optimise all identical(x,y) invocations where either x or y are known to have a non-Object, non-structural-identity static type.

  • Capitalising on unboxing opportunities might require some from of derived pointers, consider

    @pragma('value-type')
    class Rect {
      final double x,y,w,h;
    }
    
    void op(Rect r) {
    }
    
    class A {
      Rect r;
      void foo() {
        // If [r] is unboxed then naive implementation would require
        // copying [r] to the stack to pass it to [op]. A more sophisticated
        // implementation would be to pass a (derived) pointer to [this.r] 
        // instead - something that requires some level of sophistication in GC.
        // Possible approach to pass a pair `(this, &this.r)`.
        op(r);
      }
    }
    

/cc @leafpetersen @lrhn @munificent

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureProposed language feature that solves one or more problems

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions