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

Null-aware collection-for #3730

Open
anqit opened this issue Apr 27, 2024 · 7 comments
Open

Null-aware collection-for #3730

anqit opened this issue Apr 27, 2024 · 7 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@anqit
Copy link

anqit commented Apr 27, 2024

Currently, for collection-for's on nullable collections, we need a null check:

List<int>? myInts = ...;

if (myInts != null) {
    for (final i in myInts) {
        // use i
    }
}

It would be nice if we could have a null-aware version of this syntax, something along the lines of

for(final i in myInts?) ...

or

for(final i in? myInts) ...

which would skip iteration if the iterable were null.

Sorry if this already exists and I'm just (null-)unaware of it :)

@anqit anqit added the feature Proposed language feature that solves one or more problems label Apr 27, 2024
@lrhn
Copy link
Member

lrhn commented Apr 27, 2024

It does not exist. I'm not sure it will.

One issue is that you should rarely have a nullable collection, it's better to use an empty collection, and not have two ways to represent "no elements".
Another is that the syntax could be better used for something else.

If we get Null-aware elements (#323), then you should be able to write:

 for (final i in [?myInts]) { ... }

and with a little luck and prodding, the compiler should be able to optimize that into:

if (myInts case final myInts?) for (final i in myInts) { ... }

(if you don't want to write that out yourself).

@anqit
Copy link
Author

anqit commented Apr 27, 2024

you should rarely have a nullable collection

while largely I agree, one situation I have run into this is optional function parameters.
Also, do you mind explaining or linking to whatever pattern-matching foo is happening here:
if (myInts case final myInts?) ..., I don't think I've seen that syntax before

@julemand101
Copy link

@anqit https://dart.dev/language/pattern-types#null-check

It matches if myInts are not null and it will define a new variable called myInts inside the scope of the if-statement which have a non-nullable type.

@munificent
Copy link
Member

If we get Null-aware elements (#323), then you should be able to write:

 for (final i in [?myInts]) { ... }

That wouldn't do what you want because it would only unwrap the outer list, so if myInts is not null, then you get one iteration where i is bound to the entire myInts list. What you really want is:

for (final i in [...?myInts]) { ... }

That's a little silly, but does work and do the right thing.

Figuring out what sort of null-aware operations are worth adding to Dart and which aren't is hard. In theory, we could support null-aware operations for every possible operation that has a reasonable "do nothing" semantics to fall back on when the value is null. That's definitely the case for for loops: just don't execute the body at all. We could also have null-aware while loops.

But in most cases, I think it's easy enough to use ?? and then you get code that's clearer about what happens when the value is null.

@lrhn
Copy link
Member

lrhn commented Sep 26, 2024

I think we should guarantee that a spread of a list literal, ...[something in here], is always optimized to avoid allocating a list.
It will guaranteed work as an element by having each element emitted by the content of the list being directly emitted into the outer elements instead. (Like yield* flattening, if we do that.)

(Or introduce a new element syntax for inlining a number of elements in the current collection, fx .[e1, e3, e3] in a list literal, .{e1, e3, e3} in a list literal and ."foo${e1}bar${e2}vaz" if we get string interpolation elements.)

@Reprevise
Copy link

I think we should guarantee that a spread of a list literal, ...[something in here], is always optimized to avoid allocating a list. It will guaranteed work as an element by having each element emitted by the content of the list being directly emitted into the outer elements instead. (Like yield* flattening, if we do that.)

I like this idea. Anytime I'm writing an if statement in a List (like I do a lot in Flutter) I always get worried about using the spread operator like so:

return Column(
  children: [
    if (condition) ...[
      FooWidget(),
      const SizedBox(height: 10),
    ],
  ],
);

Don't mean to hijack this issue, just saw Irhn's comment and wanted to reply to it

@munificent
Copy link
Member

munificent commented Oct 10, 2024

Anytime I'm writing an if statement in a List (like I do a lot in Flutter) I always get worried about using the spread operator like so:

Don't worry about it (unless your profiler tells you otherwise). If you were to not use the spread operator, the code you would write instead (like calling addAll() is likely also going to lead you to create a list. And, in general, Dart tends to be very fast when it comes to short-lived objects.

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

5 participants