Skip to content
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

Support continue inside collection for expressions #4139

Open
rrousselGit opened this issue Oct 24, 2024 · 12 comments
Open

Support continue inside collection for expressions #4139

rrousselGit opened this issue Oct 24, 2024 · 12 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@rrousselGit
Copy link

Hello!

Currently, [for (...) item] doesn't support the continue keyword.
Although we can express everything without it, this can lead to a lot of nesting.

The continue keyword is a very useful tool to unwrap some amount of nesting. For example:

for (final item in list) {
  if (condition) {
    doSomething();
  }
}

// vs:

for (final item in list) {
  if (!condition) continue;
  doSomething();
}

It'd be cool to support continue inside collections too:

final list = [
  for (final item in [1,2,3])
    if (item < 2) continue
    item,
]
@rrousselGit rrousselGit added the feature Proposed language feature that solves one or more problems label Oct 24, 2024
@hydro63
Copy link

hydro63 commented Oct 24, 2024

This is possible, but it isn't very useful imo. You can just as easily do array.where(...).map(...), and it would have the same functionality. The true power of continue can be seen when you have multiple statements, which the current list comprehension doesn't allow.

For clarification, i'm not saying continue is bad, just that it is insufficient. There is already a proposal for blocked expression, which would allow you to write the same thing, and a lot more. Another way to do exactly that is to have for as expression in Dart (which imo is a lot better than list comprehention).

// for as expression
// for expression returns iterable, yield passes the value to iterable

final list = [
  firstElement,
  ... for(final item in [1,2,3]){
    if(item < 2) continue;
    doSomething();
    doSomethingElse();
    yield item + 3;
  },
  lastElement,
];

// for -> Iterable<int> -> List<int> -> retList
var retList = for(final item in [1,2,3]){
  if(item < 2) continue;
  doSomething();
  doSomethingElse();
  yield item + 3;
}.toList();

But then again, both list comprehention and for expressions are just arr.map under the hood, so i don't know how useful it really is.

@rrousselGit
Copy link
Author

This is possible, but it isn't very useful imo. You can just as easily do array.where(...).map(...), and it would have the same functionality. The true power of continue can be seen when you have multiple statements, which the current list comprehension doesn't allow.

By that standard, we never need [for]/[if] :)
Similarly, multiple statements could be expressed with .expand

I agree that multiple statements in for would be nice. But that's a separate feature.

@mateusfccp
Copy link
Contributor

For this to happen, we would have to change dramatically how collection-if work. Currently, they expect an expression. We would have to have an expression in the form if (<condition>) continue <expression>, or we would have to accept non-expressions on collection-ifs, which would be even more complex IMO.

As for my personal opinion (because this issue is based on subjective perceptions),

for (final item in list) {
  if (condition) {
    doSomething();
  }
}

Is better than:

for (final item in list) {
  if (!condition) continue;
  doSomething();
}

Also, I can't see how

final list = [
  for (final item in [1,2,3])
    if (item < 2) continue
    item,
]

is better than

final list = [
  for (final item in [1,2,3])
    if (item >= 2) item,
]

@mateusfccp
Copy link
Contributor

mateusfccp commented Oct 24, 2024

You can just as easily do array.where(...).map(...), and it would have the same functionality.

The idiomatic way to do this in Dart is to use collection-if/collection-for instead of collection methods, so it just makes sense for the language to be more expressive in this regard.

@hydro63
Copy link

hydro63 commented Oct 24, 2024

The idiomatic way to do this in Dart is to use collection-if/collection-for instead of collection methods, so it just makes sense for the language to be more expressive in this regard.

I understand the reasoning, but i don't really think that expanding what collection-for can do is the way to go. As i said, i think that for as expression would be a lot better construct, than the python style list comprehention we have.

Still, since collection-for is already part of Dart, i guess it is already decided.

@rrousselGit
Copy link
Author

We would have to have an expression in the form if (<condition>) continue <expression>

That's what I'm asking, yes.

For a simple example, the difference may not be big. But you can technically chain if/for multiple times

For example, we could do:

if (!a) continue
if (!b) continue
for (...)
  <expr>

as opposed to:

if (a)
  if (b)
    for (...
      <expr>

Widgets are already quite nested.
DartFmt is getting reworked in large part for the sake of removing levels of indentations inside widgets. IMO there's value here.

@Levi-Lesches
Copy link

I agree that there's value to a short if (!something) continue as opposed to wrapping a possibly complex expression in an if. Can't say I haven't wished for this feature a few times myself

@tatumizer
Copy link

Rather than adding more and more syntax, dart could optimize Iterable methods (where, map and friends) - e.g by inlining them whenever possible - so you would never need anything beyond

[...[1, 2, 3]
  .where(# < 2) // no extra indentation
  .map(# + 10)  // no extra indentation
  .etc...
]

(You can do it today, albeit with a huge hit in performance).

@rrousselGit
Copy link
Author

@tatumizer Dart added collection expressions for a reason.

Using iterables with widgets both has horrible readability and is a lot more limited.

@lrhn
Copy link
Member

lrhn commented Nov 13, 2024

As pointed out, the way elements work, they are generally in tail position, so you don't need a continue, just an empty branch.

The proposal is to introduce a new syntactic form

  if (expression) continue element

which is really a "shorthand" syntax for if (!(expression)) element, and with an accompagnying formatting that makes it look like

  if (expression) continue
  element

Doesn't seem like it's worth its own complexity.

@Wdestroier
Copy link

An idea is to have if! (expression) element as an alternative to if (!(expression)) element.

@mateusfccp
Copy link
Contributor

An idea is to have if! (expression) element as an alternative to if (!(expression)) element.

This would be basically an unless (❤️), but I think the main gist of this issue is to have a semantic that makes sense to format in the same column instead of nesting it with indentation.

By having an unless-like condition, by itself, we would still have indented nesting:

if! (a)
  if! (b)
    for (...)
      <expr>

Instead of the proposed

if (!a) continue
if (!b) continue
for (...)
  <expr>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

7 participants