Arrow functions, or lambdas as some languages call it, are frequently used in dart, even more so in the Flutter framework. Most of these arrow functions only contain one argument. At the language's current standing, if I were to write a forEach loop on a List, I would have to do something like this:
void main(List<String> args) {
List list = [1,2,3,4];
list.forEach((item) => /* Do something item */);
}
forEach is just one of the many places that an arrow function is frequently used.
As you can see, the parentheses around the item make the whole arrow function a little awkward to write and also read. I propose the following change: make the parenthensis around one argument arrow functions optional, permitting writing code as such:
void main(List<String> args) {
List list = [1,2,3,4];
list.forEach(item => /* Do something item */);
}
This makes the code much cleaner to read and to write.
I think this can be made into a fairly safe syntax change.
It likely won't affect readability, the => is syntax enough for the reader to recognize what is going on (although very long parameter names might make that harder to read, say x.forEach(wellFirstDoSomethingAndThenDoSomethingElse => print(well....)). That's probably just as unreadable with parentheses鈥攑arentheses are not a clear clue that a parameter begins here, they could also just be grouping parentheses, it's the => or { following that marks something as a function.
Without parentheses, there is no way to specify an argument type, so it will only work where the argument type is inferred (so it has to be equivalent to (item) => ..., and it cannot cover all use-cases handled by parentheses). You also cannot make the function asynchronous, because the keyword would now be undelimited. No x async =>, and async => is a one parameter function with a parameter named async, not a zero parameter asynchronous function.
Would we want to allow zero-parameter functions like foo.whenComplete(=> foo()) too?
(Or would we rather reserve those for one-argument functions with an implicit parameter name, like somethings.forEach(=> print(it.name)))?
Would we want it to work for block bodies too? x.forEach(thing { print(thing.name));
Probably not, that's not readable. The => and that it's a single expression following it, makes foo => stand out in a way that foo { .... } does not. If anything, that can be mistaken for a control flow structure.
I fear that we might end up with style guides requiring or disallowing the parentheses when they are no necessary. If you change (item, thing) => ... into (item) => ..., you would suddenly also need to remove the parentheses. That's annoying.
On the other hand, I know that some groups of programmers will definitely make one of the choices and require everybody to do the same thing, rather than allowing two different styles ("coding style" hard-liners who believe that you shouldn't need to waste time making a choice in such cases).
Would we want to allow zero-parameter functions like
foo.whenComplete(=> foo())too?
(Or would we rather reserve those for one-argument functions with an implicit parameter name, likesomethings.forEach(=> print(it.name)))?
I think we should reserve it for the latter.
This is related to https://github.com/dart-lang/language/issues/265 which is about eliminating the argument entirely when there is exactly one required argument.
We could then write somethings.forEach(=> print(name)) where the argument is named this and is accessed implicitly for invocation of the getter name. An alternative design would name the argument it (like in Kotlin) and yield somethings.forEach(=> print(it.name)).
Zero-argument functions would still have to be written explicitly, but that's already a bit shorter than a one-argument function (so the motivation for abbreviating it is less compelling).
We could then write
somethings.forEach(=> print(name))where the argument is namedthisand is accessed implicitly for invocation of the gettername.
I think that's probably the wrong default. Many closures want to access the original surrounding method's this in the body, so shadowing that with the implicit lambda parameter may be both confusing and not what they want. Code like this is common:
somethings.forEach((foo) => methodOnThis(foo, 3));
It would be nice to be able to shorten it to, say:
somethings.forEach(=> methodOnThis(it, 3));
[Edit May 28th 2019: A custom binding of this is such a deep change to the semantics of code that, at this point, I strongly prefer to have it _only_ when there is a local syntactic flag for it. So I changed #265 to use the parameter name it. Adjusted the text below accordingly.]
@munificent wrote:
Many closures want to access the original surrounding method's
thisin the body
Right, the updated proposal #265 only supports the form that you prefer:
somethings.forEach(=> methodOnThis(it, 3));
Most helpful comment
I think that's probably the wrong default. Many closures want to access the original surrounding method's
thisin the body, so shadowing that with the implicit lambda parameter may be both confusing and not what they want. Code like this is common:It would be nice to be able to shorten it to, say: