-
Notifications
You must be signed in to change notification settings - Fork 205
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
Should extension structs support sub-classing? #2368
Comments
@srujzs and @sigmundch have indicated fairly strongly that this is close to a requirement for the JS interop use case, so this discussion may be somewhat pro-forma. Overall, I see almost nothing objectionable to allowing inheritance without overriding. The discussion of overriding is tracked separately in dart-lang/sdk#2369 . There is one concern around allowing inheritance across libraries, not because of extension structs, but because of structs. Discussion of this for structs is tracked in dart-lang/sdk#2367 . We of course do not need to be consistent between extension structs and structs here, but it's slightly odd if we don't. This might push towards splitting the two features further apart (e.g. into the |
What does it mean to extend a view/extension struct. Presumably it means that you include the extended view's methods and add your own on top (whether overriding or not). That sounds safe. The big question is whether the extension struct types has a subtype relation.
That doesn't seem particularly valuable, since you can always cast back to the underlying viewee type and back into the supertype. It's the type of the underlying object which really matters for which assignments are valid, not the extension struct types. |
The subtype relationship may be more convenient, to such an extent that it matters: extension struct Node(JSObject that) {...}
extension struct Element(JSObject that) extends Node(that) {...}
void f(List<Node> nodes) {...}
void g(List<Element> elements) {
... f(elements) ... // OK when `extends` implies subtyping.
// As opposed to:
... f(elements as List<Node>) ... // Required when not.
} In particular, we may get |
My goal with this proposal is to minimize surprise for consumers of the API. With that in mind, I had the design goals in mind that I listed here. Specifically for extension, I think a consumer will be very surprised if |
Rocking the boat a bit here - I'd like to revisit our requirements from JSInterop and see if "extension/subclassing" is the right model here. Some alternatives to think about:
I believe most of our goals around subclassing are really about having the ability to selectively forward nonvirtual methods. By selectively here I mean that sometimes we want to forward all, sometimes some and hide others, and sometimes we want to shadow some. Note: the fact that we need to hide APIs brings already a semantic difference from the traditional One proposal that comes to mind is to explicitly introduce the concept of forwarding nonvritual methods. For example: extension struct B(int x) {
get isOne => x == 1;
}
extension struct A(int x) forwardsto B {
// the forwardsto gets expanded into:
// get isOne => B(x).isOne
} Such forwarding concept will allow us to handle several of our use cases:
Also worth noting that some of these use cases sometimes introduce the need to shadow multiple classes in a class hierarchy together, which creates non-traditional multiple inheritance relationships. For example: // library 1 defines:
extension struct Node(JavaScriptObject x) {}
extension struct HtmlElement(JavaScriptObject x) forwardsto Node {}
// library 2 defines:
extension struct Node(JavaScriptObject x) forwardsto library1.Node /*with some shadowing */ {}
extension struct HtmlElement(JavaScriptObject x) forwardsto library2.Node, library1.HtmlElement /* with some shadowing */ {} That said, this kind of shadowing of class hierarchies may be too specific to one single use case of ours. As such, we would be OK if we decide this is not something we will design for. If needed, we can address all the shadowing through codegen instead (it would mean though that we replicate every definition and manually do the expansion I presented earlier with "forwardsto", though) |
What if we introduced method forwarding in general, in a way that also applies to classes. class Something implements Foo {
FooBar x;
// ...
export x { // or whatever keyword.
foo,
bar,
operator+,
interface Foo,
}
} which means that the The The expression after Shorthands: export e; // same as `export e {interface <static type of e>}` (Getters/setters may be a problem, if Since each forwarding is independent, you can forward extension/non virtual methods as well, it all depends on the static type of the exported expression. This can then be used in any declaration, whether it's a class, mixin, struct or view. It's just shorthand for writing forwarders, (Hmm, that means we can even do |
I do like that. and the export syntax is growing on me. I could even imagine using the show/hide as well: class A {
...
export e
show add, remove;
export f
hide foo;
} |
I like this syntax. This would cover my use case in #3748 |
Everything about the |
In the extension struct proposal (#2360), I propose that extension structs should be forbidden from extending other extension structs. At the end of the proposal, there is a discussion of allowing this. This issue is for discussion of whether to allow this, and with what constraints and semantics.
The text was updated successfully, but these errors were encountered: