dart-lang / language

Design of the Dart language
Other
2.67k stars 205 forks source link

Allow augmenting an abstract variable with a concrete getter/setter? #3998

Closed jakemac53 closed 3 months ago

jakemac53 commented 3 months ago

@sgrekhov had some questions surrounding this recently.

I could imagine this being useful, especially for extension types, consider this:

extension type User(Map<String, Object?> json) {
  abstract String name;

  // allowed? Today we would require them to also be abstract I believe.
  augment String get name => json['name'];
  augment set name(String value) => json['name'] = value; 
}

Specifically, an abstract variable is a nice way of defining the setter/getter without also creating a backing store (presumably extension types wouldn't even allow a non-abstract field). But it is only really useful if an augmentation can actually fill in concrete implementations of the getter and setter.

Conversely though, removing the abstract nature of the field does feel like it is fundamentally altering the field. And in general we do not allow you to augment an abstract function with a non-abstract function, so it feels inconsistent.

jakemac53 commented 3 months ago

Alternatively, we could tell people to use external in this case. But that does somewhat violate the intention of external.

munificent commented 3 months ago

Yeah, I think it's totally reasonable to augment the getter and/or setter of an abstract variable declaration with an augmenting getter and/or setter. An abstract variable is purely syntactic sugar for an abstract getter (and setter if not final), so the semantics should be the same as if you'd declared an abstract getter or setter.

jakemac53 commented 3 months ago

so the semantics should be the same as if you'd declared an abstract getter or setter.

This would mean not allowing any useful augmentations though, because the augmenting getter/setter would have to be abstract as well. That may still be what we want to do in order to retain the desired semantics for augmentations (which don't generally allow converting abstract things into non-abstract things).

leafpetersen commented 3 months ago

It definitely feels to me that this should be consistent. If you can use augmentations to provide concrete implementations for abstract setter/getters, then you should be able to do the same for methods.

jakemac53 commented 3 months ago

I do think the consistency argument is a strong one, and we can just tell users to use external instead if they want a non-abstract getter/setter pair, which they can augment as they see fit. It is slightly unfortunate that the language won't require a body to be provided via augmentation in this case, which will result in a runtime failure when the functions are called if one is not provided.

I will go ahead and work on clarifying this in the spec. It is how I would interpret the spec currently, but it isn't very explicit.

munificent commented 3 months ago

It is slightly unfortunate that the language won't require a body to be provided via augmentation in this case, which will result in a runtime failure when the functions are called if one is not provided.

I think it's more than slightly unfortunate. I could be wrong, but I think a very common pattern for macros will be that the author declares a member and the macro implements it. Using abstract and having the macro turn it concrete seems fairly intuitive, but I get the argument against a macro changing modifiers/capabilities. Using external feels kind of odd and the lack of static checking seems like a problem.

I dimly recall discussing this before but maybe we need some way to declare a member as "should be filled in by augmentation".