Skip to content

Commit 9b2d2e9

Browse files
authored
A little copy-editing on the enum shorthand proposal. (#4126)
A little copy-editing on the enum shorthand proposal. I was reading the latest changes and noticed a duplicated sentence. And once I fixed that, I started noticing other things... There are no semantic changes. This is just making it hopefully easier to read.
1 parent 13dc7e2 commit 9b2d2e9

File tree

1 file changed

+40
-35
lines changed

1 file changed

+40
-35
lines changed

working/3616 - enum value shorthand/proposal-simple-lrhn.md

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ are fairly simple and easy to explain.
77

88
### Elevator pitch
99

10-
An expression starting with `.` is an implicit static namespaces access on the
10+
An expression starting with `.` is an implicit static namespace access on the
1111
*apparent context type*.
1212

13-
The type that the context expects is known, and the expression avoids repeating
14-
the type, and starts by doing a static access on that type.
13+
Since the type that the context expects is known, the shorthand expression
14+
avoids repeating the type, and starts by doing a static access on that type.
1515

1616
This makes immediate sense for accessing enum and enum-like constants or
1717
invoking constructors, which will have the desired type. There is no requirement
@@ -22,7 +22,7 @@ the end. The context type used is the one for the entire selector chain.
2222
There must be a context type that allows static member access, similar to when
2323
we allow static access through a type alias.
2424

25-
We also special-case the `==` and `!=` operators, but nothing else.
25+
We also special-case the `==` and `!=` operators, but nothing else.
2626

2727
## Specification
2828

@@ -44,7 +44,8 @@ We introduce grammar productions of the form:
4444

4545
We also add `.` to the tokens that an expression statement cannot start with.
4646

47-
That means you can write things like the following (with the intended meaning as comments, specification to achieve that below):
47+
That means you can write things like the following (with the intended meaning as
48+
comments, specification to achieve that below):
4849

4950
```dart
5051
Endian littleEndian = .little; // -> Endian.little (enum value)
@@ -79,6 +80,8 @@ This is a simple grammatical change. It allows new constructs in any place where
7980
we currently allow primary expressions, which can be followed by selector chains
8081
through the `<postfixExpression>` production `<primary> <selector>*`.
8182

83+
#### Non-ambiguity
84+
8285
A `<primary>` cannot immediately follow any other complete expression. We trust
8386
that because a primary expression already contains the production
8487
`'(' <expression> ')'` which would cause an ambiguity for `e1(e2)` since `(e2)`
@@ -100,17 +103,17 @@ shorthand can compile at all. If we ever allow metadata on statements, we don’
100103
want `@foo . bar(4) ;` to be ambiguous. If we ever allow metadata on
101104
expressions, we have bigger issues.)_
102105

103-
A primary expression *can* follow a `?` in a conditional expression,
106+
A primary expression *can* follow a `?` in a conditional expression, as in
104107
`{e1 ? . id : e2}`. This is not ambiguous with `e1?.id` since we parse `?.` as a
105108
single token, and will keep doing so. It does mean that `{e1?.id:e2}` and
106109
`{e1? .id:e2}` will now both be valid and have different meanings, where the
107110
existing grammar didn’t allow the `?` token to be followed by `.` anywhere.
108111

109112
### Semantics
110113

111-
Dart semantics, static and dynamic, does not follow the grammar precisely. For
114+
Dart semantics, static and dynamic, do not follow the grammar precisely. For
112115
example, a static member invocation expression of the form `C.id<T1>(e2)` is
113-
treated as an atomic entity for type inference (and runtime semantics), it’s not
116+
treated as an atomic entity for type inference (and runtime semantics). It’s not
114117
a combination of doing a `C.id` tear-off, then a `<T1>` instantiation and then
115118
an `(e2)` invocation. The context type of that entire expression is used
116119
throughout the inference, where `(e1.id<T1>)(e2)` has `(e1.id<T1>)` in a
@@ -119,14 +122,15 @@ inference, it may have something, but a selector context is not a type context,
119122
and it won’t be the context type of the entire expression)._
120123

121124
Because of that, the specification of the static and runtime semantics of the
122-
new constructs need to address all the forms <Code>.*id*</code>,
125+
new constructs needs to address all the forms <code>.*id*</code>,
123126
<code>.*id*\<*typeArgs*\></code>, <code>.*id*(*args*)</code>,
124127
<code>.*id*\<*typeArgs*\>(*args*)</code>, `.new` or <code>.new(*args*)</code>.
125128

126-
_(It also addresses `.new<typeArgs>` and `.new<typeArgs>(args)`, but those will
127-
always be compile-time errors because `.new` denotes a constructor which is not
128-
generic. We do not want this to be treated as `(.new)<typeArgs>(args)` which
129-
creates and calls a generic tear-off of the constructor.)_
129+
_(The proposal also addresses `.new<typeArgs>` and `.new<typeArgs>(args)`, but
130+
those will always be compile-time errors because `.new` denotes a constructor
131+
which is not generic. We do not want this to be treated as
132+
`(.new)<typeArgs>(args)` which creates and calls a generic tear-off of the
133+
constructor.)_
130134

131135
The *general rule* is that any of the expression forms above, starting with
132136
<code>.id</code>, are treated exactly *as if* they were prefixed by a fresh
@@ -144,13 +148,13 @@ context type scheme of the entire, maximal selector chain to the static member
144148
shorthand, and does not change that when recursing on shorter prefixes._
145149

146150
_The effect will be that `.id…` will behave exactly like `T.id…` where `T`
147-
denotes the declaration of the context type.
151+
denotes the declaration of the context type._
148152

149153
**Definition:** If a shorthand context type schema has the form `C` or `C<...>`,
150154
and `C` is a type introduced by the type declaration *D*, then the shorthand
151155
context *denotes the type declaration* *D*. If a shorthand context `S` denotes a
152-
type declaration *D*, then so does a shorthand context `S?`. Otherwise a
153-
shorthand context it does not denote any declaration.
156+
type declaration *D*, then so does a shorthand context `S?`. Otherwise, a
157+
shorthand context does not denote any declaration.
154158

155159
_This effectively derives a *declaration* from the context type scheme of the
156160
surrounding `<postfixExpression>`. It allows a nullable context type to denote
@@ -160,12 +164,10 @@ type to nullable just to allow omitting things ._
160164

161165
**Constant shorthand**: When inferring types for a `const .id(arguments)` or
162166
`const .new(arguments)` with context type schema *C*, let *D* be the declaration
163-
denoted by the shorthand context assigned to the `<staticMemberShorthand>`. It’s
164-
a compile-time error if the shorthand context does not denote a class, mixin,
165-
enum or extension type declaration. Then proceed with type inference as if
166-
`.id`/`.new` was preceded by an identifier denoting the declaration *D*. It’s a
167-
compile-time error if the shorthand context does not denote a class, mixin, enum
168-
or extension type declaration.
167+
denoted by the shorthand context assigned to the `<staticMemberShorthand>`. Then
168+
proceed with type inference as if `.id`/`.new` was preceded by an identifier
169+
denoting the declaration *D*. It’s a compile-time error if the shorthand context
170+
does not denote a class, mixin, enum or extension type declaration.
169171

170172
**Non-constant shorthand**: When inferring types for constructs containing the
171173
non-`const` production, in every place where the current specification specifies
@@ -193,12 +195,13 @@ Doing `List<int> l = .filled(10, 10);` works like doing
193195
`List<int> l = List.filled(10, 10);`, and it is the following downwards
194196
inference with context type `List<int>` that makes it into
195197
`List<int>.filled(10, 10);`. This distinction matters for something like:
198+
196199
```dart
197200
List<String> l = .generate(10, (int i) => i + 1).map((x) => x.toRadixString(16)).toList();
198201
```
199202

200-
which is equivalent to inserting `List` in front of `.filled`, which will then
201-
be inferred as `List<int>`. In most normal use-cases it doesn’t matter, because
203+
which is equivalent to inserting `List` in front of `.generate`, which will then
204+
be inferred as `List<int>`. In most normal use cases it doesn’t matter, because
202205
the context type will fill in the missing type variables, but if the
203206
construction is followed by more selectors, it loses that context type. _It also
204207
means that the meaning of `.id`/`.new` is *always* the same, it doesn’t matter
@@ -218,21 +221,23 @@ FutureOr<int> = .parse("42"); // Context `FutureOr<int>` is structural type.
218221

219222
#### Special case for `==`
220223

221-
For `==`, we special-case a second operand that is an static member shorthand.
224+
For `==`, we special-case when the right operand is a static member shorthand.
222225

223226
If an expression has the form `e1 == e2` or `e1 != e2`, or a pattern has the
224-
form `== e2`, where static type of `e1` is *S1* and the function signature of
225-
`operator ==` of `S1` is <code>*R* Function(*T*)</code>, *then*
226-
before doing type inference of `e2` as part of that expression or pattern:
227-
* If `e2` has the form `<staticMemberShorthand> <selector>*` and
227+
form `== e2`, where the static type of `e1` is *S1* and the function signature
228+
of `operator ==` of `S1` is <code>*R* Function(*T*)</code>, *then* before doing
229+
type inference of `e2` as part of that expression or pattern:
230+
231+
* If `e2` has the form `<staticMemberShorthand> <selector>*` and
228232
<code>*T*</code> is a supertype of `Object`,
233+
229234
* Then assign *T* as the shorthand context of `e2`.
230235

231236
_If the parameter type of the `==` operator of the type of `e1` is,
232-
unexpectedly, a proper subtype of `Object` (so it's declared `covariant`),
233-
it's assumed that that is the kind of object it should be compared to.
234-
Otherwise it's assumed that it's something of the same type,
235-
most likely an enum value._
237+
unexpectedly, a proper subtype of `Object` (so it's declared `covariant`), it's
238+
assumed that that is the kind of object it should be compared to. Otherwise we
239+
assume the right-hand side should have the same type as the left-hand side, most
240+
likely an enum value._
236241

237242
This special-casing is only against an immediate static member shorthand.
238243
It does not change the *context type* of the second operand, so it would not
@@ -246,7 +251,7 @@ Examples of allowed comparisons:
246251

247252
```dart
248253
if (Endian.host == .big) ok!;
249-
if (Endian.host case == .big) ok!
254+
if (Endian.host case == .big) ok!;
250255
```
251256

252257
Not allowed:
@@ -380,7 +385,7 @@ shorthands for any interface type.
380385
It is a conspicuous special-casing to allow `int?` to denote a static namespace,
381386
but it’s special casing of a type that we otherwise special-case all the time.
382387

383-
It allows `int? v = .tryParse(42);` will work. That’s a *pretty good reason*.<br>
388+
It allows `int? v = .tryParse(42);` to work. That’s a *pretty good reason*.
384389
It also allows `int x = .tryParse(input) ?? 0;` to work, which it
385390
wouldn’t otherwise because the context type of `.tryParse(input)` is `int?`.
386391

0 commit comments

Comments
 (0)