dart-lang / language

Design of the Dart language
Other
2.66k stars 205 forks source link

allow to destructure record types with positional fields #3782

Open ekuleshov opened 5 months ago

ekuleshov commented 5 months ago

The Dart language allows to define a positional fields record and then destructure its fields like so:

  final (int, int, int) color = (1, 2, 3);  
  final (r, g, b) = color;
  print(r);

Also, in the following snippet the record type declaration containing field names is valid, but the field names declared in the record type can't be dereferenced:

  final (int r, int g, int b) color = (1, 2, 3);  
  print(r); // <-- "undefined name 'r'" at compile time

It would be great if the Dart language allowed to dereference these values by name. This would be a really handy feature when records with positional fields are passed around.

Consider the following example. The record type declaration in the map() method is valid, but these parameters can't be used in the lambda expression.

  List<String> list = ['a', 'b', 'c'];
  print(list.indexed.map(((int index, String s) en) => '$index:$s').join(', '));

Currently we have instead write lambda like (en) => '${en.$1}:${en.$2}', which is much less readable code.

julemand101 commented 5 months ago

This are more fitting for the Dart Language issue tracker. Also, your last suggestion are very similar to: https://github.com/dart-lang/language/issues/3001

ekuleshov commented 5 months ago

This are more fitting for the Dart Language issue tracker. Also, your last suggestion are very similar to: dart-lang/language#3001

Wasn't sure if that is a language feature or a compiler issue. As you can see compiler allows to specify field names, but it does not allow to dereference them.

Please move/triage as you see fit.

julemand101 commented 5 months ago
  final (int r, int g, int b) color = (1, 2, 3);  

Since you have put the variable name color in here, it means you are creating a variable named color that contains a Record defined as the type (int r, int g, int b). The name of each variable in the record are not getting used so it kinda pointless besides for documentation.

Are you asking for this syntax instead should define the variable r, g, b and color? If so, it gets a bit confusing when having records with named arguments since should they also be defined as variables in your code or not?

final (int r, int g, int b) = (1, 2, 3);

Here, we don't have color so we can assume you want to destruct the record into three variables.

ekuleshov commented 5 months ago

Here, we don't have color so we can assume you want to destruct the record into three variables.

I know. The catch is that compiler allows these param names when record name is declared after its type declaration. And that declaration looks similar to what you get when declaring record type in parameters of other methods.

In my other example, the type of en parameter ((int index, String s) en) => ... is accepted by the Dart compiler, but the compilation errors come from dereferencing index and s there.

lrhn commented 5 months ago

The syntaxes for types and for patterns are eerily similar, for very good reasons, which is actually why what you're trying doesn't work The parser needs to figure out whether it is a seeing a type or a pattern, and it uses the following identifier to decide that what came before is a type, not a pattern.

So you would have to write

final (int r, int g, int b) & color = ...;

to bind all the names ... if declaration patterns allowed & patterns. (They probably should, but today they don't.)

eernstg commented 5 months ago

We can actually use a variant, as long as we stick to an <outerPattern> as the outermost construct:

void main() {
  final ((int r, int g, int b) && color) = (2, 3, 4);
  ...
}
ekuleshov commented 5 months ago

The parser needs to figure out whether it is a seeing a type or a pattern, and it uses the following identifier to decide that what came before is a type, not a pattern.

I wonder if parser could delay its decision about exact type or infer a pattern for the time being until it is time to disambiguate exact type?

I used variable declaration for record, just to illustrate that compiler takes it as a valid syntax. Don't really have use for that variable (unless Dart would allow to do something like color.r instead of direct reference to r).

Other syntax variants discussed here and in dart-lang/language#3001 are adding some syntactic noise. Out of all, this one (:int r, :int g, :int b) would be concise in context of list.indexed.map(((:int index, :String s)) => '$index:$s').

mmcdon20 commented 5 months ago

See also https://github.com/dart-lang/language/issues/3487.

lrhn commented 5 months ago

So this is a request for two things:

This is a language issue, so moving to language repository.