Skip to content

Allow the type of an object pattern to be inferred when specified as _ #4124

@eernstg

Description

@eernstg

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:

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, 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:

class MyClass<X> {
  final X x;
  MyClass(this.x);
}

void main() {
  var xs = MyClass(1); // Inferred as `MyClass<int>(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<num>(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:

void main() {
  var xs = MyClass(1); // Inferred as `MyClass<int>(1)`.
  switch (xs) {
    case _(x: var y) when y.isEven:
      print('Got an A!');
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    patternsIssues related to pattern matching.small-featureA small feature which is relatively cheap to implement.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions