-
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
loop expressions / collection-for alternative #4142
Comments
How do you differentiate between for-expression and for-statement inside var x= sync for(int i=0; i<10; i++) yield i*2;
var y = async for(int i=0; i<10; i++) yield (await foo())*2; |
@tatumizer I don't think for-expression and for-statement is ambiguous. It would be parsed as // (0, 1, 2, 3, 4, 2, 550)
Iterable<int> gen() sync* {
for(int i=0; i<5; i++){
yield i;
}
yield 2;
yield 550;
}
// the for-expression yield doens't yield to main function, only the resulting for iterator
// (2, 550, 0)
Iterable<int> gen() sync* {
var iter = for(int i=0; i<5; i++){
yield i;
};
yield 2;
yield 550;
yield iter.first;
} If you are talking about making it more readable and accessable to the users, it is also possible to add a new keyword // pass keyword with for-expression
Iterable<int> gen() sync* {
var iter = for(int i=0; i<5; i++){
pass i;
};
yield 2;
yield 550;
}
// pass keyword with block expression
print({
var a = 5;
a *= 10;
pass a;
});
// pass keyword with multistatement switch expression
var value = switch(...){
// other patterns
...
int age => {
if(age > 60) pass "Too old";
if(age < 18) pass "Too young";
pass "Good age";
}
_ => "Undefined"
} It's also possible that there is some syntax problem i'm not aware of, and if that's the case, please provide example so i can understand it. |
Technically there is already a way to generate an So this example var iter = for(final item in [1,2,3]){
if(item < 2) continue;
doSomething();
doSomethingElse();
yield item + 3;
} can be written in current dart as var iter = () sync* {
for (final item in [1, 2, 3]) {
if (item < 2) continue;
doSomething();
doSomethingElse();
yield item + 3;
}
}(); |
I'm aware that i can do that (it was mentioned in #1633, if i remember correctly), but i don't really like the syntax. It's sort of unintuitive to read, and also clunky (you have to wrap it inside a generator, and call it). I understand that it's more or less just a sugar, and it isn't really needed, since it doesn't provide any new functionality, but i'd argue it does. Currently, i'd say the reason why people never do it, despite the benefits over collection-for, is that it is really clunky to do it. I think that if it were implemented, it would be used at least somewhat frequently. Still, that's just my biased opinion, but imo the existence of the |
@hydro63: interpretation of expression should not depend on whether its value is assigned to some variable or not. E.g. those are both valid expressions: var x = a > b ? a : b;
a > b ? a: b; Dart's "switch expression" is not a typical "expression" - it's basically a hack; the support of two totally different syntactic forms of switch is one of the most unfortunate parts of the language IMO. |
@tatumizer that's why i've provided another way, where the I know that making a new keyword is a last resort, but that's why i've provided other uses for the new keyword, where it could be used in other proposals. Also, i've found another proposal where it could be used to make a feature less confusing - #4141 with a var x = foo case Foo(:String prop && pass prop) ||(String() && pass); Still, i know that's not perfect, but since this feature is more or less a syntactic sugar, i don't want to compromise on the ease of writing it, if possible. The |
This is different from Iterable literals, and probably more viable This proposal would run synchronously and emit all the elements while building the collection literal. The latter is something you can't do today. [sync* {
Any statements whatsoever, with no yield;
yield value;
}].last) to execute any statements (except yield for an outer function) inside an expression, and evaluate to a value. I think we might want to consider the consequences of that. (What if it does So let's look at it from that perspective instead: of we have a functionality to allow statements inside an expression which evaluates to a single value, should that be extended to allow statements inside an element that emotes zero or more elements. |
That is possible, and probably the best way to do it, since i suppose it would see most use with flutter.
In my mind,
You are right, that statements in expressions should be designed together, if this were to be implemented, but AFAIK that is the Also, i'm starting to realise the limitations of the purely |
An interesting point about f() {
var a = do {
//...
return 0; // return from the do-block, not from f
}
} As you remember, the controversy about the treatment of @lrhn: could you warm up to an idea of (Expression block can take several forms: |
Back when I was first working on collection-for, one of the things I really struggled with was whether to allow blocks and statements inside the collection literal. Doing so is obviously useful, because it means you can do things like define local variables for computations before emitting values. The main problem I ran into is a question of defaults:
The feature we shipped basically takes approach #1 (except that there is no real support for opting in to statements/blocks). Your proposal here is #2. As you note, it has the advantage of being more powerful. You can put all kinds of statements in there. But it comes at the expense of being imperative instead of declarative. The nice thing about an expression like a collection literal is that it's inherently declarative: it implicitly is a value. You don't have to do make in some imperative way. It just is. That's one of the reason why Flutter widget building code can be so nicely declarative and markup-like: Often a If we made the default be "block of statements" then you'd have to imperatively yield each element value. That makes common simple cases more imperative and verbose than I wanted. Here are a few random samples of collection-for in some pub packages with the current syntax and what you propose here: // Current:
[
for (final column in indexedColumns)
IndexedColumn(Reference(columnName: column.nameInSql)),
]
// Proposed:
[
for (final column in indexedColumns) {
yield IndexedColumn(Reference(columnName: column.nameInSql));
}
] And: // Current:
[
if (title != null) ...{
Text(
title,
style: const TextStyle(
color: Color(0xFF212124),
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(height: 8),
},
for (var child in children) child,
]
// Proposed (I'm assuming here that we do something similar for if):
[
if (title != null) {
yield Text(
title,
style: const TextStyle(
color: Color(0xFF212124),
fontSize: 16,
fontWeight: FontWeight.w500,
),
);
yield const SizedBox(height: 8);
},
for (var child in children) {
yield child;
}
] And: // Current:
[
for (
int i = 0;
i < widget.footerCards!.length;
++i
) ...[
if (i > 0) ...[
SizedBox(
height: TodaySpacing.tdsSpace2.spacing,
),
],
widget.footerCards![i],
],
]
// Proposed:
[
for (
int i = 0;
i < widget.footerCards!.length;
++i
) {
if (i > 0) {
yield SizedBox(
height: TodaySpacing.tdsSpace2.spacing,
);
},
yield widget.footerCards![i];
},
] And: // Current:
[
for (final frameData in data.frames)
SpriteAnimationFrame(
Sprite(
image,
srcSize: frameData.srcSize,
srcPosition: frameData.srcPosition,
),
frameData.stepTime,
),
]
// Proposed:
[
for (final frameData in data.frames) {
yield SpriteAnimationFrame(
Sprite(
image,
srcSize: frameData.srcSize,
srcPosition: frameData.srcPosition,
),
frameData.stepTime,
);
}
] There's definitely some nice stuff about what you propose. Users wouldn't have to use the This is obviously a biased sample because I'm only including cases that are already using collection-for. Since what you propose is more expressive, then more code would likely be able to use collection-for that isn't able to today (for example if it wants to declare a local variable). I think this area is worth exploring, but it's tricky to make collection literals more expressive without leading simple cases to be more verbose. I also feel like at some point if you're doing a lot of imperative behavior... maybe it's good to hoist that logic out of a collection literal anyway? |
See also: #4024. |
Motivation / Inspiration
This proposal is proposes a sort of alternative / anti-thesis to the current collection-for we have, it would allow the collection-for to be more powerful and expressive, and it would solve almost all the proposals asking for extending the collection-for. The inspiration / motivation comes from #4139. I've also searched for similar proposals, because i was sure there had to be one already, but there was none exactly to my liking. The most similar proposal i found is #1633. If there is some proposal i've overlooked, please point me to it.
Proposal
The proposal is straightforward. I propose allowing
for
/while
to be used as expressions. The return value for the loop expression would beIterable<T>
/Stream<T>
, which would be lazily evaluated. The values would be passed to the output iterable with ayield
keyword. Basically, you can think of it as a modification on proposal #1633, which makes it imo easier and more compact to use.This allows it to be used outside the collections, and allows for easier representation of complex operation on some list.
collection-for
andfor as expressions
interactionThese two paradigms don't mix well / use the same syntax which makes them ambiguous, which is which. That is a big problem, of which i'm aware of. This proposal is more of an antitheses to the collection-for and as such it is a different look at how to do list comprehention. One way to fix the ambiguity is to assume all
for
is used asexpression
and just spread the resulting iterable. This could be easily done with a migration script.Other
I personally think that that
for as expressions
is a much better way to do list comprehention compared to the python-style collection-for we have today. My main argument for this is thatfor as expressions
allow for a lot more flexibility thancollection-for
without using any of the usefulness (they can also be easily embedded into collections by spreading).The text was updated successfully, but these errors were encountered: