language icon indicating copy to clipboard operation
language copied to clipboard

Safe list destructuring

Open Reprevise opened this issue 8 months ago • 3 comments

Let's say I have a variable called list that stores, you guessed it, a list of items.

final list = ['a', 'b'];

For this example, we're always going to assume that list at least has a length of 1. When we need to safely access the second value in the list though, it's kind of annoying.

final a = list.first;
final b = list.elementAtOrNull(1);

Here are some proposed syntax additions that can help make it easier to safely access list items:

final [a, b?] = list; // b would be null if the list doesn't have enough items

For the syntax below, you can specify default values, but they must be a subtype of the inferred list type. So list is inferred as List<String> so you can do default values of type String?.

Using else:

final [a, b else null] = list;
final [a, b else 'b'] = list;

Using ??:

final [a, b ?? null] = list; // looks odd but could work, reads as if not present, use null
final [a, b ?? 'b'] = list;

Default value syntax used in constructors:

final [a, b = null] = list;
final [a, b = 'b'] = list;

Reprevise avatar Oct 25 '23 01:10 Reprevise

The default-value syntax or else syntax are the most viable, the ?? one looks like it should act on null, not absence.

Also makes sense for map patterns, as a way to match an entry if it's there, but also continue is it's not.

Not sure how it generalizes to refutable patterns. As written it makes sense for a binding pattern. It must be at the top-level list element pattern, since it's what triggers of there is no matched value, and we have to make that choice before trying any pattern that has to use that value.

Using the default/else value as they matched value, and still executing the pattern is the most consistent, it ensures all the bindings exist.

lrhn avatar Oct 25 '23 06:10 lrhn

the ?? one looks like it should act on null, not absence.

Yeah I'm just throwing out ideas, it didn't look great to me either. I also had a b : null syntax idea but I thought it'd be too bad of an idea to post here lol.

Also makes sense for map patterns, as a way to match an entry if it's there, but also continue is it's not.

Agreed, I could file a separate issue for that, or make this issue more generic. That could also apply if it wasn't the type you were expecting, instead of throwing an exception, but that behavior seems unclear, though it could make programs safer.

Not sure how it generalizes to refutable patterns. As written it makes sense for a binding pattern. It must be at the top-level list element pattern, since it's what triggers of there is no matched value, and we have to make that choice before trying any pattern that has to use that value.

I assume this is in response to final [a, b?] = list; which I actually tried to write (it's what inspired this issue), and to me seems natural to read. It's also the shortest syntax I proposed. The ? is synonymous with nullable so I thought it just made sense.

Reprevise avatar Oct 26 '23 17:10 Reprevise

Any movement on this?

My Use Case

I'm writing some extension methods for cloud_firestore's Filter class that has a named constructor taking up to 30 arguments. I'd love to create an extension getter so I can have something like:

final myListFilters = [...some actual filters here];

myListOfFilters.asAndGroup;

In the asAndGroup method I'd want something like this:


    final [
      l1,
      l2,
      l3?,
      l4?,
     ... all the way to 30
    ] = this;

    return Filter.and(
      l1, // required `Filter`
      l2, // required `Filter`
      l3, // optional `Filter?`
      l4, // optional `Filter?`
      ... all the way to 30
    );

SupposedlySam avatar Apr 19 '24 15:04 SupposedlySam