-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow setting named private properties #2005
Comments
Although I agree with your reasoning, I find the proposed alternative too much obscure. It's not intuitive to have |
I don't mind the logic. It says that In general, there should be no guarantee that passing |
Further more you can already do class House {
final int _windows;
House(this._windows);
House.alternatively(int windows) : _windows = window;
} So there is somewhat of a parallel that will make the proposal feel intuitive. I can see how they both differ but they are also related |
So, basically, if you use an initializing formal for a privately named field as a named parameter, the name of the parameter, in the function type of the constructor, becomes the name of the field without the leading This is non-breaking and new because you currently cannot have named parameters with private names, so there are no existing occurrences of We'd have to decide whether the final variable the initializing formal introduces into the constructor list should have the private name or the non-private name. Either is fine, we just have to make a choice. We have to decide whether another positional parameter can have the same name as either the private name or the publicized name. (Probably whatever makes it not conflict with the final variable mentioned above, or both). We have to decide how to handle edge cases like I actually also have a problem with positional initializing formals, because they show up in dartdoc. That is:
It does introduce a new concept: The "corresponding non-private name" for a private name, which not all private names have. |
I don't actually agree this is a new concept. Everybody is already doing this, its just not codified in the spec, forcing us to manually spell it out each time :). I have never heard of any other answer to this question other than removing the leading According to the analysis here https://github.com/dart-lang/language/blob/db9f63185707c4c89a69118e842e4cc6e0e59cc3/resources/instance-initialization-analysis.md, >17% of all initializers are of this form ( |
Of course you have, we were just discussing another answer to this question! :) As we were just discussing, we could just... not treat these as private in parameter lists. And then we're done, without any need for renaming schemes. |
(╯°□°)╯︵ ┻━┻ |
What is the rules behind this ? class House {
final int _windows;
House(this._windows);
} It just makes sens to me but I can't really explain a reasoning, it's just sugar for
Isn't it kind of the same ? Quoting the analysis above:
Currently only positional is supported |
From my understanding of the proposal, that'd be correct. It's more annoying when there are multiple named private parameters, but that's the simple version. |
You said would be but that snippet works today. It's not clear from the comment if you knew it or not. |
The snippet works today, but the parameter name is the private identifier With the proposal here, the API docs would say the parameter name is |
I think this is a good idea. It definitely feels weird. If I had a time machine, I would sure as heck get rid of The It's particularly frustrating because it's very obvious what the user wants to happen when they are combined. Fully 17% of field initializers in initializer lists are There is (at least) one silly corner we need to worry about. If we say that class C {
var _a;
var _b;
C(this._a) : _b = a; // <-- Note "a", not "_a".
} Is that what we want? |
It looks like The following is valid today but does it make sens ? Irhn mentioned privacy is leaked in the documentation, which goes against the concept of privacy. It's not really privacy here, it's just the name of the param which happens to have a character reserved for privacy if I understood correctly, but the line is a bit blury and a bit brain busting. class House {
final int _windows;
final int _doors;
House.valid(this._windows): _doors = _windows;
House.invalid(this._windows): _doors = windows;
} If the behavior was the same for positionals, shouldn't valid and invalid be inverted ? (valid becomes invalid and vice versa). Both are imperfect anyway, might as well have one imperfect. If I understand correctly those today are strictly equivalent: class House {
final int _windows;
House.one(this._windows);
House.two(int _windows): _windows = _windows;
} I can't see a good reason to let a character that is also used as an access modifiers at the start of a parameter's name be valid but I'm going on a tangent a bit. It's just that treating it sometimes as a character and sometimes as an access modifier is well, context dependent which is harder on the brain. |
@munificent, @lrhn proposed earlier:
That is, the parameter is passed in as |
Yes, the parameter name and the local variable name of the variable induced by the parameter need not be the same. It would mean that the argument-to-parameter binding step and the parameter-to-local-variable binding step would be separate. Currently the two are conflated, with the "parameter" not really being used for anything (we bind argument values directly to local variables). I think making that distinction is useful in other cases too:
We'd still have to use the public parameter name in DartDoc. |
The fact that the "name" of a positional If we don't remove the leading I do think that coming up with some syntax (like the |
The constant redundancy that class C {
var _a;
var _b;
C(this._a) : _b = a; // <-- Note "a", not "_a".
} Because I suspect this would be used far less in comparison. Beside it's not really inconsistent, if the feature is that it's just sugar for today's equivalent, it makes sens. |
@cedvdb , Dart already has too many inconsistencies, no need to bring more. Also what should happen if I have both - a and _a member? |
This would just be an error, you can't have two parameters with the same name. We already have that error so we shouldn't even need to add anything, although the analyzer/compilers may want a special message to help the user understand where the conflict came from. |
@jakemac53, but _a and a have different names and you can at this moment create both. Aka this code compiles: class Test {
final a = 0;
final _a = 0;
int test() => a + _a;
} So you will need to change compiler and it will potentially break old code. |
The renaming here only happens for private this. constructor parameters. That is still potentially breaking (when it comes to positional parameters) but we can roll it out with language versioning so people don't get "broken" until they opt in. |
Has anything been decided here ? What about an optional class House {
final bool _isBuilt;
House.named({ required bool isBuilt}): _isBuilt = isBuilt;
House.namedWithAs({this._isBuilt as isBuilt}); // suggar for the above
House.namedWithoutAs({this._isBuilt}); // suggar for above
House.namedOther({this._isBuilt as isAlreadyBuilt}); // not sure what the use case would be here
} dart doc would show: [isBuilt] blablabla in all those 3 first cases. Maybe that's less obscure for some people although the result is the same ? |
No decision yet. The language team is mostly focused on views, records, pattern matching, and macros, so other smaller language changes aren't getting much attention right now. That doesn't mean we've forgotten, it just means we try to focus on things in priority order. |
Personally, I don't like the idea. Because I would instead prefer if we could support private optional named parameters. Such that we could do: // file.dart
void fn({int? public, int? _private}) {}
fn(public: 42, _private: 21);
// another.dart
fn(public: 42); // _private cannot be used here |
This seems more idiomatic, in dart's philosophy: void fn({int? public}) => _fn(public: public);
void _fn({int? public, int? private}) {} The alternative proposed would be confusing regarding the different meaning with constructors. SomeConstructor(this._privateMember, { this._someMember }) {}
doSomething({int? _privateParam }) // I assume the |
Having to duplicate the function is exactly the sort of thing I'd want to avoid. In the end, I think private named parameters and the shorthand for initializing private parameters kind of overlap in uses. I wish we could do: abstract class Example {
final int a;
final int _private;
// Allow cloning private vars too
Example copyWith({int? a, int? _private});
} The current best alternative is to do: Example copyWith({int? a, @internal int private}); This is very similar in principle to this issue, where the private parameter had to somehow be made public. |
I'd definitely go with: Example._(this.a, this._private);
Example copyWith({int? a}) => _copyWith(a: a);
Example _copyWith({int? a, int? private}) => Example(a ?? this.a, private ?? _private); here too. I design API for a living, to be used by other people than myself, so I care a lot more than just pragmatically about how an API looks and how it's presented. If it's just your own class, and nobody else is ever going to use it (it's part if your application), you can do whatever you want with the API, it only affects yourself. (And in that case, maybe you wouldn't need to make I would never want a private name to be visible in the public API, because it's noise. A visible I'm sure there is a middle-ground where an API is "public", but not really that important. Where strict separation isn't as necessary, and a pragmatic approach like But it has costs, some not obvious at the declaration point. You cannot implement the class interface in another library, or extend and override the method (say to add a public It can be used to leak a symbol of the private name. (Private symbols are not something you should depend on secrecy of for anything important, but still.) It cannot be mocked, because you can't prepare for a call passing the private parameter, The static type of the method includes the private name (the runtime type obviously does too). Unless we special case private parameters and remove inaccessible optional named parameters from the static type of members, but then it becomes even more weird if you cannot override the method without the parameter. |
I see some people avoiding using private named fields because of the current syntax, when you have more than 5 parameters it is weirdly bulky. I hope they give some attention to this issue in the future! |
I find dart constructors initializer list often hinder readability and needlessly redundant when trying to have private members. They are also sometimes annoying to read in public api scenarios
I would like the ability to populate private members with named parameters without resorting to an initializer list.
What I propose is to have those 2 snippets be equivalent:
Currently:
Suggared:
In both cases you'd call
House.named(windows: 2, bedrooms: 1, swimmingPools: 4);
. The first snippet has needless noise in it.I'm sure this was a design decision but I can't find an issue other than the closed one I posted above about this.
The text was updated successfully, but these errors were encountered: