Coder Social home page Coder Social logo

Comments (46)

wmleler avatar wmleler commented on May 28, 2024 19

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.

from language.

lrhn avatar lrhn commented on May 28, 2024 15

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

from language.

munificent avatar munificent commented on May 28, 2024 14

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.

from language.

escamoteur avatar escamoteur commented on May 28, 2024 12

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?

from language.

munificent avatar munificent commented on May 28, 2024 9

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.

from language.

nate-thegrate avatar nate-thegrate commented on May 28, 2024 9

I think it's worth noting that there's some nice synergy between this issue and the new Dart 2.17 features.

Since we now have "named arguments anywhere", we could eventually change child and children into positional arguments:

Container(
  color: Colors.green,
  Column(
    mainAxisSize: MainAxisSize.min,
    [
      Text('Elegant as heck'),
    ],
  ),
)

Combining optional positional & named arguments can also be helpful when using super initializers:

class CustomBox extends StatelessWidget {
  final int? height;
  const CustomBox([this.height, Key? key]) : super(key: key); // the old way to do it

  const CustomBox([this.height, super.key]);   // throws an error
  const CustomBox([this.height], {super.key}); // it would be great if this worked!
  ...
}

It's unfortunate to hear that this issue affects Dart's calling convention. Guess I'll just keep my fingers crossed that it'll work out!

from language.

sigurdm avatar sigurdm commented on May 28, 2024 8

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.

from language.

PrimaryFeather avatar PrimaryFeather commented on May 28, 2024 7

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

from language.

kasperpeulen avatar kasperpeulen commented on May 28, 2024 6

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

from language.

n7trd avatar n7trd commented on May 28, 2024 6

I would prefer removing optional positional parameters as it greatly simplifies things. #2232

from language.

vovkd avatar vovkd commented on May 28, 2024 5

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

from language.

j-vasil avatar j-vasil commented on May 28, 2024 4

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.

from language.

munificent avatar munificent commented on May 28, 2024 4

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.

from language.

fercsi avatar fercsi commented on May 28, 2024 4

@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 open you usually read a text file, so using open("myfile") is sufficient. However if you happen to write one, you use the positional parameter mode: open("myfile", "w"). But there are a bunch of further options, for which you must use names. Of course, this could be written, too: open("myfile", mode="w"). Note, that Python have 3 types of parameters all of which can be optional: positional only, named/keyword only, and those which can be used in position or with name (this latter is the default).

This can be a really strong feature if you get used to it.

from language.

munificent avatar munificent commented on May 28, 2024 3

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

from language.

blunderous avatar blunderous commented on May 28, 2024 2

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

from language.

rrousselGit avatar rrousselGit commented on May 28, 2024 2

@lrhn There seems to be an infinite recursion in that issue link list :P

What's the issue with tracking this?

from language.

lrhn avatar lrhn commented on May 28, 2024 2

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.

from language.

floitschG avatar floitschG commented on May 28, 2024 1

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

from language.

FMorschel avatar FMorschel commented on May 28, 2024 1

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

from language.

lrhn avatar lrhn commented on May 28, 2024 1

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.

from language.

eernstg avatar eernstg commented on May 28, 2024 1

@lrhn, did you intend to close some other issue which is subsumed by this one?

from language.

mateusfccp avatar mateusfccp commented on May 28, 2024 1

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

from language.

escamoteur avatar escamoteur commented on May 28, 2024 1

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

from language.

eernstg avatar eernstg commented on May 28, 2024 1

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 dynamic or Function, which is a very nice consistency property.

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 ??= construct in order to use the parameter that actually received a useful value and ignore the other parameter. There is no support (in the language) for getting a diagnostic message if both parameters receive a non-trivial value by mistake.

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.

from language.

floitschG avatar floitschG commented on May 28, 2024

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

from language.

DartBot avatar DartBot commented on May 28, 2024

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

from language.

gbracha avatar gbracha commented on May 28, 2024

Added this to the Later milestone.
Added Accepted label.

from language.

DartBot avatar DartBot commented on May 28, 2024

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.

from language.

DartBot avatar DartBot commented on May 28, 2024

This comment was originally written by @rbishop-bah


Related: Issue dart-lang/sdk#17101

from language.

kasperl avatar kasperl commented on May 28, 2024

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

from language.

kasperl avatar kasperl commented on May 28, 2024

Removed Oldschool-Milestone-Later label.

from language.

gbracha avatar gbracha commented on May 28, 2024

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

from language.

lrhn avatar lrhn commented on May 28, 2024

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

from language.

donny-dont avatar donny-dont commented on May 28, 2024

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?

from language.

seaneagan avatar seaneagan commented on May 28, 2024

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

from language.

bobjackman avatar bobjackman commented on May 28, 2024

Any guesses when this might get implemented?

from language.

eladcandroid avatar eladcandroid commented on May 28, 2024

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

from language.

ykmnkmi avatar ykmnkmi commented on May 28, 2024

@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);
}

from language.

FMorschel avatar FMorschel commented on May 28, 2024

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.

from language.

lrhn avatar lrhn commented on May 28, 2024

Whoops, yes. I thought I was looking at an SDK-repo issue. I followed a link with dart-lang/sdk/ in it to get there.
I just hadn't noticed the issue had been moved here, and the old link was forwarding, so it's all the same issue.

from language.

JaffaKetchup avatar JaffaKetchup commented on May 28, 2024

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!

from language.

JaffaKetchup avatar JaffaKetchup commented on May 28, 2024

Sad to hear that, but thanks for letting us know. Just looking forward to Dart 3 now :)

from language.

eernstg avatar eernstg commented on May 28, 2024

@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 dynamic or Function can work, or even statically checked invocations on a supertype). This would potentially require a substantial amount of work in several different backends.

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 foo(1, 2, 3, z: 4) becomes foo(1, x: 2, y: 3, z: 4)). The declaration specifies which named parameters are subject to this transformation, and in which order.

The main limitation is that (foo as dynamic)(1, 2, 3, z: 4) will fail because it will be called exactly as written (the compile-time transformation won't change any dynamic calls), and foo doesn't actually accept 3 positional parameters (also, x or y could be required).

How would that work for you?

from language.

escamoteur avatar escamoteur commented on May 28, 2024

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.

from language.

JaffaKetchup avatar JaffaKetchup commented on May 28, 2024

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.

from language.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.