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
Augmented constants? Really? ;-) #3721
Comments
Possibly, augmenting consts is no worse--from the compiler's point of view--than shadowing them?
In any case, yes, definitely an important one to consider for macro metadata. |
It's not impossible to support augmenting constant, but it's also weird. We may have to rule that macros can't change the value of any constant that occur in a macro application annotation. (We may want to have rules that macro generated names can't change the resolution of anything that was part of triggering or maybe executing a macro. If so, we probably also don't want macros to change the meaning of constants that were used. All in all, I think we can safely allow it, and we can also allow macros to change constants, as long as we can live with the fallout of the change. (The model of an augmentation application being equivalent to renaming the parent to a fresh privat name that's not used anywhere else also applies here: const String _$fresh$c = "Original";
const String c = "Augment: ${_$fresh$c}"; and if the resulting program would works, so should the augmentation. Anything that saw the pre-augmented constant may be out-of-date, but we get to decide whether that's a problem, and for whom. |
It does bring up the question of what the syntax is for an augmenting declaration that doesn't change something. final int x = 42;
@Banana()
augment final int x; Is the second declaration allowed, and does it not change the initializer expression? @Banana()
augment final i; and not specify a type? (Can I omit the For (The spec is clear: You cannot augment a variable with a variable and not supply an initializer expression. The spec also claims that "Since the initializer is the only meaningful part of the augmenting declaration, an initializer must be provided.", which this example proves wrong - annotations matter too - so maybe we should allow it.) |
I do think that augmenting constants is potentially useful - although if it was sufficiently motivated I could see limiting it such that you can only fill in a missing initializer, and not replace an existing one. In particular, I could imagine a macro which generates a large constant object which is derived from the program structure - for example as a dart:mirrors replacement. Or a constant encoder/decoder for a specific type, etc. |
Good point, we should allow it. You can also add doc comments. |
@jakemac53 wrote:
I'm not 100% sure what we should allow. ;-) But certainly we can allow an augmenting declaration of a constant variable to add DartDoc comments and metadata. I'm just not so sure about the overall benefits of allowing an augmenting declaration to replace the value of the constant by some other value. Filling in a missing initializing expression might be benign (at least, a macro expansion step that must know the value could report a compile-time error if it is not yet available).
It sounds like we could implement the macro such that it generates the constant variable declaration as a whole, rather than insisting that there is a constant variable declaration (with no initializer, assuming that we introduce the syntax to do that) which is then augmented to have an initializing expression. @davidmorgan wrote:
(I'll adjust the example to use parts, based on the assumption that we're about to do that for this feature in general) // --- Library 'main.dart'.
import 'declares_a.dart';
part 'augment.dart';
const String c = a;
// --- Library augmentation part 'augment.dart'.
part of 'main.dart';
const String a = "some other value"; In this particular case we wouldn't actually reinterpret the meaning of any declared names, because the semantics of parts already implies that the declared names in However, it is possible to create a situation that gives rise to a re-binding of a name (e.g., when A proposal about re-binding errors is given near the end of this comment). |
I think we can safely allow constant initializers to be augmented. There is nothing particularly special about constant initializers, other than the expression needing to be a constant expression, but that is also what would potentially allow One pattern I would expect to see used, is the accumulating list: const allTheFoos = [];
@Foo("something")
class Banana {}
@Foo("other")
class Apricot {} where the augment const allTheFoos = [...augmented, FooEntry<Banana>("something")];
augment const allTheFoos = [...augmented, FooEntry<Apricot>("other")]; which would "just work", and it can work with maps too. That's definitely something people doing dependency injection would love. The next question is whether we can avoid having augmented constants. If people can get the same effect anyway with some more verbose workarounds, then we might as well give it to them directly. If they can't change the binding of a constant variable, will they just make that constant variable call a const contructor and augment that constructor instead? (Maybe, if it can work.) const List<FooEntry> allTheFoos = const _AllTheFoos();
extension type _AllTheFoos(List<FooEntry> _) {
const _AllTheFoos() : _ = [];
}
augment extension type _AllTheFoos(List<FooEntry> _) {
augment const _AllTheFoos() : augment this._ = const [...augmented, FooEntry<Banana>("something")];
}
augment extension type _AllTheFoos(List<FooEntry> _) {
augment const _AllTheFoos() : augment this._ = const [...augmented, FooEntry<Apricot>("other")];
} If we can augment initializer list entries, then this ... probably still doesn't work, because If it's useful, well-defined and not technically any more complicated than augmenting non-constant variables, I don't see a reason to forbid it. If changning the meaning of a constant is a problem for macros, macros can choose how to deal with that. The language itself has not problems. |
Closing: OK, I'll stop worrying about this. If it does create a huge amount of complexity then we can return to the issue. |
Thanks to @sgrekhov for bringing this up. Consider the following example:
Do we support
augmented
in constant variable declaration augmentations? I'd assume that we very much want to avoid this, because it brings even more complexity into the management of constant expression values involving macro phases and the like. On the other hand, it is a glaring inconsistency if we don't support it.I'd recommend that we make it an error to augment a constant declaration. We may then revisit the topic later on if needed.
Alternatively, we should support addition of metadata to a constant variable declaration, but not an augmentation of the initializing expression. In that case we should probably change the syntax such that an augmenting constant variable declaration does not have an initializing expression.
@dart-lang/language-team, WDYT?
The text was updated successfully, but these errors were encountered: