-
Notifications
You must be signed in to change notification settings - Fork 702
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
Updated Wildcard patterns #5950
Conversation
Visit the preview URL for this PR (updated for commit 0c1b4c2): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to suggest a different ordering of the text. It is a little bit tricky because it would be nice to have the two examples (List _
vs. List()
) near to each other. However, it also seems useful to have the object pattern example in the object pattern section, and the wildcard example in the wildcard section.
So my comments are surely not in a usable shape yet, but I hope they can be used to take the next step.
@@ -438,27 +438,39 @@ If an object has extra fields that the pattern doesn't destructure, it can still | |||
|
|||
## Wildcard | |||
|
|||
`_` | |||
`_` | |||
`<type>()` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think most of this needs to go into the '## Object' section.
First, note that _
is a wildcard, but <type>()
is not.
On the other hand, <type>()
isn't precisely the syntax of an object pattern, but it does overlap significantly (for instance, int()
is an object pattern; in contrast, we can't use void Function({String s})()
as an object pattern, even though void Function({String s})
is a <type>
).
The old text below is not very informative, but a wildcard is an identifier pattern in its own right (as in switch (e) { _ => 1 }
or var (_, _) = (2, 3);
). We can add a keyword or a type to make it a variable pattern. For example:
void main() {
switch (1) { case var _: break; }
switch (1) { case final _: break; }
switch (1) { case int _: break; }
}
We might want to make lines 443-444 a bit more explicit or detailed, but otherwise I think they do convey the correct information. So we could just leave them unchanged.
These patterns can either be a [variable pattern](#variable) (`_`) or | ||
[identifier pattern](#identifier) (`<type>()`). | ||
|
||
* Object patterns are more concise and convenient, | ||
even more so with generic types. | ||
With generic types, an object pattern (`MyType()`) infers the | ||
most specific type arguments from the matched value. | ||
* A variable pattern (`MyType _`) uses less specific, often dynamic, | ||
type arguments. | ||
* With non-generic types, there's no reason to prefer variable patterns. | ||
|
||
When you need a subpattern to destructure later positional values, | ||
use a wildcard as a placeholder. This example uses a variable pattern. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 446-447 in the old text could also be kept unchanged (the new text doesn't really fit in this section).
|
||
<?code-excerpt "language/lib/patterns/pattern_types.dart (wildcard-typed)"?> | ||
```dart | ||
switch (record) { | ||
case (int _, String _): | ||
case (int(), String()): | ||
print('First field is int and second is String.'); | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I think it would be OK to keep lines 455-464 from the old text unchanged.
What I wanted to propose in #5926 was actually that there should be an extra paragraph here at the end of this section, perhaps something like the following:
This approach works fine for non-generic types like int
and String
. However, if you wish to test whether the matched value has a generic type then you need to specify the type arguments explicitly. The section about object patterns shows how you can use object patterns to infer the type arguments, which may be both more convenient and safer. Here is an example:
Iterable<int> iter = [2, 3, 4];
switch (iter) {
case List<int> _:
print('It was a list whose first element is ${iter[0].isEven}');
}
If we had used case List _:
rather than case List<int> _:
then it means case List<dynamic> _:
, which in turn means that iter
does not get promoted, and iter[0]
is a compile-time error.
Then we could have something like the following at the end of the 'Object' section:
An extreme example is the case where the object pattern doesn't specify any properties at all. In this case the object pattern amounts to a type test. It is particularly useful in the case where the given type is generic, because the actual type arguments are inferred from the static type of the matched value. For example:
Iterable<int> iter = [2, 3, 4];
switch (iter) {
case List():
print('It was a list whose first element is ${iter[0].isEven}');
}
The object pattern List()
is inferred to mean List<int>()
because the static type of iter
is Iterable<int>
, and this means that the invocation of iter[0].isEven
is type correct.
Hence, it may be a good habit to use an "empty" object pattern like List()
when you just want to test the type of the matched value, rather than using a wildcard like List<int> _
.
I'm going to close this for now. This change will likely be picked up again at a later date, with attribution back to this PR. |
Fixes #5926