Skip to content
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

Arguments shorthand #1123

Open
savioserra opened this issue Jul 30, 2020 · 16 comments
Open

Arguments shorthand #1123

savioserra opened this issue Jul 30, 2020 · 16 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@savioserra
Copy link

I didn't find any issue talking about this (and to be honest I don't know if it's doable in Dart), but here we go:

String hello({String name}) {
  return "Hello $name";
}

var name = "Foo";

// Instead of hello(name: name), we could just:
hello(name); 
@savioserra savioserra added the feature Proposed language feature that solves one or more problems label Jul 30, 2020
@fsteenkamp
Copy link

So you mean like:

String hello(String name) {
  return "Hello $name";
}

?

You can do that...search for positional arguments and named parameters in the language tour 💯

@savioserra
Copy link
Author

I know about that approach, and it's not what I wanted. I'm really talking about named parameters, just like we have in Javascript.

@lrhn
Copy link
Member

lrhn commented Jul 30, 2020

This feature would use a variable in argument position, where it could/must be a named argument, to be treated as a named argument with the same name as the variable.

That's not a feature you are going to create a new variable to use. It's going to be almost exclusively used to forward named parameters, the place where you currently write bar({name}) => foo(name: name) and become annoyed by the repetition.

If we add such a feature, we might want to limit it to only forwarding parameters, so you can't do it with a local variable, or a global or instance variable for that matter. That would reduce the risk of accidentally triggering the feature.

The biggest problem here is that it doesn't interact well if we ever allow the same function to have both optional positional and optional named parameters.
Say we allow you to write foo(int id, [String? address], {String? name}), then someone writing:

fromStreetName(int id, String name) => foo(42,  name);

would you then expect the second argument to be a positional argument or a named argument (named name)?

This ambiguity means that there are some sharp edges where the feature would stop working, possibly unexpectedly.

All in all, I think I'd prefer some other approach to making parameter forwarding easier, without linking variable names and named argument names. That feels a little too fragile. Not sure what those other approaches would be, though.

@munificent
Copy link
Member

Yes, like @lrhn says, I don't like the idea of an entirely bare identifier being a shorthand for passing a named argument with that name. It's fragile and potentially ambiguous. I could imagine some lightweight but still explicit notation like, I don't know:

String hello({String name}) {
  return "Hello $name";
}

var name = "Foo";

// Instead of hello(name: name), we could just:
hello(name:); 

But I don't know if that's too weird too carry its weight.

@TimWhiting
Copy link

TimWhiting commented Aug 12, 2020

@munificent
In Julia's recent release, they have an explicit notation where a semicolon in the argument list denotes that all trailing arguments after the semicolon are named arguments with the name being the same as the name of the variable passed in.

I think it is explicit, but not too weird.

So as an example:

String hello({String name, String lastName, int age}) {
  return "Hello $name $otherName $age";
}

var name = "Foo";
var age = 10;
hello(lastName: 'other' ; name, age); 

@lrhn
Copy link
Member

lrhn commented Aug 17, 2020

That makes a kind of sense. The ; is a separator, which otherwise cannot occur inside parentheses. Here it separates parameters being passed from (likely) parameters being forwarded. It's not a bad syntax, or semantics. It might look a little weird doing foo(;bar), but I think it's something you can easily get used to.

@savioserra
Copy link
Author

Wow guys, thank you so much for taking the time needed to participate on this! As I told before I wasn't really hopeful on this, but I'm glad you all brought some cool ideas! 😄

@cedvdb
Copy link

cedvdb commented Sep 18, 2021

what about using the same token as in the declaration ? It's more streamlined this way

String hello({String name}) {
  return "Hello $name";
}

final name = "Foo";

// Instead of hello(name: name)
hello({ name }); 

Could be ambiguous in some circumstances maybe ?

The big advantage of using named params is to document the code. When the variable name is the same as the named param then it's duplicate documentation.

I'm curious what's the proportion of named vs positional parameters in the packages on pub dev

@ykmnkmi
Copy link

ykmnkmi commented Sep 18, 2021

developer:log log(message, :time, :error, :stackTrace); ?
Edit: or log(message, _:time, _:error, _:stackTrace);

@TimWhiting
Copy link

TimWhiting commented Sep 18, 2021

what about using the same token as in the declaration ? It's more streamlined this way

String hello({String name}) {
  return "Hello $name";
}

final name = "Foo";

// Instead of hello(name: name)
hello({ name }); 

Could be ambiguous in some circumstances maybe ?

This is the main problem with this approach. At parse time this looks like a set literal. Using the context type it could work, but it just makes the grammar more ambiguous at parse time which is not desired. I think I still prefer the ; in the argument list since it is explicit and unambiguous.

@cedvdb
Copy link

cedvdb commented Sep 18, 2021

looks like a set literal

Oh yeah. Still, in the idea of keeping things streamlined I don't think using a new symbol entirely is a good idea. This at least looks a bit like current named params:

log(message, :time, :error, :stackTrace);

@lrhn
Copy link
Member

lrhn commented Sep 20, 2021

If we compare it with "if variables" (#1201), both needs some way to extract a name from an expression.
If the expression is a plain identifier, then it's trivial. If not, then maybe we can allow an expression ending in a getter, anyExpression.name, and make the getter's name the implicit name of the expression.

Then log(message, :time, :error.error, :error.stackTrace) would also become possible.

@TimWhiting
Copy link

Similar issue, #58. It seems like tuples and tuple destructuring into arguments of function would solve this use case and many others.

@cedvdb
Copy link

cedvdb commented Sep 27, 2021

If we compare it with "if variables" (#1201), both needs some way to extract a name from an expression.
If the expression is a plain identifier, then it's trivial. If not, then maybe we can allow an expression ending in a getter, anyExpression.name, and make the getter's name the implicit name of the expression.

Then log(message, :time, :error.error, :error.stackTrace) would also become possible.

That would be great. I don't want to go of topic but looking ahead, destructuring can be convenient too if you have a "parent" object, although you have to check the source to know which property is used so it might be a bit less readable:

log(message, :time, ...error)

@satvikpendem
Copy link

Since Dart allows a similar shorthand for the upcoming pattern matching feature (Area(:var n) for Area(n: var n)), perhaps the : prefix as an argument shorthand would work nicely as well, ie log(message, :time, :error, :stackTrace); or even log(message, :time, :error.error, :error.stackTrace).

@lrhn
Copy link
Member

lrhn commented Jan 30, 2023

We'd have to define what the "implict name of an expression" is (the patterns feature only derives the implicit name of a pattern. Patters are part-declarations and part-expressions, and we only take the name from the declaration part.

Maybe something like the following could define "expressions ending in an identifier"

  <namedExpression> ::= 
        <identifierExpression>
      | <identifierExpression> '!'
      | <identifierExpression> <postfixOperator>
      | <assignableExpression> <assignmentOperator> <namedExpression>
  
  <identifierExpression> ::=
      <identifier>
    | 'super' '.' <identifier>
    | <primary> <selector>* ('.' | '?.') <identifier>

and then foo(: e) would be a compile-time error if e could not be parsed as a <namedExpression>, and if it can, the final <identifier> of the <identifierExpression> is its implicit name.

I'm sure there are lots of gnarly corners to consider.

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

8 participants