Skip to content
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

Boilerplate mixin for poor man's hashCode, operator== and toString methods #19181

Closed
DartBot opened this issue Jun 3, 2014 · 5 comments
Closed
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. closed-not-planned Closed as we don't intend to take action on the reported issue type-enhancement A request for a change that isn't a bug

Comments

@DartBot
Copy link

DartBot commented Jun 3, 2014

This issue was originally filed by ochafik@google.com


Implementing boilerplate methods is cumbersome and error-prone, despite nice building blocks in quiver-dart and collection/equality.dart.

Relying on IDEs to generate them doesn't quite solve code bloat / maintenance issues, and relying on protobufs is a bit overkill / not idiomatic.

I propose to add a very simple Boilerplate mixin that implements these methods using collection/equality.dart on a list of public field values (see boilerplate.dart in attachment).

The list of field values can either be inferred using mirrors, or provided explicitly with a dedicated override (this override being shorter than the methods it helps implement).

This proposal addresses the general bean-like boilerplate but could be optimized for immutable types (Issue #10551, Issue #501) by caching the hashCode (either explicitly or by detecting the absence of setters).

Long story short, here's how Boilerplate could be used:

With mirrors:

    @­MirrorsUsed(targets: const[Foo, Bar], override: "*")
    import 'dart:mirrors';
    import 'boilerplate.dart';

    class Bar extends Boilerplate {
      final int i;
      Bar(this.i);
    }

    class Foo extends Bar {
      final int j;
      final String s;
      Foo(int i, this.j, this.s): super(i);
    }

    print(new Bar(1)); // "Bar { i: 1 }"
    print(new Foo(1, 2, "3")); // "Foo { i: 1, j: 2, s: 3 }"

    assert(new Bar(1) == new Bar(1));
    assert(new Bar(1) != new Bar(2));

Without mirrors:

    import 'boilerplate.dart';

    class Bar extends Boilerplate {
      final int i;
      Bar(this.i);
      @­override get fields => { "i": i };
      @­override get className => "Bar";
    }

    class Foo extends Bar {
      final int j;
      final String s;
      Foo(int i, this.j, this.s): super(i);
      @­override get fields => { "i": i, "j": j, "s": s };
      @­override get className => "Foo";
    }

Note that Boilerplate can be safely mixed in at any level:

     class A extends Boilerplate {}
     class B extends A with Boilerplate {}

I understand this doesn't solve all the potential use cases, but I think it's acceptable enough to make it to some pub package, for users who agree with the following explicit limitations:

  • Only uses public fields by default,
  • No special handling of reference cycles: user must avoid them responsibly,
  • Not optimized for speed,
  • Subsequent calls of hashCode on an object with mutable fields may yield different values (well, just as in Java).
  • Requires mirrors (with proper MirrorsUsed annotation) or explicit definition of fields and className.
    Attachment:
    boilerplate.dart (6.25 KB)
@sethladd
Copy link
Contributor

sethladd commented Jun 4, 2014

Consider publishing your own pub package, without having to file this feature request.


Added Area-Library, Triaged labels.

@lrhn
Copy link
Member

lrhn commented Jun 4, 2014

If the mixin falls back on mirrors if the fields getter doesn't return anything usefull, them mirrors are required in order to import the mixin in all cases.
The mirror import in the mixin's library can't be MirrorsUsed annotated, since it doesn't know the classes it will be used on, and the mirror code will always be there, even if the usages don't use it.

That could be alleviated by having a non-mirror mixin as well, which you can use if you know you are going to implement fields/className.

@DartBot
Copy link
Author

DartBot commented Jun 4, 2014

This comment was originally written by olivier.chafik@gmail.com


Thanks for the comments / suggestions!
So... release early, release often:
http://pub.dartlang.org/packages/boilerplate (has an ExplicitBoilerplate variant).

@srawlins
Copy link
Member

srawlins commented Jun 8, 2014

Editor-generated code discussed in Issue #18205.

@lrhn
Copy link
Member

lrhn commented Sep 29, 2014

We still have no plans to add this to the platform libraries, so use the package.


Added NotPlanned label.

@DartBot DartBot added Type-Enhancement area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. closed-not-planned Closed as we don't intend to take action on the reported issue labels Sep 29, 2014
@kevmoo kevmoo added type-enhancement A request for a change that isn't a bug and removed priority-unassigned labels Mar 1, 2016
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-core-library SDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries. closed-not-planned Closed as we don't intend to take action on the reported issue type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

5 participants