-
Notifications
You must be signed in to change notification settings - Fork 214
Augmentation ordering dependence with "with" and "implements" #4337
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
Comments
A tad off topic but:
Is there a dedicated issue about this? Because honnesty if my understanding is correct, the value of augmentation libraries would significantly decrease. I feel like one of the core value of augments is patterns such as: @log
int fn(int arg) => arg * 2;
...
augment int fn(int arg) {
print('Calling fn');
final result = augmented();
print('Returned $result');
return result;
} And same thing with encapsulating fields into getters/setters. If we can't do that, I don't see the point in having augmentations tbh. |
I don't think so. #4256 is the general issue for simplifying the feature.
Yes, not allowing wrapping members does reduce the expressiveness of the feature.
Looking around at some of the most widely used code generators, I don't actually see many uses of wrapping existing code. It's mostly adding new declarations and new members. So an augmentation feature that didn't let you wrap existing code would be less expressive, but still seems to cover many use cases. |
That's a chicken-and-egg problem. I certainly came accross many use-cases for code wrapping over the years. Although many of which are just difficult to implement today. Ultimately, what are we trying to solve here? For instance, Dart will most likely get static extensions at some point with how popular the feature request is. static extension on MyClass {
factory MyClass.fromJson(Map json) {
...
}
}
extension on MyClass {
Map toJson() => ...
} And if static extensions can replace the synthetic class Foo {
// Implicit Foo() ctor
}
static extension on Foo {
factory Foo(); // Explicitly define the default ctor
} Then we could support data classes purely with extensions: @data
abstract class Person {
String get name;
int get age;
}
// Generated
static extension on Person {
factory Person({required this.name, required this.age}) = _PersonImpl;
}
class _PersonImpl extends Person {
_PersonImpl({required this.name, ...});
final String name;
final int age;
}
extension on Person {
Person copyWith({...}) => ...
} That would be enough to remove a lot of There would be some corner cases. But that's no different than with augmentations as currently discussed ; which would also be limited in some aspects. So to double down: What is the purpose of augments if we can't wrap a member/declaration? :) |
When I looked at existing uses of code generators, I tried to figure out what augmentation features they would use if those features existed. You're right that there are probably code generators that don't exist at all because the capabilities to enable them aren't there.
We're trying to improve the user experience of packages that provide or use code generators.
Personally, I don't think static extensions are a great fit for adding what should feel like "real" members onto a class.
I think we're trying to figure out what the right scope for the feature is so we can finish it off and ship it. Without macros, it doesn't need to be as maximal as it was originally designed. It's a really complex feature. So we're trying to figure out if there is a way to scope it down such that users still get most of the most useful functionality at a fraction of the implementation cost on our end. |
My feeling is that the discussions regarding augmentations are backward. Usually, a problem is expressed. Then various proposals to solve that issue are made. It doesn't sound like there's a clear list of problems we're trying to solve. If we're looking at solving codegen pain points, I'd say that wrapping top-level functions and existing class members is definitely an important one. For starter, I write code-generators as a workaround to limitations in language/ One of such problems is function composition. Think middlewares, dependency-injection to inject a parameter, function currying, ... The initial augmentation proposal at least solved some of it. @log
@myMiddleware
void fn() {} Although it wouldn't solve currying/DI like: @widget
Widget example(BuildContext context) => ...
void main() {
runApp(example()); // The `context` parameter was injected by @widget
} With this discussion, it sounds like even the |
We talked about this on the language team and we're OK with continuing to specify augmentation application order, so I can go ahead and close this. The somewhat off-topic discussion about augmentations in general is a good one. It's probably best to continue it on #4256. Though, for what it's worth, we're still leaning towards eliminating |
I'm working on simplifying augmentations now that we don't need to support full macro generality.
My recollection from a previous language meeting is that we want eliminate
augmented
and allowing an augmentation to wrap existing code. The goal is to make it so that an introductory declaration and all of its augmentations form a non-overlapping union of disjoint declarations. That way, we don't have to define any sort of augmentation application order.Disallowing an augmentation from replacing/wrapping an existing (non-"augmentation abstract") member covers much of this.
But augmentations are also allowed to append to a type declaration's
with
andimplements
clauses. I'm not sure if the order of types in animplements
clause is user-visible, but the order of mixins in awith
clause certainly is if they happen to override the same instance member.Any thoughts on how we should handle this in augmentations? We do know that some code generators have use cases for adding to the
implements
andwith
clauses, so disallowing augmentations from touching those entirely is probably too restrictive.The text was updated successfully, but these errors were encountered: