@@ -4,7 +4,7 @@ Author: Bob Nystrom
4
4
5
5
Status: Accepted
6
6
7
- Version 2.28 (see [ CHANGELOG] ( #CHANGELOG ) at end)
7
+ Version 2.29 (see [ CHANGELOG] ( #CHANGELOG ) at end)
8
8
9
9
Note: This proposal is broken into a couple of separate documents. See also
10
10
[ records] [ ] and [ exhaustiveness] [ ] .
@@ -604,7 +604,7 @@ rest element with no subpattern as a *non-matching rest element*.
604
604
```
605
605
mapPattern ::= typeArguments? '{' mapPatternEntries? '}'
606
606
mapPatternEntries ::= mapPatternEntry ( ',' mapPatternEntry )* ','?
607
- mapPatternEntry ::= expression ':' pattern | '...'
607
+ mapPatternEntry ::= expression ':' pattern
608
608
```
609
609
610
610
A map pattern matches values that implement ` Map ` and accesses values by key
@@ -621,45 +621,31 @@ It is a compile-time error if:
621
621
expressions in a future release without it being a breaking change, similar
622
622
to default values in parameter lists.*
623
623
624
- * Any two keys in the map are identical. * Map patterns that don't have a rest
625
- element only match if the ` length ` of the map is equal to the number of map
626
- entries. If a map pattern has multiple identical key entries, they will
627
- increase the required length for the pattern to match but in all but the
628
- most perverse ` Map ` implementations will represent the same key. Thus, it's
629
- very unlikely that any map pattern containing identical keys (and no rest
630
- element) will ever match. Duplicate keys are most likely a typo in the
631
- code.*
632
-
633
- * Any two record keys which both have primitive equality are equal. * Since
634
- records don't have defined identity, we can't use the previous rule to
635
- detect identical records. But records do support an equality test known at
636
- compile time if all of their fields do, so we use that.*
624
+ * Note that we don't require map keys to have primitive equality, to enable
625
+ more flexibility in key types.*
637
626
638
- * There is more than one ` ... ` element in the map pattern.
627
+ #### Open and closed maps
639
628
640
- * The ` ... ` element is not the last element in the map pattern.
629
+ Unlike list and record patterns (but like object patterns), map patterns don't
630
+ require the pattern to match the * entire* map. If a map has extra keys that
631
+ aren't destructured by the pattern, it can still match.
641
632
642
- * Note that we don't require map keys to have primitive equality, to enable
643
- more flexibility in key types. If the keys have user-defined ` == ` methods, then
644
- it's possible to have keys that are equal according to those ` == ` methods, but
645
- the compiler won't detect it.*
646
-
647
- #### Rest elements
633
+ This aligns with the most common use cases for working with maps where extra
634
+ keys should be silently ignored. When maps are used as protocols, it tends to
635
+ make pattern matching code over those maps more resilient to protocol evolution.
648
636
649
- Like lists, map patterns can also have a rest element. However, there's no
650
- well-defined notion of a map "minus" some set of matched entries. Thus, only a
651
- non-matching rest element is allowed.
637
+ Ignoring extra keys also makes maps more reliable to use in irrefutable contexts
638
+ where an extra key would otherwise cause a runtime exception.
652
639
653
- Also, there is no ordering to entries in a map, so we only allow the ` ... ` to
654
- appear as the last entry. Appearing anywhere else would send a confusing,
655
- meaningless signal.
640
+ If you want to check that a map has a given set of keys and no others, the
641
+ easiest way is to check the length in a guard:
656
642
657
- In practice, this means that the only purpose of ` ... ` in a map pattern is to
658
- allow matching a map that contains extra entries, while ignoring those entries.
659
- By default, a map pattern only matches if the map's length is exactly the same
660
- as the number of entry subpatterns. Adding a ` ... ` element allows the map
661
- pattern to match if the map's length is * at least * the number of (non-rest)
662
- entry subpatterns (and all of those subpatterns match).
643
+ ``` dart
644
+ switch ( map) {
645
+ case {'a': _, 'b': _} when map. length == 2:
646
+ print('Only a and b');
647
+ }
648
+ ```
663
649
664
650
### Record pattern
665
651
@@ -997,9 +983,9 @@ in assignments, it is useful to have an expression statement that begins with
997
983
```dart
998
984
var map = {'a': 1, 'b': 2};
999
985
int a, b;
1000
- // more code...
986
+ // More code...
1001
987
1002
- // later ...
988
+ // Later ...
1003
989
{'a': a, 'b': b} = map;
1004
990
```
1005
991
@@ -3035,48 +3021,7 @@ To match a pattern `p` against a value `v`:
3035
3021
*This type test may get elided. See "Pointless type tests and legacy
3036
3022
types" below.*
3037
3023
3038
- 2. Let `n` be the number of non-rest elements.
3039
-
3040
- 3. Check the length:
3041
-
3042
- 1. If `p` has a rest element and `n == 0`, then do nothing for checking
3043
- the length.
3044
-
3045
- *We only call `length` on the map if needed. If the pattern is
3046
- `{...}`, then any length is allowed, so we don't even ask the map
3047
- for it.*
3048
-
3049
- 2. Else let `l` be the length of the map determined by calling `length`
3050
- on `v`.
3051
-
3052
- 3. If `p` has a rest element *(and `n > 0`)*:
3053
-
3054
- 1. If `l < n` then the match fails.
3055
-
3056
- *When there are non-rest elements and a rest element, the map must
3057
- be at least long enough to match the non-rest elements.*
3058
-
3059
- 4. Else if `n > 0` *(and `p` has no rest element)*:
3060
-
3061
- 1. If `l != n` then the match fails.
3062
-
3063
- *If there are only non-rest elements, then the map must have exactly
3064
- the same number of elements.*
3065
-
3066
- 5. Else `p` is empty:
3067
-
3068
- 1. If `l > 0` then the match fails.
3069
-
3070
- *An empty map pattern can match only empty maps. Note that this
3071
- treats a misbehaving map whose `length` is negative as an empty map.
3072
- This is important so that a set of map patterns that is clearly
3073
- exhaustive over well-behaving maps will also cover a misbehaving
3074
- one.*
3075
-
3076
- *These match failures become runtime exceptions if the map pattern is
3077
- in an irrefutable context.*
3078
-
3079
- 4. For each non-rest entry in `p`, in source order:
3024
+ 2. For each entry in `p`, in source order:
3080
3025
3081
3026
*Unlike in list patterns, we don't skip wildcard subpatterns. In a map
3082
3027
pattern, you may want to use a `_` value subpattern to detect whether a
@@ -3123,7 +3068,7 @@ To match a pattern `p` against a value `v`:
3123
3068
4. Else, match `r` against this entry's value subpattern. If it does
3124
3069
not match, the map does not match.
3125
3070
3126
- 5 . The match succeeds if all entry subpatterns match.
3071
+ 3 . The match succeeds if all entry subpatterns match.
3127
3072
3128
3073
* **Record**:
3129
3074
@@ -3434,9 +3379,7 @@ To bind invocation keys in a pattern `p` using parent invocation `i`:
3434
3379
3435
3380
* **Map**:
3436
3381
3437
- 1. Bind `i : ("length", [])` to the `length` getter invocation.
3438
-
3439
- 2. For each entry in `p`:
3382
+ 1. For each entry in `p`:
3440
3383
3441
3384
1. Bind `i : ("containsKey()", [key])` to the `containsKey()`
3442
3385
invocation where `key` is entry's key constant value.
@@ -3556,6 +3499,20 @@ Here is one way it could be broken down into separate pieces:
3556
3499
3557
3500
## Changelog
3558
3501
3502
+ ### 2.29
3503
+
3504
+ - Map patterns no longer check length.
3505
+
3506
+ - Remove `...` from map patterns since it is redundant with the previous
3507
+ change.
3508
+
3509
+ - Make it an error to have an empty map pattern. Since map patterns don't
3510
+ check their length, an empty map pattern will match all maps, which is
3511
+ likely to confuse users. For now, to minimize confusion, we just disallow
3512
+ it.
3513
+
3514
+ - Make it no longer an error for map patterns to have duplicate keys.
3515
+
3559
3516
### 2.28
3560
3517
3561
3518
- Clarify that when downwards is used to infer type arguments for an object
0 commit comments