-
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
Allow both optional positional and optional named arguments in the same function signature. #1076
Comments
This issue is related to issue dart-lang/sdk#6496, but asks for less. |
This comment was originally written by @seaneagan Is this asking for:
The former could be solved by non-overlapping [] and {}. If overlapping [] and {} are allowed, then would have to decide whether to allow both {[ and [{ as well as }] and ]]. |
It is asking for non-overlapping [] and {}. |
This comment was originally written by @seaneagan Sounds perfect! The only other thing I would change (also mentioned in issue dart-lang/sdk#6496) is to use = instead of : for named positional default values: new List([length = 0], {fill = null}); The = to default positional optionals doesn't mimic call site syntax, so why does the named positional default syntax try to? I think it's more importatnt to be consistent between how to default optional parameters regardless of whether they are named or positional. |
This comment was originally written by @rbishop-bah Related: Issue dart-lang/sdk#17101 |
Removed this from the Later milestone. |
Removed Oldschool-Milestone-Later label. |
Issue dart-lang/sdk#17101 has been merged into this issue. |
Marked this as blocking dart-lang/sdk#21406. |
Since 2.0 is thinking of some drastic changes is there any way the more extreme do like C#, Python, of dart-lang/sdk#6496 can be revisited. @gbracha would a DEP be required for this? |
@donny-dont if this issue is fixed then we will be able to do: greet([String salutation = 'Hello'], {String who}) {
print('$salutation $who!');
} Then if there is really a need to allow parameters to be both named and positional, we could further allow something like this syntax: greet({[String salutation = 'Hello', String who]}) {
print('$salutation $who!');
} But personally, I think that creates unnecessarily bloated API surface. |
Any guesses when this might get implemented? |
I would like to have the ability to have a parameter be either positional or named, for a common usage case in Flutter -- the children/child of a widget (note that this case also applies to any place where you are building tree-structured values). Consider the following Flutter widget tree:
Once "new" is optional, this starts looking like a reasonable replacement for HTML, as soon as we can treat child or children arguments as positional, like this:
We kinda already have this in that Text doesn't specify the 'Hello, World!' string as a child, even though it kinda is. Same thing for Icon. It would be nice (albeit not required) if the positional argument did not have to be the first argument so you could specify the named arguments first before following them with the children specified as a positional argument. |
I would love to see this, together with making child en children positional in Flutter 🙏 |
In maintaining the protobuf library we have functions that take positional optional arguments, making it hard to add further optional arguments. For example void mergeFromBuffer(List<int> input,
[ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]); If we want to add more options to the parsing (for example |
Something new? This sounds like a useful feature... |
That wasn't the problem. The problem was using them as separate, and not nested, like this: Future<Database> connect({String dbName}, [String dbUserName, String dbPassword]) |
I too ran today into this while working on a new API and I actually don't understand this restriction. If it's possible to mix named and positional parameter , why can't that positional not being optional? |
I agree. Why we cant have both: positional and named optional parameters at the same time? |
@lrhn There seems to be an infinite recursion in that issue link list :P What's the issue with tracking this? |
@lrhn, did you intend to close some other issue which is subsumed by this one? |
Whoops, yes. I thought I was looking at an SDK-repo issue. I followed a link with |
Is there any chance of this coming in Dart 3? It would seem like an ideal time for it, considering that this has been opened for 11 years! |
I'm pretty sure it won't... |
Sorry, no, this won't make it into Dart 3. I would like to fix it at some point, but it's one of those issues that's annoying but not troublesome enough to reach the top of the priority list. |
Sad to hear that, but thanks for letting us know. Just looking forward to Dart 3 now :) |
I'm currently overhauling the APIs of get_it and get_it_mixin. It really would make APIs much better if we at least had the first parameter an optional positional that can have a default value and get mixed with named ones that follow |
@escamoteur, it would be interesting to see the concrete example, can you show it using a small snippet of code? One of the sources of complexity in this feature is the need to support a different calling convention (in particular, such that invocations of first class functions using the type If we're satisfied with a static mechanism then we do have a lightweight proposal here: #831, 'optionally named parameters'. The idea is that some parameters can be named, but they are marked as 'optionally named', and they can then be passed as positional parameters. At each call site, a compile-time transformation will check the actual argument list, cut off any 'extra' positional parameters (so if the function declares 1 positional parameter, but we're passing 3, we have 2 extras), and re-adding those extra parameters with a name (so The main limitation is that How would that work for you? |
Just a very simple example T get<T extends Object>({
dynamic param,
String? instanceName,
Type? type,
}); in 99% of the case, people don't pass any of these parameters with the exception, of the registered Type in GetIt is a factory. Then being able to pass a parameter is quite common and it would be nice to be about to write GetIt.I<Myfactory>(valueTopassToTheFactory);
instead of
GetIt.I<Myfactory>(param: valueTopassToTheFactory); not sure if that would be possible with the linked proposal. |
Thanks for the example, @escamoteur! With a rough idea about the possible rules of a proposal according to this issue ('allow both positional and optional named parameters'), the example declaration might be expressed as follows: T get<T extends Object>(
[dynamic paramPositional], {
dynamic param,
String? instanceName,
Type? type,
}) {
param ??= paramPositional;
... // Remaining implementation unchanged.
}
void main() {
var v = valueTopassToTheFactory;
get<MyFactory>(v); // OK.
get<MyFactory>(param: v); // OK.
get<MyFactory>(v, param: v); // Bug: Pass both parameters, no error.
} Pro: This is a mechanism that will work for statically checked invocations as well as invocations where the function has static type Con: There is no direct support for making a choice ("should we pass this argument by name or by position?"), which means that we have to declare two distinct parameters and use something like a default value of null and the With the 'optionally named parameter' proposal, we'd do as follows: T get<T extends Object>({
dynamic param?,
String? instanceName,
Type? type,
}) {
// ... // Remaining implementation unchanged.
}
void main() {
var v = valueTopassToTheFactory;
get<MyFactory>(v); // OK.
get<MyFactory>(param: v); // OK.
get<MyFactory>(v, param: v); // Compile-time error.
(get as dynamic)<MyFactory>(param: v); // OK.
(get as dynamic)<MyFactory>(v); // Throws at run time.
} Pro, at least for this example: We can express the property that it is the same parameter which is passed by name or positionally, and there is no danger that we're going to pass both of them by accident. Con: This mechanism is compile-time-only, which means that the ability to pass a named parameter positionally does not work for dynamic invocations. However, it does work to pass it as a named parameter. |
If I'm honest, I'm not sure I like #831 ('optionally named parameter') to resolve this ('allow both positional and optional named parameters'). It in itself seems like an overcomplication and a fairly niche use-case feature - are there other languages that have this feature? As a resolution to this, it kind of feels like a low quality workaround. Just as another example for this issue, adding named arguments to a method with existing unnamed positional arguments, without causing a breaking change. |
C# has the "optionally named parameters" feature, in that they allow you to specify arguments either by position or by name. They're named arguments, not named parameters. C# does not have named (only) parameters. |
I would prefer removing optional positional parameters as it greatly simplifies things. #2232 |
@JaffaKetchup, This feature is fairly often used in Python. Probably, the one who raised this request, use it too. The main reason for having both at the same time is, that the options you often use (but not always), you don't want to name. However, the rarely used options should be named for maintainability, but they often do not even have a position. E.g. Using This can be a really strong feature if you get used to it. |
My use case is simple:
The name makes those last two omega-optionals. However, since the language forbids it, now 4 are forced to have name, because, like if we pass |
Id consider that a bad API actually. Multiple positional booleans aren't clear what they're for or do. Imo; always name Boolean args. (Unless you're setting a Boolean value, maybe.) |
Whether a design is good or bad depends on its specific implementation. For example, I discussed Python's open function in the first post of this thread, which I consider a good design. |
Python's function definition is very clear in terms of required positional only, required keyword/named only, positional as well as named parameters just by using two operators "/" and "*". |
There is nothing fundamental holding Dart back. void Foo(int x, [int? y], {int? z}) => …
void Function(int, [int?], {int? z}) is the obvious choice. No need for changes to call syntax. The actual thing holding back this change is that it requires a change to fundamental parts of the implementation, the calling connection, where, fx, a function implemention cannot assume that named arguments always follow the same number of positional arguments. That makes it a potentially expensive change, do other changes end up being chosen first. |
Dart functions currently can have only either optional named parameters or optional positional parameters, but not both.
While developing libraries repeatedly run into cases where this restriction gets in the way of the signature we want.
It would be preferable if you could have both kinds of parameters on the same function.
The only syntax change necessary is to the declaration of functions. The call syntax is unaffected, and so is noSuchMethod/Function.apply.
The text was updated successfully, but these errors were encountered: