Skip to content

Commit 7d9adfb

Browse files
authored
Add a "Concerns" section to "Private Named Parameters". (#4468)
Add a "Concerns" section to "Private Named Parameters". Talk a bit about how the feature might be confusing at first and suggest ways we can mitigate that.
1 parent 4eefade commit 7d9adfb

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

working/2509-private-named-parameters/feature-specification.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ class House {
4242
}
4343
```
4444

45+
Calls to the constructor are unchanged and continue to use the public argument
46+
names:
47+
48+
```dart
49+
House(windows: 5, bedrooms: 3);
50+
```
51+
4552
This proposal harmonizes with (and in a couple of places mentions) the [primary
4653
constructors][] proposal. When combined with that proposal, the above example
4754
becomes:
@@ -268,6 +275,79 @@ class House({
268275
});
269276
```
270277

278+
### Concerns
279+
280+
This proposal makes the language implicitly convert a private named parameter
281+
into the verbose pattern users do today where they declare a public named
282+
parameter and explicitly initialize the private field from it:
283+
284+
```dart
285+
class C {
286+
int? _variable;
287+
288+
C({int? variable}) : _variable = variable;
289+
}
290+
```
291+
292+
While verbose, this code has the advantage of being very clear what's going on.
293+
A reader can see that the argument name they must use at the callsite is
294+
`variable`, the field is named `_variable`, and the latter is initialized from
295+
the former.
296+
297+
Discarding the `_` implicitly may be confusing for users. If a user sees a class
298+
like:
299+
300+
```dart
301+
class C({int? _variable}) {}
302+
```
303+
304+
They may try to call it like `C(_variable: 123)` and be confused that it doesn't
305+
work. There's nothing in the code hinting that the `_` is removed from the
306+
argument name.
307+
308+
In general, we try to design features that are both useful once you know
309+
them and intuitive to learn in the first place. This feature is helpful for
310+
brevity once you know the "trick", but it's opaque when it comes to learning.
311+
This is a real concern with the feature, but I believe the brevity makes it
312+
worth the learnability cost.
313+
314+
We mitigate confusion here in a couple of ways:
315+
316+
#### Only allow the syntax where it's meaningful
317+
318+
At the language level, this proposal only allows `_` in a named parameter when
319+
doing so is *useful and meaningful*. It doesn't allow *any* named parameter to
320+
start with underscore, only a named parameter that declares or initializes a
321+
private instance field.
322+
323+
A private named parameter *looks weird* since privacy makes little sense for an
324+
argument name and makes even less sense for the local parameter variable. (We
325+
already have a lint that warns on using `_` for local variable names since it
326+
accomplishes nothing.)
327+
328+
If a user sees `_` on a parameter and is trying to figure out what's going on,
329+
they will reliably be in a context where that parameter is also referring to a
330+
field. If we're lucky, that may lead them to intuit that the privacy is for the
331+
field, not the parameter.
332+
333+
#### Provide a teaching error if they use `_` for other named parameters
334+
335+
If a user tries to put `_` before a named parameter that *isn't* an initializing
336+
formal or declaring parameter, it's an error. That error message can explain
337+
that it's forbidden *here* but that the syntax can be used to declare or
338+
initialize a private field.
339+
340+
#### Provide a teaching error if they use `_` on the argument name
341+
342+
If a user sees a named parameter with a private name, they may try to call the
343+
constructor with that same private argument name, like `C(_variable: 123)`.
344+
When they do, this is always an error.
345+
346+
The error message for that can explain that the `_` is only used to make the
347+
corresponding field private and that the argument should be the public name. The
348+
first time a user tries to call one of these constructors the wrong way, we can
349+
teach them the feature.
350+
271351
## Static semantics
272352

273353
An identifier is a **private name** if it starts with an underscore (`_`),
@@ -396,8 +476,23 @@ initializing formal with the private name.
396476
Since there's no reason to *not* prefer using an initialing formal in cases
397477
like this, it probably makes sense to have a lint encouraging this as well.
398478

479+
### Good error messages when users misuse this feature
480+
481+
Since this feature likely isn't as intuitive as we hope to be, error messages
482+
are even more important to help users understand what the language is doing and
483+
getting them back on the right path.
484+
485+
The [Concerns][] section suggests two error cases and how good messaging there
486+
can help users learn the feature.
487+
488+
[concerns]: #concerns
489+
399490
## Changelog
400491

492+
### 0.2
493+
494+
- Add section about concerns for learnability and mitigations.
495+
401496
### 0.1
402497

403498
- Initial draft.

0 commit comments

Comments
 (0)