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

Allow overriding extensions from extensions on subclasses #1127

Closed
AKushWarrior opened this issue Aug 3, 2020 · 4 comments
Closed

Allow overriding extensions from extensions on subclasses #1127

AKushWarrior opened this issue Aug 3, 2020 · 4 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@AKushWarrior
Copy link

AKushWarrior commented Aug 3, 2020

I realize that the title is somewhat vague/difficult to parse, so let me explain what I mean. When we have an extension method, we can override members of super-classes:

extension Foo on int {
   @override
   int get hashCode => myOwnHashingFunction(this);
}

However, if those members were defined in an extension themself, the code breaks down:

extension Bar on num {
   int get hash => someHashFunction(this);
}

extension Foo on int {
   @override //shows a lint saying that hash is not a member of a superclass
   int get hash =>  myOwnHashingFunction(this); //results in an extension conflict
}

As a package developer trying to support extensions, user-defined integration, and type support in the same package, I've been butting my head into a wall due to this issue.

I recognize that this is a section of the larger extension conflicts issue; I would contend that, in this particular case, given the @override marker, the compiler should be allowed to automatically delegate calls to the subclass extension.

Another syntactical suggestion, which might ease the load on the compiler and reduce unnecessary inference:

extension Bar on num {
   int get hash => someHashFunction(this);
}

extension Foo on int delegating Bar {
   @override
   int get hash =>  myOwnHashingFunction(this);
}

Where Foo's extension-ed type must be a subclass of Bar's extension-ed type.

@AKushWarrior AKushWarrior added the feature Proposed language feature that solves one or more problems label Aug 3, 2020
@AKushWarrior AKushWarrior changed the title Allow overriding extension methods from extension methods on subclasses Allow overriding extensions from extensions on subclasses Aug 3, 2020
@AKushWarrior
Copy link
Author

My use case has to do with my assertions library, should: https://pub.dev/packages/should

I would like to a) be able to delegate some assertions to type-specific handlers and b) allow users to define their own type-specific handlers. This is possible without using extension methods, but becomes impossible using extension methods.

@lrhn
Copy link
Member

lrhn commented Aug 4, 2020

When we have an extension method, we can override members of super-classes:

extension Foo on int {
   @override
   int get hashCode => myOwnHashingFunction(this);
}

You can't, actually. That extension is always going to be shadowed by the instance hashCode member on int , unless you call it explicitly as Foo(42).hashCode. Instance members have higher priority than extension members of the same name.

However, if those members were defined in an extension themself, the code breaks down:

extension Bar on num {
   int get hash => someHashFunction(this);
}

extension Foo on int {
   @override //shows a lint saying that hash is not a member of a superclass
  int get hash =>  myOwnHashingFunction(this); //results in an extension conflict
}

That's actually also not correct. The extension Foo has higher priority than the extension Bar because its defined on a subtype. Writing 42.hash will invoke the extension getter from Foo.

What won't happen is dynamic dispatch (which I guess is what you are asking for): num x = 1; x.hash; will call the Bar getter even if x contains an integer. Dart extension methods are static extension methods. They are resolved based on the static type. There is no way to get dynamic dispatch in Dart using extension methods (or using anything other than instance members declared on the class).

The @override is a red herring. You are not overriding anything, those two getters are independent static methods from different extension declarations with different on types. The fact that one on-type is a subtype of the other is only used to prioritize which one we pick when both extensions apply. The two can be completely different declarations (say, a getter and a method), and that's only going to be a problem if the extension resolution chooses one that your invocation doesn't match.

The lint is confused, because not only is hash not a member of the superclass, there is no superclass for an extension. The on type is a type pattern to match, not a superclass to extend. What the lint should really say is that @override does not apply to extension members (like it doesn't apply to other static declarations).
(Also, the @override is just an annotation, it has no effect on the compiler. Language semantics are not affected by annotations, they are just hints that, e.g., the analyzer can use to give more warnings or errors than what the language requires, but the compilers will ignore it completely).

@AKushWarrior
Copy link
Author

Thanks for clearing this up. I'll have to do some research as to why my code isn't working then.

@eernstg
Copy link
Member

eernstg commented Aug 4, 2020

FYI, #177 is a proposal for an enhanced extension feature which is aimed specifically at supporting late binding for extension methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

3 participants