-
Notifications
You must be signed in to change notification settings - Fork 205
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
Allow referring to assigned variable within its initializer. #542
Comments
Dart does not allow access to a variable inside its own initialializer. (This is unrelated to cascades, so I've updated the title to the more general situation). It is usually hit when a closure contains a reference to the variable, and the author knows that the function won't be called immediately. Other examples include: var subscription = stream.listen((o) {
if (o == null) subscription.cancel();
...
});
var port = rawReceivePort((o) {
if (o == null) port.close();
...
}); The problem is that if we allow access to the yet-uninitialized variable, we also have to give it a semantics. Dart has steadfastly refused to give access to final variables in a way where you can see them prior to initialization. With NNBD, this becomes even more essential because they might not even have the value The options are to throw on access prior to initialization, or to give "some value". In either case (and the latter is unlikely, especially for NNBD), it requires an extra check on each access to the variable, which might be an unnecessary overhead. |
I agree that solving the general problem for an initializer most likely solves it for cascades, however I do think the problem of cascades is a bit narrower and could potentially be solved more easily. From a user perspective the most straightforward desugaring of a cascade would make the reference to the variable happen outside of its initialization. |
You are suggesting that an assignment to a cascade expression would perform the assignment to the receiver value prior to evaluating the cascade sections? That is possible, but somewhat inconsistent. In all other situations the cascade in total before the value becomes available. It would still expose the object in a state prior to the cascade, which may be an initialization. If it's only variable initialization, and not any assignment, then it's inconsistent. It would mean that All in all, I think changing the evaluation order for cascades is too inconsistent and error-prone to be really viable. |
I see what you mean about the assignment to something other than a variable initializer. I think I was implicitly treating variable initialization as a different desugaring to other usages of cascades. It feels sensible to me to treat |
The duplicated bug I linked is basically this one, although I found it even more confusing because I wasn't trying to refer to the variable explicitly - but this code fails: const Foo foo = Foo()..baz(); I would have expected that to desugar into: const Foo foo = Foo();
foo.baz(); which is allowed - and final Foo foo = const Foo()..resolve(); is also allowed. |
Just FYI: The notion of an anonymous method was proposed with, among other things, this particular kind of situation in mind. You could say that it works as a "better cascade". Here's the // Desired actions.
var foo = bar();
foo.baz(() {
foo.blub();
});
// Same thing using (proposed, not yet implemented) anonymous methods.
var foo = bar()..{ baz(() => blub()); }; The point is that the These properties make the code in |
Came across this today with final WebViewController controller = WebViewController()
.. // ...
..setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
controller.runJavascript(); // "Local variable 'controller' can't be referenced before it is declared" error
},
),
); Looks like "anonymous methods" are just Kotlin's final WebViewController controller = WebViewController().{
setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
controller.runJavascript(); // no error?
},
),
);
}; |
You can implement something like that yourself, as: extension WithSelf<T> on T {
R apply<R>(R Function(T) onSelf) => onSelf(this);
} Then you can write: final WebViewController controller = WebViewController()..apply((controller) {
setNavigationDelegate(
NavigationDelegate(
onPageFinished: (url) {
controller.runJavascript(); // no error?
},
),
);
}); You'd have to name the controller twice (if you need it outside at all). It would be nice to have a block with bindings that doesn't introduce a new function body. Doesn't have to bind the value to (Personally I'd like declaration expressions (#1420 or similar), which would allow something like |
Cascade syntax helps avoid redundant references to the same variable, but it breaks down once you need to reference that variable within a cascade call.
The following are logically equivalent:
However the same equivalence can't be made with:
The text was updated successfully, but these errors were encountered: