-
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
Views: Using non-virtual methods as a basis for view classes #2402
Comments
cc @kallentu |
How about treating non-virtual methods as static methods with a parameter for class A<T> {
final T data;
const A(this.data);
/// Simply returns [data].
nonvirtual T getProperty() => data;
// translated to:
static T getProperty<T>(A self) => self.data;
}
/// A specialized version of [A] that only works with [int]s.
class B extends A<int> {
const B(super.data);
/// Returns the string form of [data], formatted with commas.
///
/// This is normally an error since [String] is not a subtype of [int], but here
/// it is okay since [A.getProperty] belongs to [A]'s static interface.
String getProperty => formatWithCommas(data);
}
void main() {
print(A(1000).getProperty()); // "1000"
// translated to
print(A.getProperty(A(1000))); // "1000"
/// This is a regular virtual method call, unaffected by [A]
print(B(1000).getProperty()); // "1,000"
} With this interpretation, non-virtual methods are just instance methods that are only part of the class's static interface. And Dart users don't have to learn a new concept. So, for the questions above:
No, since the non-virtual method is not even inherited.
No, since non-virtual methods are only in the static interface of the class that defines them. ...Don't ask how this will interact with #356. |
This is the underlying semantic model of the proposal above. Whether we choose to "inherit" the dispatch in subclasses or not is a largely orthogonal choice, I think.
Yes, this is a choice we can make, though we then need some other mechanism to provide code re-use for the JS interop use case. |
I see that @sigmundch had already filed an issue on this idea here. |
Can we reuse the show/hide mechanisms from views to resolve these two issues? Presumably for the first case, we should get a static error asking the user to resolve either through hiding one method, showing the methods you only care about (and therefore purposefully do not result in collisions), or implementing their override. For example: class A {
nonvirtual String get val => 'A';
}
class B {
nonvirtual String get val => 'B';
nonvirtual String get otherVal => 'B';
}
class C implements A hide val, B {}
class D implements A, B show otherVal {} // implicitly hides B.val
class E implements A hide val, B hide val {
// Multiple hides may be cumbersome, maybe an `@override` implicitly should hide any conflicts with that member?
nonvirtual String get val => 'E';
}
void main() {
A().val; // 'A'
B().val; // 'B'
C().val; // 'B'
D().val; // 'A'
E().val; // 'E'
} |
Closing, we have a design. |
In discussion with @sigmundch @rileyporter @srujzs and @joshualitt this afternoon, we discussed basing the view feature around non-virtual methods. This provides a fairly clean semantic model that potentially meets most requirements, but it's not clear how to build it out, and it has a lot of surface area. This issue is not to make a full proposal, but to provide a venue for discussing the idea, and seeing if it's worth trying to flesh out into a proposal.
Non-virtual methods.
Suppose we added non-virtual methods to classes (ignore views for now). To start with, let's assume that we simply mark methods as
nonvirtual
.There are some questions about exactly what we require.
Do we require compatible signatures in subclasses (we don't have to).
We could make this an error, or allow it, I think - either is valid. Extension methods would allow it.
Do classes "inherit" nonvirtual things that they don't override (the way that they inherit extension methods), or are nonvirtual methods only available on the interface that they are defined in?
If we view these as analogous to extension methods, both should work, but we don't have to take that model. There are lots of questions about how we would resolve conflicts, and about how we would ensure that
this
calls in the body of "inherited" methods continued to reach valid method targets.Applicability to views
We can now start by saying that a view is (in part) a class which is required to only have nonvirtual methods, with the following implications:
So a view is a restricted class which:
For the interop use case, this covers the following needs/wants:
If we take the extension method semantics approach (where nonvirtual methods on the supertype apply to the subtype unless there is a more specific nonvirtual method available), this also satisfies the following two interop requirements:
Things not addressed by default:
Syntax
Putting "nonvirtual" on every method seems a little painful, though maybe not too bad. Alternatives:
view class
methods (or whatever) are nonvirtual by default, and only require the keyword on normal class methodsIf we use the extension method resolution semantics, we could consider using "extension" instead of "nonvirtual", since the semantics essentially become the same as defining an extension method with the class that gets imported with the type.
Discussion
Pros:
Cons:
cc @munificent @lrhn @eernstg @natebosch @jakemac53 @stereotype441 @mit-mit @johnniwinther @chloestefantsova @srawlins @mraleph
The text was updated successfully, but these errors were encountered: