-
Notifications
You must be signed in to change notification settings - Fork 213
IIFE insufficient flow analysis #2848
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
Comments
The intermediate language "kernel" uses a construct that they call a That construct could serve as a replacement for the If we had a construct like that then there wouldn't be a need to keep track of function objects at all: It's just a block plus an expression. There is no syntax, but let's just assume that we could use this: void main() {
print(
(*) { statement; statement; return expression; statement; ... return expression; ... }
);
} This construct is by definition not leaking anywhere, because there is no function, it's just code plus the ability to evaluate to a result. It would be type checked just like Hence, assignments to local variables in the "body" would be known to occur exactly where the |
Are you proposing that a new construct (e.g. |
I was suggesting that we use new syntax (say, It is possible that it could simply be compiled into However, it might be better than that: The kernel Finally, I think it makes sense to have a visible (syntactic) hint on this construct itself that serves as a reminder that it has special properties: It is treated differently than an invoked function literal during flow analysis, and if it turns out to be nonsensical/useless/impractical to have I think that's more comprehensible/readable than an implicit mechanism which would simply find certain expressions (say, |
Perhaps I'm missing something here, but since the function expression in
That sounds wonderful! What I like about
I believe that custom analyses (see: #2852) would make |
I don't think the function object could leak. For instance, But there are lots of properties of a program which could be shown to hold, perhaps by design or perhaps by accident, and there's no way the flow analysis could take all of them into account. That's the reason why I tend to prefer that semantically significant properties are indicated in source code rather than being applied implicitly for any given entity that satisfies some kind of checklist. |
This issue could really benefit from some motivation. Why are you using IIFEs in the first place? What problem are they solving? |
The only problem I'm aware of IIFEs solving is as a "statement-expression" which allows statements inside an expression, and which allows local return to emit the value of the expression from multiple statement branches. Say: foo(a, b, last: () {
while (cursor.next != null) cursor = cursor.next; return cursor;
}()); I'd rather introduce some notion of statement-expression/do-block with a value than try to formally write IIFE's into the language semantics. (They also interact badly with So, what Erik said! |
I can think of roughly 3 situations where IIFEs are useful to me.
class Foo {
final int i;
Foo() : i = (() {
// ...
return 0;
}())
}
final bar = [
for (final a in [1, 2, 3])
...() {
// ...
return [];
}()
];
void foo() {
// ...
// 'b' and 'c' are only used to create 'a', so it
// doesn't make much sense to make them available to others.
final a = () {
int b = 0;
String c = "foo";
// ...
return ...;
}();
// ...
}
Of course, many of these benefits can be had by declaring explicit functions. However, functions need a name (and naming is hard), pollute the namespace in which they have been declared in, and require context switching (e.g. one needs to find the function, go to its location, go back, ...). |
For both variable scope and block collapsing, a block statement should enough. I can see that Dart-Code doesn't provide collapse-information for block statements, but that should be fixed, not worked around by introducing a new function nesting. Using IIFEs is really a bad habit from back when JavaScript didn't have block-scoped variables. |
I find it very useful to use the 'expand-selection' IDE feature inside of the block of an IIFE to quickly select and then cut and paste the whole IIFE to a place where an expression is expected. I agree that block statements would help here, but the UX wouldn't be as fluid with them because we couldn't return a value to turn them into expressions. Refactorings would help here, but I think it is hard to beat the convenience of an IIFE.
@lrhn in what way?
It sounds to me like you are suggesting that IIFEs should be avoided? If that's the case, are there other reasons, besides the async issue, for why you think that? |
IIFE's work badly with async, because if you have an async body, you need to add extra Not a big issue, but still more verbose and less efficient. Also, while an IIFE allows a That's why a proper "statement expression" would be a better language feature. If combine with an "expression break with value", it should be possible to have everything. foo(a, b, {:
while (something) other;
=> 42;
:}); Inside the And you can give it a label too: |
I agree with everything you said. One thing that worries me is that, in my opinion, Dart is becoming quite big syntax-wise. I don't know if adding a novel construct for block expressions/statement expressions would be worth it when IIFEs can do most of what a new block expression/statement expression could give us.
Note that this could be seen as a feature. With an IIFE I immediately know that all its exit points jump to the end of the IIFE. If it could, for example, break into its outer scope, then I would have to assume that it could jump somewhere else and not just to the end. I like this behavior because it allows me to "compartmentalize" control-flow into self-contained units that have a single entry and exit point. |
Consider the following example:
The
iife
function causes the program to fail compilation.I think it would improve the usability of
() { ... }()
constructs as IIFEs if such immediately invoked function expressions could participate in current, and future intraprocedural data flow analyses as a list of statements and not as a function expression whose statements are not considered (which seems to be the case today).Edit: by "usability" I'm not just referring to the use of IIFEs by humans, but also by tools that offer refactorings.
Edit: I think that this issue does not apply to function literals that have been annotated with async/sync/sync* because, in general, the value that they produce can't be considered to be a list of statements.
The text was updated successfully, but these errors were encountered: