Comments (3)
The analyzer may need to traverse several steps up the class heirarchy to check whether a class declaration is valid, but it already does this with the current
base
modifier:
This is true, but it never needs to traverse across multiple libraries. Traversing the hierarchy within a library is fairly reasonable, I think, because it's all one unit of code. If you can edit any of it, you can edit all of it. If you need to understand any part of it, you may need to understand the whole thing.
Extending that across libraries is something we did consider at some point during the design process, but I felt then and now that it's too brittle and too confusing.
In your example, with what you propose, imagine if at the point in time that class C
was authored, A
did not have base
on it yet. The author of C
believes they are writing a class that can be freely implemented, which it can at that point. Later, the author of A
adds base
, which is a breaking API change. But the author of C
might not see the breakage because class C
itself wouldn't be broken. It's just that every downstream class implementing C
is now an error.
To avoid those kinds of situations, we felt it was better for every class that extends a base
class to have to itself be explicitly marked base
too. That makes it easier for someone looking at the class declaration to know what they can do with it. With the current design, when A
is changed, the author of C
discovers immediately that their own class's affordances have changed too and they are required to put base
on it and advertise that fact to their downstream users.
You are definitely right that there's a weird edge case where you may need to traverse a chain of classes within a single library to determine if an implements
is valid or not. I hope that users rarely run into this.
Figuring out the right set of modifiers, what guarantees they offer, and what constraints they require in order to meet those guarantees, was surprisingly hard given Dart's very permissive history. What we ended up with isn't as elegant as I'd hoped, but it's hard to get a good sense for whether there are tweaks we should make that would be an improvement or not.
from language.
Thanks for responding here.
What we ended up with isn't as elegant as I'd hoped, but it's hard to get a good sense for whether there are tweaks we should make that would be an improvement or not.
I completely identify with that conclusion.
At this point, I don't feel strongly about how the base
keyword should behave—as of today, it isn't used at all in flutter/flutter or in my personal projects—but I'd still like to engage with the ideas shared here.
imagine if at the point in time that class
C
was authored,A
did not havebase
on it yet. The author ofC
believes they are writing a class that can be freely implemented, which it can at that point. Later, the author ofA
addsbase
, which is a breaking API change. But the author ofC
might not see the breakage because classC
itself wouldn't be broken.
You bring up a good point; I should amend my previous statement:
All of the guarantees from the Class modifiers documentation would still hold, as long as the base class isn't implemented within its own library.
In my mind, you'd be able do the following (though I don't know why you would want to):
base class A {}
class B extends A {}
class C extends B {}
class D implements C {}
and then another library could implement D
:
class E implements D {} // compiles without issues
Adding the base
modifier to A
would break any class that implements A
, B
, or C
without inheriting from A
, but the error message would provide a solution:
class F implements B {} // error: the class 'A' can't be implemented outside of
// its own library because it's a base class.
class F extends A implements B {} // works!
It's kind of amusing to read the benefits of the base
keyword and then watch the Flutter framework completely disregard them.
A new implemented member in a base class does not break subtypes, since all subtypes inherit the new member.
A new implemented member was added to ColorScheme
and then cherry-picked into a stable release without any mention of potentially breaking subtypes.
All implemented private members exist in subtypes.
I wasted about half an hour trying to set up the following enum:
enum AppKey implements GlobalKey {
screen1,
screen2,
screen3;
GlobalKey get _key => GlobalObjectKey(this);
@override
BuildContext? get currentContext => _key.currentContext;
}
only to realize that GlobalKey
relies on a _currentElement
getter to function, and even if I define that getter in my enum, it doesn't work.
I could make a PR that adds the base
keyword, but it wouldn't be worth it, since it'd involve adding the keyword to every GlobalKey
subclass, and it could invalidate a class that would otherwise work fine:
class MyKey extends GlobalKey implements GlobalObjectKey {
// ...
}
I feel like the right way to go is to remove the keyword requirement for subclasses:
- It enables
class C extends A implements B
- In the case of
GlobalKey
(and other similar classes), it could greatly mitigate debugging frustration without any downsides - one less word to type!
At the same time, it just doesn't feel like the benefit is worth the hassle.
Though perhaps in the future there will be another language feature proposal that synergizes with this one :)
from language.
(hid my above comment because it was long 🙃)
#4041 was the proposal I had in mind to synergize with this one.
Since a base class with a base method guarantees that the method won't be overridden outside the declaring library, I imagine that the base
keyword would start to see more use.
The additional flexibility proposed here would allow class C extends A implements B
when A
is a base class
and B
inherits from A
, which I believe to be highly valuable.
Since the base
class modifier's guarantees fail to hold if it were able to be implemented by a non-base class in the same library, I'll go ahead and modify the original proposal to prevent that problem.
from language.
Related Issues (20)
- Do we allow constructors whose implementation is omitted, but provided by augmentation? HOT 2
- Make `augmented` an error in the body of a non-redirecting generative constructor? HOT 4
- Should reading of wildcard variables be allowed in the inferred code? HOT 12
- Would Replacing LateInitializationError with LateInitializationException Be More Intuitive? HOT 3
- Better error handling - Is it possible to add "if exception" operator HOT 11
- Nullable function check with '??' causes both sides to be executed HOT 9
- Le
- Enhance override inference to infer type parameter bounds
- Allow accessing `Enum.values` inside extensions and mixins HOT 8
- URI shorthands, not for parts
- `augmented()` can't occur in an initializer list of an augmenting constructor declaration, right? HOT 4
- Allow refutable patterns in assignment patterns.
- Proposal: `base` class members
- Generic type with "extends" constraint falls back to dynamic if unspecified, rather than the declared "extends" base class HOT 2
- Static extensions declare constructors with a two-part name, do we want to use a less verbose form? HOT 2
- For extension type constructor augmentation, relax the rule about initializing formals? HOT 4
- With constructors in static extensions, do we resolve them using type arguments? HOT 4
- [Static Extensions] Can static extensions add constructors to mixins? HOT 2
- [Static extensions] Should static extensions apply through typedefs? HOT 2
- [Static extensions] Can static extensions add members to (or via) a typedef? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from language.