@@ -4,7 +4,7 @@ Author: Bob Nystrom
4
4
5
5
Status: In progress
6
6
7
- Version 1.5 (see [ CHANGELOG] ( #CHANGELOG ) at end)
7
+ Version 1.6 (see [ CHANGELOG] ( #CHANGELOG ) at end)
8
8
9
9
## Motivation
10
10
@@ -71,11 +71,8 @@ modified, but may contain references to mutable objects. It implements
71
71
` hashCode ` and ` == ` structurally based on its fields to provide value-type
72
72
semantics.
73
73
74
- A record may have only positional fields or only named fields, but cannot be
75
- totally empty. * There is no "unit type".* A record with no named fields must
76
- have at least two positional fields. * This prevents confusion around whether a
77
- single positional element record is equivalent to its underlying value, and
78
- avoids a syntactic ambiguity with parenthesized expressions.*
74
+ A record may have only positional fields, only named fields, both, or none at
75
+ all.
79
76
80
77
## Core library
81
78
@@ -98,18 +95,17 @@ grammar is:
98
95
```
99
96
literal ::= record
100
97
| // Existing literal productions...
101
- record ::= '(' recordField ( ',' recordField )* ','? ')'
98
+ record ::= 'const'? ' (' recordField ( ',' recordField )* ','? ')'
102
99
recordField ::= (identifier ':' )? expression
103
100
```
104
101
105
- This is identical to the grammar for a function call argument list. There are a
106
- couple of syntactic restrictions not captured by the grammar. It is a
107
- compile-time error if a record has any of:
102
+ This is identical to the grammar for a function call argument list (with an
103
+ optional ` const ` at the beginning). There are a couple of syntactic restrictions
104
+ not captured by the grammar. It is a compile-time error if a record has any of:
108
105
109
106
* The same field name more than once.
110
107
111
- * No named fields and only one positional field. * This avoids ambiguity with
112
- parenthesized expressions.*
108
+ * Only one positional field and no trailing comma.
113
109
114
110
* A field named ` hashCode ` , ` runtimeType ` , ` noSuchMethod ` , or ` toString ` .
115
111
@@ -119,6 +115,21 @@ compile-time error if a record has any of:
119
115
hidden state. Two such records might unexpectedly compare unequal even
120
116
though all of the fields the user can see are equal.*
121
117
118
+ * A field name that collides with the synthesized getter name of a positional
119
+ field. * For example: ` ('pos', $0: 'named') ` since the named field '$0'
120
+ collides with the getter for the first positional field.*
121
+
122
+ In order to avoid ambiguity with parenthesized expressions, a record with
123
+ only a single positional field must have a trailing comma:
124
+
125
+ ``` dart
126
+ var number = (1); // The number 1.
127
+ var record = (1,); // A record containing the number 1.
128
+ ```
129
+
130
+ There is no syntax for a zero-field record expression. Instead, there is a
131
+ static constant ` empty ` on ` Record ` that returns the empty record.
132
+
122
133
### Record type annotations
123
134
124
135
In the type system, each record has a corresponding record type. A record type
@@ -159,7 +170,7 @@ typeNotFunction ::= 'void' // Existing production.
159
170
// New rules:
160
171
recordType ::= '(' recordTypeFields ',' recordTypeNamedFields ')'
161
172
| '(' recordTypeFields ','? ')'
162
- | '(' recordTypeNamedFields ')'
173
+ | '(' recordTypeNamedFields? ')'
163
174
164
175
recordTypeFields ::= recordTypeField ( ',' recordTypeField )*
165
176
recordTypeField ::= metadata type identifier?
@@ -171,24 +182,30 @@ recordTypeNamedField ::= metadata typedIdentifier
171
182
```
172
183
173
184
* The grammar is exactly the same as ` parameterTypeList ` in function types but
174
- without ` () ` , ` required ` , and optional positional parameters since those don't
175
- apply to record types. A record type can't appear in an ` extends ` , ` implements ` ,
185
+ without ` required ` , and optional positional parameters since those don't apply
186
+ to record types. A record type can't appear in an ` extends ` , ` implements ` ,
176
187
` with ` , or mixin ` on ` clause, which is enforced by being a production in ` type `
177
188
and not ` typeNotVoid ` .*
178
189
190
+ The type ` () ` is the type of an empty record with no fields.
191
+
179
192
It is a compile-time error if a record type has any of:
180
193
181
194
* The same field name more than once.
182
195
183
- * No named fields and only one positional field . * This isn't ambiguous, since
184
- there are no parenthesized type expressions in Dart. But there is no reason
185
- to allow single positional element record types when the corresponding
186
- record values are prohibited .*
196
+ * Only one positional field and no trailing comma . * This isn't ambiguous,
197
+ since there are no parenthesized type expressions in Dart. But prohibiting
198
+ this is symmetric with record expressions and leaves the potential for
199
+ later support for parentheses for grouping in type expressions .*
187
200
188
201
* A field named ` hashCode ` , ` runtimeType ` , ` noSuchMethod ` , or ` toString ` .
189
202
190
203
* A field name that starts with an underscore.
191
204
205
+ * A field name that collides with the synthesized getter name of a positional
206
+ field. * For example: ` (int, $0: int) ` since the named field '$0' collides
207
+ with the getter for the first positional field.*
208
+
192
209
### No record type literals
193
210
194
211
There is no record type literal syntax that can be used as an expression, since
@@ -201,6 +218,28 @@ var t = (int, String);
201
218
This is a record expression containing two type literals, ` int ` and ` String ` ,
202
219
not a type literal for a record type.
203
220
221
+ ### Ambiguity with ` on ` clauses
222
+
223
+ Consider:
224
+
225
+ ``` dart
226
+ void foo() {
227
+ try {
228
+ ;
229
+ } on Bar {
230
+ ;
231
+ }
232
+ on(a, b) {;} // <--
233
+ }
234
+ ```
235
+
236
+ Before, the marked line could only be declaring a local function named ` on ` .
237
+ With record types, it could be a second ` on ` clause for the ` try ` statement
238
+ whose matched type is the record type ` (a, b) ` . When presented with this
239
+ ambiguity, we disambiguate by treating ` on ` as a clause for ` try ` and not a
240
+ local function. This is technically a breaking change, but is unlikely to affect
241
+ any code in the wild.
242
+
204
243
## Static semantics
205
244
206
245
We define ** shape** to mean the number of positional fields (the record's
@@ -218,17 +257,18 @@ handle function typedefs.)
218
257
219
258
A record type declares all of the members defined on ` Object ` . It also exposes
220
259
getters for each named field where the name of the getter is the field's name
221
- and the getter's type is the field's type.
222
-
223
- Positional fields are not exposed as getters. * Record patterns in pattern
224
- matching can be used to access a record's positional fields.*
260
+ and the getter's type is the field's type. For each positional field, it exposes
261
+ a getter whose name is ` $ ` followed by the number of preceding positional fields
262
+ and whose type is the type of the field.
225
263
226
264
For example, the record expression ` (1.2, name: 's', true, count: 3) ` has a
227
265
record type whose signature is like:
228
266
229
267
``` dart
230
268
class extends Record {
269
+ double get $0;
231
270
String get name;
271
+ bool get $1;
232
272
int get count;
233
273
}
234
274
```
@@ -283,17 +323,54 @@ fields are) and collection literals.
283
323
284
324
** TODO: Specify this more precisely.**
285
325
326
+ ### Constants
327
+
328
+ A record expression in a constant context or beginning with ` const ` defines a
329
+ constant record. A record expression starting with ` const ` establishes a const
330
+ context for its fields. It is a compile-time error if a field of a constant
331
+ record is not constant.
332
+
333
+ Since identity is definely loosely for records, an implementation is not
334
+ required to canonicalize equivalent constant records.
335
+
336
+ ``` dart
337
+ print(identical(const (1, 2), const (1, 2)));
338
+ ```
339
+
340
+ This may print ` true ` or ` false ` .
341
+
286
342
## Runtime semantics
287
343
288
344
### Records
289
345
290
- #### Members
346
+ #### Field getters
291
347
292
348
Each field in the record's shape exposes a corresponding getter. Invoking that
293
349
getter returns the value provided for that field when the record was created.
294
350
Record fields are immutable and do not have setters.
295
351
296
- The ` toString() ` method's behavior is unspecified.
352
+ #### ` toString() `
353
+
354
+ In debug builds, the ` toString() ` method converts each field to a string by
355
+ calling ` toString() ` on its value and prepending it with the field name followed
356
+ by ` : ` if the field is named. It concatenates these with ` , ` as a separator
357
+ and returns the resulted surrounded by parentheses. For example:
358
+
359
+ ``` dart
360
+ print((1, 2, 3).toString()); // "(1, 2, 3)".
361
+ print((a: 'str', 'ing').toString()); // "(a: str, int)".
362
+ ```
363
+
364
+ The order that named fields appear and how they are interleaved with positional
365
+ fields is unspecified. Positional fields must appear in position order. * This
366
+ gives implementations freedom to choose a canonical order for named fields
367
+ independent of the order that the record was created with.*
368
+
369
+ In a release or optimized build, the behavior of ` toString() ` is unspecified.
370
+ * This gives implementations freedom to discard the full names of named fields in
371
+ order to reduce code size.* Users should only use ` toString() ` on records for
372
+ debugging purposes. They are strongly discouraged from parsing the results of
373
+ calling ` toString() ` or relying on it for end-user visible output.
297
374
298
375
#### Equality
299
376
@@ -379,6 +456,18 @@ covariant in their field types.
379
456
380
457
## CHANGELOG
381
458
459
+ ### 1.6
460
+
461
+ - Support constant records (#2337 ).
462
+
463
+ - Support empty and one-positional-field records (#2386 ).
464
+
465
+ - Re-add support for positional field getters (#2388 ).
466
+
467
+ - Specify the behavior of ` toString() ` (#2389 ).
468
+
469
+ - Disambiguate record types in ` on ` clauses (#2406 ).
470
+
382
471
### 1.5
383
472
384
473
- Make the grammar for record types closer to function type parameter lists.
0 commit comments