-
Notifications
You must be signed in to change notification settings - Fork 205
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
Inferring required named parameters without making function types a pitfall #3287
Comments
Pair this with private named initializing formal parameters (#3058, #2509, ...), and we could save a lot of real estate! I do think that the current |
The behavior of abstract methods (and So given this code: abstract class C {
void m({int i});
}
class D implements C {
@override
void m({int i = 0}) {}
}
class E implements C {
@override
// ERROR: Invalid override.
void m({required int i}) {}
} Today, the requiredness is not explicitly specified on
I think we would not change the behavior (not infer |
External and abstract method declarations are more, but not entirely, like function types than function declarations. So is redirecting factory constructors, which cannot have default values at all. But they are written like declarations (positional parameters need names). Maybe we need a third category, an "abstract function declaration", so that we can test the exceptions uniformly and separately when necessary. |
Ah, abstract is a good catch. Yes, we could require those to be explicit too and force you to write either |
I like the direction of this, but I have to admit I don't love that this is driven by the type, and even more so when the type is not immediately visible because of const _ColorPickerSwatch({
required this.color,
required this.selected,
this.onTap,
}); is at least very clear about what is and is not required, whereas with const _ColorPickerSwatch({
this.color,
this.selected,
this.onTap,
}); it is not at all clear to me which things are required and which things are not. I think there was another proposal from someone (@munificent or @lrhn ?) to say that all parameters are required unless they have a default value (and I think to use a marker like |
It's something I've noodled on but never put a full proposal together. |
This is very similar to #878. Although the approach is not the same, the results are similar, IMO. |
Yes, thanks for bringing that up! The proposal here is basically an extension of that one that answers the question of how to make function types and abstract function declarations less confusing. |
In a function declaration like:
The declaration of
x
is fine. It's non-nullable but optional, since it has a default value. The declaration isy
is an error since it's optional (norequired
), has a non-nullable type, and no default value.If we wanted to make
y
not an error, the obvious interpretation is to inferrequired
. We don't do that because that would lead to an asymmetry in function types:This typedef is fine and both
x
andy
are optional even though they have non-nullable types and no default value.We could say that function declarations and function types just work differently: We infer required in the former and optional in the latter. But that's a pitfall for users. If they take a function declaration and copy/paste its signature to a function type or typedef the resulting type might be different.
Function declarations are much more common than function types. So this rule punishes the common case in order to avoid problems with the rare case. I have an idea for how we could make function declarations smarter and shorter while avoiding this pitfall.
Proposal
The proposal is pretty simple:
Infer
required
for named parameters in function declarations that have potentially non-nullable types and no default value. So in this declaration:There is no error and
y
is a required named parameter.Require function types to be explicit for potentially non-nullable named parameters. Instead of inferring optional (current) or required (what I propose doing for function declarations), it just forces the user to choose. This is an error:
It doesn't know whether
x
and/ory
should be required. To resolve this, you must explicitly mark the parameter as being required or optional. If you change it to:Now there is no error. The first parameter is optional and the second is required.
Note that this only applies to potentially non-nullable parameter types. Nullable parameter types are inferred optional if there is no
required
as they are today and as they are in function declarations.This will make function types that have optional named parameters of potentially non-nullable types more verbose. In return, it will make function declarations that have required named parameters of non-nullable types more succinct.
For example, this class in Flutter gallery:
Becomes:
If we also do primary constructors, it goes from:
To:
I'm generally not in favor of lashing two levers together a user might want to operate independently. That's why, for example, we have both
final
andsealed
on classes because you might want to prevent subclassing independently of opting in to exhaustiveness checks.But in this case, you can still operate the levers independently. You can have required nullable parameters and optional non-nullable ones. It's just that if the user doesn't pull the optional/required lever at all, we infer the completely obvious answer in places where we have the context to do so.
In places where there isn't that context, we make the user state it explicitly. This is arguably even better than the current behavior because when someone is reading a function type today, they might not realize that the language defaults to optional for all named parameters.
The text was updated successfully, but these errors were encountered: