dart-lang / dart-lang/language
Support paired-up private and public variables: "Private var, public final"
- Dominant language
- TeX
- Stars
- 2.9k
- Forks
- 239
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 14
Description
It can be useful to have a variable declaration that allows for mutation in a limited scope (as in "we can read and write this variable, but they can only read it!"). Here is a way to model that concept, such that the current library can read and write the variable (using the private name), but other libraries can only read it (using the corresponding public name):
```dart
int _x = 0;
int get x => _x;
```
This idiom can be used with several kinds of variables (top-level, static, instance), but not with local variables (they can't be accessed from other libraries anyway).
However, there is a connection to formal parameters (which are a kind of local variables). In [this proposal](https://github.com/dart-lang/language/issues/2509#issuecomment-1539012088) we have a named initializing formal parameter of the form `this._x` in a constructor (which will initialize a private variable named `_x`), and we would automatically use the corresponding public name `x` at call sites. So we allow "them" to initialize the variable, but only "we" can access it after initialization. (A named parameter whose name is private is an error today, but with that proposal it would be allowed.)
The fact that we're using a private name and the corresponding public name is not an irritating limitation, it's a feature! This is because we can allow for the syntax to be concise, and developers who have ever encountered this feature will immediately know what's going on, as opposed to the situation where the two names are declared independently. With separate declarations using independently chosen names, developers will need to read both names (more verbosity) and then understand and remember the connection between those two names. So it seems useful to pair up names like `_x` and `x`, and to be able to set up this pairing concisely.
We can use a lexical rule to allow a `_?` to occur at the beginning of a new kind of identifiers, for example `_?x`. The idea is that (just like regular expressions) the `?` hints that the `_` may or may not be there. So `_?x` declares two names together, namely `_x` and `x`. The use of those names follows from the kind of declaration where this name occurs. For example:
```dart
int _?x = 0;
// Means
// int _x = 0;
// int get x => _x;
class A {
late final int _?i;
// late final int _i;
// int get i => _i;
final int _j;
A({this._?j = 10});
// A({int j = 10}): _j = j;
}
class const A({required int _?i});
// class A {
// final int _i;
// const A({required int i}): _i = i;
// }
```
[Here](https://dart-review.googlesource.com/374640) are the required grammar changes:
```ebnf
// Grammar rules.
::= // Updated rule.
('=' )?
::= // Updated rule.
? 'this' '.' ( '?'?)?
::= // Updated rule.
|
// Lexical rules.
::= // Lexical rule, used to be a grammar rule.
'async' | 'augment' | 'base' | 'hide' | 'of' | 'on' | 'sealed' | 'show' | 'sync' | 'when' | 'type'
::= // New rule.
'_?' (IDENTIFIER | OTHER_IDENTIFIER)
```
It should be noted that there cannot be any whitespace around the`?` because `` is a lexer rule, not a grammar rule. This ensures that there is no ambiguity for cases where a `?` is playing another role, and there is some whitespace before or after the `?`.
There may be breakage in cases like `if (0 < _?myBoolean : true) ...` because there is no whitespace around `?` and hence `_?myBoolean` is recognized as a `PRIVATE_PUBLIC_IDENTIFIER`. However, this is probably rare. A search in internal code brought only one single occurrence, which wasn't code (it was a string literal, `r'^_?pubspec\.yaml$'`).
Contributor guide
Research direction
Start by reading the proposed `_?` syntax, examples, and grammar changes in this issue, then inspect the linked proposal and Dart review change. Determine the specification and lexer/parser work required for private-public identifiers and field formal parameters. Done means the proposal is resolved and the grammar precisely defines the supported declarations and whitespace behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- dart
- Domain
- compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100