language icon indicating copy to clipboard operation
language copied to clipboard

Allow both optional positional and optional named arguments in the same function signature.

Open lrhn opened this issue 12 years ago • 46 comments

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.

lrhn avatar Nov 30 '12 13:11 lrhn

This issue is related to issue dart-lang/sdk#6496, but asks for less.

floitschG avatar Nov 30 '12 13:11 floitschG

This comment was originally written by @seaneagan


Is this asking for:

  • 2 separate optional parameters within the same declaration, one named, one positional
  • allowing a single optional parameter to be both named and positional

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 ]].

DartBot avatar Nov 30 '12 16:11 DartBot

It is asking for non-overlapping [] and {}. Examples: new List([length = 0], {fill: null}); Stream.subscribe([onData], {onError, onDone}) new Date(year, [month, ...., milliseconds], {isUtc: false})

floitschG avatar Nov 30 '12 16:11 floitschG

Added this to the Later milestone. Added Accepted label.

gbracha avatar Nov 30 '12 18:11 gbracha

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.

DartBot avatar Dec 01 '12 00:12 DartBot

This comment was originally written by @rbishop-bah


Related: Issue dart-lang/sdk#17101

DartBot avatar Feb 25 '14 12:02 DartBot

Removed this from the Later milestone. Added Oldschool-Milestone-Later label.

kasperl avatar Jul 10 '14 10:07 kasperl

Removed Oldschool-Milestone-Later label.

kasperl avatar Aug 04 '14 07:08 kasperl

Issue dart-lang/sdk#17101 has been merged into this issue.

gbracha avatar Aug 27 '14 00:08 gbracha

Marked this as blocking dart-lang/sdk#21406.

lrhn avatar Oct 28 '14 10:10 lrhn

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 avatar Sep 09 '15 22:09 donny-dont

@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.

seaneagan avatar Sep 10 '15 15:09 seaneagan

Any guesses when this might get implemented?

bobjackman avatar Sep 29 '17 23:09 bobjackman

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:

new Center(child:
   new Column(children: [
      new Text("Hello, World!"),
      new Icon(Icons.star, color: Colors.green)
   ])
)

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:

Center(
   Column([
      Text("Hello, World!"),
      Icon(Icons.star, color: Colors.green)
   ])
)

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.

wmleler avatar Jan 02 '18 22:01 wmleler

I would love to see this, together with making child en children positional in Flutter 🙏

kasperpeulen avatar Aug 11 '18 12:08 kasperpeulen

In maintaining the protobuf library we have functions that take positional optional arguments, making it hard to add further optional arguments. Refactoring the existing functions to take only named arguments is probably the 'right thing to do(tm)' but that is a breaking change and thus very costly.

For example GeneratedMessage.fromBuffer currently has the signature

void mergeFromBuffer(List<int> input,
      [ExtensionRegistry extensionRegistry = ExtensionRegistry.EMPTY]);

If we want to add more options to the parsing (for example bool ignoreUnknownFields) it would be nice to add as a named optional, but that is currently not possible.

sigurdm avatar Aug 13 '19 13:08 sigurdm

Something new? This sounds like a useful feature...

eladcandroid avatar Aug 25 '20 12:08 eladcandroid

Is such a thing allowed now?

Future<Database> connect({String dbName, [String dbUserName, String dbPassword]}) {}
connect(dbName:"sample");
connect(dbName:"sample", dbUserName:"username", dbPassword:"password");

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]) 

blunderous avatar Aug 26 '20 10:08 blunderous

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?

escamoteur avatar Sep 17 '20 13:09 escamoteur

I agree. Why we cant have both: positional and named optional parameters at the same time?

vovkd avatar Jan 28 '21 06:01 vovkd

There were a handful of comments that were all either just "+1" or "+1 for this feature", which is better expressed by adding a 👍 to the issue itself, so I deleted them.

munificent avatar Apr 07 '21 00:04 munificent

There are lots of confusing and conflicting statements in the comments on this issue. What I was just looking for--and what I feel @lrhn was asking for when opening this issue nine years ago--was the ability to have both an optional positional argument and an optional named argument (see function4, below). While looking into this, it appears to me that Dart actually doesn't allow any arguments of any type after the closing bracket of an optional positional argument (see functions 4, 5, and 7)!

  void function1 (int reqdPosn, {required int? reqdNamed}) {}     // allowed
  void function2 (int reqdPosn, {int optNamed=1}) {}              // allowed
  void function3 (int reqdPosn, [int optPosn=1]) {}               // allowed
  void function4 ([int optPosn=1], {int optNamed=1}) {}           // error
  void function5 ([int optPosn=1], {required int reqdNamed=1}) {} // error
  void function6 ({int optNamed=1}, [int optPosn=1])  {}          // error*
  void function7 ([int optPosn1=1], [int optPosn2=1]) {}          // error
  void function8 ([int optPosn1=1, int optPosn2=1]) {}            // allowed

  // * Yes, this violates the 'All positional arguments must be before any named
  //   arguments" rule but I though it worth testing for the sake of completeness!

