-
Notifications
You must be signed in to change notification settings - Fork 208
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
It is inconvenient that the default value of a formal parameter is constant #140
Comments
One starting point for addressing this request is the reference to Kotlin given in #137. |
I am curious if this issue has been reconsidered/revisited due to the incoming non-nullable types, default values of parameters that are non-nullable would require that any user created classes/types would have to have a const value just to be able to default it. which may not always be possible. |
We have made some changes to the rules about default values for parameters. In particular, named parameters can be Moreover, abstract methods do not have to have a default value for an optional parameter, even when its type is non-nullable, because the run-time semantics will never rely on such default values anyway. However, this actually doesn't make much difference here: This issue is essentially about allowing default values to be normal expressions in some scope (maybe: the instance or static scope of the enclosing class, or the library scope, depending on where the parameter is declared). Those expressions would then be evaluated at each invocation where the corresponding parameter is omitted. The requirement that a default value must be assignable to the type of the corresponding parameter remains unchanged, and the requirement that a default value must be specified iff the dynamic semantics may use it also remains unchanged. So the changes that we're introducing along with non-nullable types will just carry over directly to the kind of defaults that this issue is targeting. |
Note that #951 makes a similar request. |
But assuming we care a lot about allowing a I personally think it is incredibly desirable to create such So given Is there generally a solution that would satisfy both A and B or is the current conclusion that we are simply trading B for A? |
No, there is no design which satisfies both A and B. Trivially, if the default value expression is not constant, so A, it can have side effects, which means it's possible to detect whether it has run, which precludes B. I don't have a problem with allowing you to programmatically detect whether an argument was given or not, if you also have a way to programmatically decide whether to pass an argument or not, without requiring a combinatorial explosion of individual call expressions. Having the former without the latter is what makes forwarding optional parameters nigh impossible to do precisely. With both, it can (and should) be a one-liner. |
Thats a really good point. I have seen there was some proposal about Records being able to be spread into parameter lists, Unfortunately I dont recall where this discussion happened. |
Let's give a concrete design for non-constant default values. Proposal
AsyncWe can allow an asynchronous function to have That does mean that an invocation of an asynchronous function can introduce an asynchronous gap before it even reaches the function body. Since constructors cannot be ConsequencesThat will trivially allow a class Point
final int x, int y;
final Color? color;
Point(this.x, this.y, {this.color});
Point copyWith({int x = this.x, int y = this.y, Color? color = this.color}) =>
Point(x, y, color: color);
} Because of the "no assignment" rule, the compiler doesn't need to have the incoming values stored in any particular place. It can probably be loosened, but it makes it much easier to read when one initializer cannot change a previously initialized variable. However, because expressions can have side effects, this feature allows determining whether a parameter was passed, even if it has the same value as the eventual default value. bool __wasPassed = true;
// Can be read only once, then it resets.
// Should be read to reset it, before the next call to `foo`.
bool get _wasPassed {
var result = __wasPassed;
__wasPassed = true;
return result;
}
Object? _notPassed() {
_wasPassed = false;
return null;
}
void foo([Object? arg = _notPassed()]) {
if (_wasPassed) {
print("Passed: $arg");
} else {
assert(arg == null);
print("Not passed");
}
}
void main() {
foo(); // Not passed
foo(null); // Passed: null
foo(); // Not passed
foo(null); // Passed: null
} That's so cumbersome it's probably not going to be used much, which is why I don't necessarily think we need to allow computationally deciding whether to pass an argument. It'll be an anti-pattern to rely on distinguishing a passed argument from a default value, because it makes it harder for callers to choose the behavior they want. |
This is fantastic, and exactly what I need. Would this be a non-breaking change? it does seem to me like that, but perhaps it isnt. |
Control flow in argument lists would do that: forward({
String? a,
String? b,
String? c,
String? d,
String? e,
String? f}) {
original(
if (?a) a: a,
if (?b) b: b,
if (?c) c: c,
if (?d) d: d,
if (?e) e: e,
if (?f) f: f,
);
} |
"Parameter elements" was absolutely what I was hinting at when writing that sentence. |
Oh, yes, agreed that |
i'm against that particular example beyond that, I do want non-const arguments, and late can be used to avoid executing expensive defaults at runtime, as otherwise it would have a weird mismatch with normal variables. I would expect that i mean, think about it. parameter lists just look like normal variables. (and can even be possibly; we keep const defaults, and non-const late defaults |
Parameter default values are not the same as variable initializers. They are not executed unconditionally, but only if a value isn't given by an argument. A more (but not totally) precise syntax would be |
Sure. Like I said, we might be okay with that - especially since the const values can't really have unexpected behavior due to the arguably inconsistent syntax. That's why I say, leave the const defaults, but enable the use of |
Currently, default values are constant expressions, which implies that it is impossible for a function declaration to use the default value mechanism to specify a non-trivial computation which fills in an actual argument value when it is omitted at the call site.
The text was updated successfully, but these errors were encountered: