Skip to content

Implicitly required formal named parameters #4349

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

Open
acarstoiu opened this issue May 2, 2025 · 5 comments
Open

Implicitly required formal named parameters #4349

acarstoiu opened this issue May 2, 2025 · 5 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@acarstoiu
Copy link

If I compile something like

void p({int a}) {
...
}

the compiler complains that The parameter 'a' can't have a value of 'null' because of its type 'int', but the implicit default value is 'null'., while it should simply understand that the named parameter a is required.

Proposal

A formal named parameter that has a non-nullable type and no default value, should be understood as being mandatory. I am surprised that no one has already asked for that (at least I did not find a relevant issue).

The required keyword can make a difference only in the case of a formal named parameter without a default value, but which has a nullable type. Specifically, it would force the caller to actually send null as a value instead of not specifying a value at all for the parameter. But that's hardly programmer-friendly so I deem the required keyword should be removed from the language.

@acarstoiu acarstoiu added the feature Proposed language feature that solves one or more problems label May 2, 2025
@lrhn
Copy link
Member

lrhn commented May 2, 2025

That would create a difference between function declarations and function types.

typedef IntEater = void Function({int value}); // Function type, parameter is *not* required.
void intEater({int value}) {} // Parameter is (with this proposal) required.
IntEater eater = intEater; // Compile-time error.
void maybeIntEater({int value = 0}) {} // Parameter is optional
IntEater eatOrZero = maybeIntEater; // OK.

It would also affect forwarding constructors.

class C {
  C.req({int a}); // required
  C.opt({int a = 42}); // optional
  factory C.fwdReq({int a}) = C.req;
  factory C.fwdOpt({int a}) = C.opt; // Forwarding constructor must not write default value.
}

It would require a way to specify that one of C.fwdReq and C.fwdOpt has a required parameter and the other an optional parameter. The required keyword does that today.

When you say "non-nullable type", that means that generic functions can have parameters that are optional depending on the type argument.

void consume<T>({T value}) {}
consume<int>(); // Error
consume<int?>(); // OK?

Or does it use the declared type, not the actual parameter type, which in this case is the type parameter T which is not nullable (nor is it non-nullable, it's potentially either).

I'm not unsympathetic to doing something here, but it's not as easy as this.
(Related issues include at least #1639 and #2232, where the latter tries to formalize precisely this "nullable means optional"/"null means no argument" approach.)

@acarstoiu
Copy link
Author

acarstoiu commented May 2, 2025

typedef IntEater = void Function({int value}); // Function type, parameter is *not* required.

I'm missing something here, why doesn't the IntEater type also contain the compulsoriness of value?

class C {
C.req({int a}); // required
C.opt({int a = 42}); // optional
factory C.fwdReq({int a}) = C.req;
factory C.fwdOpt({int a}) = C.opt; // Forwarding constructor must not write default value.
}

As they are written, both forwarding constructors have a as required parameter. To preserve the behaviour of C.opt, one should define factory C.fwdOpt({int? a}) = C.opt.
That's because if a formal parameter's type is non-nullable, then its default value should be used when the function is called with null as the parameter's value.

When you say "non-nullable type", that means that generic functions can have parameters that are optional depending on the type argument.

Yes, precisely.

@acarstoiu
Copy link
Author

Read a bit some messages on those threads. You're an advocate of the equivalency between "null actual value" and "no actual value" getting passed to a function.

But, since in Dart there's the notion of nullable type, I strongly believe "null actual value" and "no actual value" are to be treated alike only when the declared parameter type is non-nullable. That's just to allow the programmers do their own design, whether you consider it poor or not.

If the type is nullable, then the function body should expect to receive a null for the parameter's value, regardless of the presence of a default value in the declaration. In this case, the syntactic sugar proposed in this comment should be enough to promote the parameter's type to non-nullable inside the function's body.

@mateusfccp
Copy link
Contributor

mateusfccp commented May 2, 2025

I am surprised that no one has already asked for that (at least I did not find a relevant issue).

There are at least two issues similar to this one. One of them was opened during the null-safety development, and was closed.

The other is still open and IIRC is slightly different but on the same line.

GitHub's search is just terrible.

Edit:

Found one of them, #878.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

3 participants