I see no inherent confusion (to parsers or humans) in allowing both types of arguments but if there is, perhaps it would be best to state it (them?) and close this issue.

IMO, I suspect that the requested feature is wanted the most when one is adding functionality to an existing function that has an optional positional argument. My own takeaway (for the microscopic amount it is worth) is that I will rarely use optional positional arguments in the future--the little bit of typing this saves is just not worth it when compared to the effort that I now know will be required to add any named arguments in the future.

j-vasil avatar Jun 06 '21 03:06 j-vasil

My own takeaway (for the microscopic amount it is worth) is that I will rarely use optional positional arguments in the future--the little bit of typing this saves is just not worth it when compared to the effort that I now know will be required to add any named arguments in the future.

You are not the only one thinking so, and some teams have a "no optional positional parameters ever" policy. That's not necessarily the best API design, but it's driven by the available features and a wish to avoid future regret.

That's why we should still add this feature. Every year we delay is another year of people doing sub-optimal API design, for APIs which might stay with us forever.

(One alternative is to change the parameter semantics completely, so that any nullable parameter is optional - aka. can be omitted, which then passes null implicitly, and passing null triggers the default value if there is one. E.g. #836. Since both positional and named parameters can be nullable, both can be optional at the same time).

lrhn avatar Jun 07 '21 09:06 lrhn

@Irhn what about python like args/kwargs: def fun(*args, **kwargs): pass

abstract class Controller {
  String render(String template, [List<Map<String, Object?>> *contexts /*= const []*/, ]{Map<String, Object?> **data /*= const {}*/}) {
    data = contexts.reduce(...) + data;
    return environment.getTemplate(template).render(data);
  }
}

abstract class Template {
  String render(Map<String, Object?> context);
}

abstract class Environment {
  Template getTemplate(String name);
}

ykmnkmi avatar Jun 07 '21 10:06 ykmnkmi

@irhn what about python like args/kwargs: def fun(*args, **kwargs): pass

Python's approach doesn't work well in a statically-typed language like Dart where you want to be able definitely associate different static types with each optional parameter.

munificent avatar Jun 17 '21 01:06 munificent

Every year we delay is another year of people doing sub-optimal API design, for APIs which might stay with us forever.

This is really is the most important argument for prioritizing this feature, IMHO. I'm currently designing a new API, and there are simply method signatures that beg to have optional, positional arguments – but I can't use them, because I need named ones, too.

So once the API is published, I'm going to be stuck with the suboptimal signature. Which will be a small stab in my heart every time I'm going to call it. 😐

So seeing that this was first proposed nine years ago, and nobody really posted a show-stopper as for the logic or sense of the feature – I think there's a chance for you guys to make a lot of people really happy by finally making a decision. 😜

PrimaryFeather avatar Dec 02 '21 08:12 PrimaryFeather

Since dart-lang/sdk#47451 has been released, I believe this could be revisited, maybe after all objectives at dart-lang/sdk#47451 have been completed, but maybe it's mature enough. @lrhn

FMorschel avatar May 25 '22 13:05 FMorschel

I, sadly, don't think "named arguments anywhere" has any effect on the viability of this issue. Named arguments anywhere is a simple reshuffling of arguments at the call site, it does not affect the declaration of functions. Allowing both named and optional positional arguments on the same function would affect function declarations and the low-level function call protocol.

lrhn avatar May 25 '22 14:05 lrhn

Any knowledge on how someone can assist on that? I just have no idea on if this could be developed from outsiders and where to look for potential solutions/problems.

FMorschel avatar May 25 '22 16:05 FMorschel

We definitely love getting help from external contributors. :)

But this is a particularly tricky issue because it affects the language's calling convention. That requires work in all of our implementations: VM, native compiler, development web compiler, and optimized web compiler. Any of those could potentially have negative performance implications that might make the feature untenable.

So to make progress on this, we really have to talk to all of the implementation teams and see how they think they would support it and what the performance costs might be before we could make any decisions.

munificent avatar May 26 '22 00:05 munificent