Issue
This guideline point suggests avoiding assigning lambdas to variables when an identifier is required. Is there any reason for this? Is it because of readability? Passing a lambda tied to a variable as an argument to a function which requires a Function
parameter seems more natural to me than passing a declared function (also, with a declared function autocomplete automatically adds the arguments, which are to be removed).
Solution
I don't know the exact rationale for the guideline, but some reasons would be:
Constant-ness. Top-level functions and
static
methods are compile-time constants. In contrast, Dart currently does not allow function expressions (lambdas) to ever be constants. Consequences include:Optimizability. When the compiler sees a call site, it knows exactly what will be called. This allows it to make optimizations (such as potentially inlining the function call).
Default arguments. Because Dart does not allow function expressions to be constants, they cannot be directly used as default arguments. For example:
int square(int x) => x * x; final cube = (int x) => x * x * x; void f([int Function(int) callback = square]) { // OK. print(callback(42)); } void g([int Function(int) callback = cube]) { // ERROR: Default value // must be constant. print(callback(42)); }
Entry-points. Entry points such as
main
and such as the callbacks used to spawnIsolate
s also must be constants, and they therefore can only be declared top-level functions orstatic
methods and never lambdas.
Recursion. Declared functions immediately bind a name, but initializing a variable from a function expression must evaluate the expression first. The ordering matters if the function expression is self-referential (i.e., recursive). For example, this is not legal:
final f = (int x) { if (x > 0) { print(x); f(x - 1); // Error: Variable can't be referenced before it is declared. } };
and instead would need to declare the variable separately:
late final void Function(int) f; f = (int x) { if (x > 0) { print(x); f(x - 1); } };
which is awkward at best for local functions and impractical everywhere else.
Return types. There's no existing syntax to directly specify the return type for a lambda, which instead relies on type inference. Consequently, there have been numerous times people have done things like:
() { if (someCondition) { return someValue; } }
where they forget to return a value along all code paths, and they then become confused why their function returns a nullable type. In contrast, had it been declared as a normal function with an explicit return type, the missing return value would have been caught. You could specify a return type by providing an explicit type for the variable, but that brings me to my final point:
Readability. Using lambdas at best is slightly more verbose for little to no benefit:
int square(int x) => x * x; var square = (int x) => x * x;
and it's even worse if you want to declare a return type and to prevent reassignment:
final int Function(int) square = (x) => x * x;
Furthermore, most functions are named, most existing functions follow the guideline, and, as described above, there are circumstances where lambdas cannot be used. Therefore there is a lot of existing inertia. Readers are already accustomed to seeing function declarations most of the time, and familiar code is inherently more readable.
Note that the points about constant-ness and return types don't explain why the language was designed that way. Arguably something like const square = (int x) => x * x;
should be legal, and some new syntax could be invented to specify a return type. (I imagine that one reason against const
lambdas would be that it would make canonicalization harder. Given:
const foo = (int x) => x * x;
const bar = (int y) => y * y;
should foo
be identical
to bar
?) Implementing those things isn't free, and ultimately there's little motivation to do so.
Answered By - jamesdlin Answer Checked By - Mary Flores (PHPFixing Volunteer)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.