Consider the following code
model
..sendByEmail.value = false
..onChange = () => setState(() {});
model
..onChange = () => setState(() {})
..sendByEmail.value = false;
The first case compiles but the second does not.
onChange is a typedef VoidCallback = void Function();
sendByEmail is an object.
Is this the intended behavior?
[Edit: changed the example to reproduce the situation more completely.]
This wouldn't be a language issue but an SDK issue. However, here's a self-contained example which shows the same behavior:
typedef VoidCallback = void Function();
void setState (VoidCallback fn) {}
class C {
VoidCallback x1;
bool x2;
}
main() {
Object o1 = C()..x1 = () => setState(() {})..x2 = false;
Object o2 = C()..x2 = false..x1 = () => setState(() {});
}
Turns out it is a parsing error. ;-)
Put parentheses around the function literal, and it works:
... // Same.
main() {
Object o1 = C()..x1 = (() => setState(() {}))..x2 = false;
Object o2 = C()..x2 = false..x1 = (() => setState(() {}));
}
@eernstg
Your code compiles for me as well. Let me try to create a better example.
However is it possible that the dot in sendByEmail.value is the problem?
So it wasn't a type issue after all. The problem was that the function literal introduces an ambiguity: Do we stop parsing the body of the function literal after setState(() {}), or do we make the body setState(() {}))..x2 = false? Adding the parentheses forces the right choice.
The actual problem here is that
void setState() {}
class C {
void Function() x1;
bool x2;
}
main() {
C()..x1 = () => setState()
..x2 = false;
}
the main function is parsed as:
main() {
C()..x1 = () => (setState()..x2 = false);
}
If you add parentheses, like:
void setState() {}
class C {
void Function() x1;
bool x2;
}
main() {
C()..x1 = (() => setState())
..x2 = false;
}
then the problem goes away.
This looks like a parsing ambiguity, and we may need to have a "function expression no cascade" production to remove the ambiguity.
Thanks for the work around
So do you think this is a bug or intended?
It is a bug. It seems to be an unintended ambiguity in the language grammar, and the compilers have recovered from that in a way different from what you intended. I'd argue they are doing the wrong thing, but we'll have to change the specification to officially make them wrong.
The language grammar is definitely ambiguous at this point: The body of an => function is an expression, and the => function as a whole is also an expression, so unless we make a choice to disambiguate in a specific way we can divide () => e in any way we please to make it (() => e1)e2.
However, even though that makes sense syntactically it doesn't work very well semantically: There are not that many operations on a function object. So if we parse () => a + b as (() => a) + b it will fail during static analysis because the function object does not have an operator +.
So the grammar which is used with the specification parser (Dart.g) has resolved the problem by giving the => functions a very low precedence (deriving them directly from <expression> and from <expressionWithoutCascade>). This solves the problem here (in the sense that it forces the function literal to not include ..x2). We might very well use that approach to fix the issue, but we always need to be careful about breaking changes when we make such a solution official.
This solves the problem here (in the sense that it forces the function literal to not include
..x2).
Are you suggesting that the body of a lambda should not include a trailing cascade all the time, or only when the lambda itself is in setter position in a cascade?
The Dart.g rules have a <functionExpression> derived from <expression> and a functionExpressionWithoutCascade> derived from <expressionWithoutCascade>. With body => e, the former requires that e is an <expression> and the latter requires an <expressionWithoutCascade>. So it's only a lambda that occurs in a cascade that cannot contain a cascade in its body. For instance, o..x = () => e..y = 42 will assign to o.x and o.y, but we can also do foo(() => e..y = 42) where ..y accesses a member of the value of e, because an actual argument is a plain <expression>.