dart-lang / dart-lang/language
Allow the type of an object pattern to be inferred when specified as `_`
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
Ideas similar to this one have been discussed already when patterns were introduced, but I don't think there's an issue where the proposal has been presented in a concrete form.
We could make object patterns a bit more concise and convenient by using a wildcard `_` to indicate the type of the object pattern when it can be inferred:
```dart
class const PairClassWithATerriblyLongName(int x, int y);
void main() {
var pair = PairClassWithATerriblyLongName(2, 3);
// Using the proposed feature:
var number = switch (pair) {
_(x: >= 0, y: >= 0) => 'first',
_(x: < 0, y: >= 0) => 'second',
_(x: < 0, y: < 0) => 'third',
_(x: >= 0, y: < 0) => 'fourth',
};
// Compare this to the current syntax:
number = switch (pair) {
PairClassWithATerriblyLongName(x: >= 0, y: >= 0) => 'first',
PairClassWithATerriblyLongName(x: < 0, y: >= 0) => 'second',
PairClassWithATerriblyLongName(x: < 0, y: < 0) => 'third',
PairClassWithATerriblyLongName(x: >= 0, y: < 0) => 'fourth',
};
print('Found a pair in the $number quadrant');
}
```
(I'm assuming that we have [primary constructors](https://github.com/dart-lang/language/blob/main/working/2364%20-%20primary%20constructors/feature-specification.md), hopefully we will have them soon.)
It might be claimed that `_` causes the code to be less readable than it would have been with the explicit type. However, it's worth noting that when the `_` occurs repeatedly at the top level in cases like the above example then it is easy to see that every `_` stands for the same type, and it must be the static type of the scrutinee. Similarly, when `_` is used in a nested object pattern, e.g., `PairClassWithATerriblyLongName(x: _(isEven: true))`, the type of the nested object pattern could be rather easy to determine based on the enclosing patterns.
In any case, it will be a matter of style whether `_` is an acceptably readable type on any given object pattern, and individual organizations and developers will choose a style for this, just like they are choosing a style for many other things.
The inferred type of an object pattern may include actual type arguments:
```dart
class MyClass {
final X x;
MyClass(this.x);
}
void main() {
var xs = MyClass(1); // Inferred as `MyClass(1)`.
switch (xs) {
case MyClass(x: var y) when y.isEven:
print('Got an A!');
}
}
```
In the object pattern `Myclass(x: var y)`, the type argument `int` is inferred. We can see that it is actually `int` because `y.isEven` is accepted with no errors, and also because `y.isEven` is an error when we have `MyClass(x: var y)`. We can also see that the inferred type isn't `dynamic`, because `y.unknownMember` is an error.
This should all work the same when we use the proposed feature:
```dart
void main() {
var xs = MyClass(1); // Inferred as `MyClass(1)`.
switch (xs) {
case _(x: var y) when y.isEven:
print('Got an A!');
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.