-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Champion "readonly for locals and parameters" #188
Comments
Any plan for readonly types(classes and structs)? |
should support readonly i = 0; // shorthand for readonly var
const j = 0; // shorthand for const var |
Wasn't |
@jnm2 I just don't like the idea of adding new keyword. Especially it is already have keyword in the language that has the same meaning
At least I have seen some suggestion to use Especially because |
@Thaina Yes, I'm inclined to agree. |
And you could continue to. Like Although I do prefer |
Oh yes! |
@HaloFour It not breaking change I understand but it still ambiguous BTW I still don't like |
|
Personally the latter reason is enough for me. It's already a contextual keyword, and in that existing context it creates a readonly identifier. |
Fair enough 😄 |
Do you mean immutable types? If so, that's a completely separate proposal (I'm pretty sure it's been made before). |
ITNOA @Richiban where I can found it? |
That's a new one! dotnet/roslyn#7626 and https://github.com/dotnet/roslyn/issues/159 are probably what you're looking for. |
Something I like about final String name;
if (entity instanceof Person) {
Person person = (Person)person;
name = person.getFirstName() + " " + person.getLastName();
}
else if (entity instanceof Company) {
Company company = (Company)company;
name = company.getFirmName();
} This can be useful in those scenarios where you want the local to be readonly, the expression to calculate it can throw and you want the scope of the local to exist beyond a |
@Richiban thanks, but I hope to see comprehensive proposal about immutable object in csharplang project. |
@HaloFour Is conditional operator ( ?: ) not sufficient for this purpose? |
Sometimes not. I amended my comment to mention Either way, Java supports this, and I make use of it frequently enough that I think it would be useful here. |
I think simple rule like "All local |
That's yet another use-case for let name = entity match (
case Person person : $"{person.FirstName} {person.LastName}",
case Company company : company.FirmName
); |
We may differ on opinion there. If the expression has to be overly complex in order to satisfy an overly strict language feature that only decreases overall maintainability and readability. I'd rather the flow be logical and the compiler enforce readonly-ness where appropriate. And as a Java programmer who works directly with hundreds of other Java programmers I can say that this has never been a source of confusion. It's a pattern used fairly frequently across the Java ecosystem. If anything I think I would find it much more annoying that I couldn't declare and assign a It's just one exceptionally simple case. |
Something being In fact, there are many cases where performance oriented code explicitly wants to reuse locals to reduce the overhead on the stack or where it allows a reduction in copies or avoids needing complex analysis required to prove that a local is actually readonly (in the context of a complex flow graph) so that the JIT could itself determine that folding two locals into one slot was possible. |
Roc Lang is a functional language which used mutability inference to reuse variable which are immutable at the language level. |
I believe It's important that we choose a keyword that is short to avoid cluttering the code, and that carries sufficient meaning to be understandable even when seeing it for the first time. It would look like this: once int Foo = 10; It might look prettier to put the modifier after the type, even though this breaks the consensus of modifiers going before the type. int once Bar = 10; |
The choice of word isn't the important point here. The issue that is blocking this feature is that the moment readonly locals and parameters are added to the language, it automatically becomes best practice to mark everything as Whether that opinion is a valid one is moot: C# isn't a democratic language. Once the language team have made a decision, it's monumentally difficult for the community to change their collective mind on the matter. |
I would say there are some small benefits to readonly locals and parameters, but imho those do not outweigh the extra noise of a modifier on 98% of all locals. I would only like to have this if you can opt-in per project to have readonly be the default. |
It's for exactly this reason that I really wish we could separate this into two feature requests: one for "readonly locals" and another for "readonly parameters". Although there's obviously some overlap between the two, I expect the resultant change to the C# code people write to be very different in each case. |
@Richiban we would think of this as two separate language changes and feature requests. There's no point in creating multiple issues though as all the discussion has happened here. |
Changing minds doesn't happen through democratic means. Changing minds happens through sound arguments and addressing the concerns brought up by the team. :-)
This topic has been discussed by the ldm numerous times over the years (as we both want to reassess based on new community arguments, as well as to see if things have changed since the last time we looked at things). The statements made here reflect the positions and decisions of the ldm as a whole. Note, an example of how things changed were your points about readonly and primary construction parameters. Which is why we're amenable to making changes there @DavidArno, and that's something we're likely to start up soon this year. |
As one of those people - close but not quite. Rather: "Best practice, in any language, is to make it harder to write bugs." I don't want |
There's practically no scenario where I reassign local - let alone a parameter. I would use I think mirroring the nullable approach is preferable, adding an attribute( I do think attributes is substantially uglier than keywords however - looks like a hack - but I don't see them adding a |
Ultimately I think an analyzer is the only path forward here. That gives you the ability to toggle the behavior of the entire project without having to change any code, and knobs to customize the behavior to your preferences. It's not really possible to compete with how succinct declarations are today (except in specific cases, e.g. |
I would be willing to consider it. Though I agree it would have an uphill battle here. |
I've been coding for nearly 30 years in at least a couple dozen languages. I genuinely don't remember a single case of a bug that would have been prevented by such a feature. That's how rare it seems to be to me. That sort of rarity space is much better served by an analyzer versus a lang feature imo. So, imo, best practice in a language is not to make it harder to write any bugs. Rather, a language should be judicious in the sorts of bugs it is trying to stamp out. |
I think those two statements sum that up nicely. Unlike NRTs (which are analyzers on steroids), this isn't attempting to solve a massive known problem. It's more attempting to enforce a degree of correctness. It's a worthy goal, but does it rise to the level of a dialect? Even I have to admit that it's not. I'd rather nip it in the bud and get to work exploring viable alternatives than drag this conversation out another 7 years. |
IMO, the real benefit here is simply as an instant signal to the code reader: hey, this won't get reassigned, so you can cross off one of a zillion aspects of the code that you do need to pay attention to as you work on understanding it. I agree that an analyzer or analyzer-like approach is basically the only way here. And since the language has not "defaulted to readonly" to date, a lite form of dialect-ization a la NRT will be needed. (At least, I lean against simply allowing the readonly keyword in all these places and making that something "people just want to add on 99% of the time.") Whether that happens "officially in the language and compiler", or outside through a configurable analyzer, possibly with indirect language support such as attributes on local variables, is up for debate. And, as always, whether the feature will happen at all is not known. "No" is not always a permanent state, but it can also be quite a long lasting one, as we've seen. |
Agreed, it does make reasoning about code easier knowing a value is never reassigned. That would be the only real benefit I see of this feature. Rider / Resharper by default underlines all variables that are reassigned in the IDE. This gives exactly the mental help I personally need while reading code to pay more attention to these variables. It does not need to be suppressed with a pragma or attribute, does not clutter code more than necessary. It even helps me read code in large legacy code bases with long complicated methods without needing to update to a new compiler version or add new analysers. I do not know if VS (code) also have such a feature, but maybe if it gets added there will be a lot less demand for this as a language feature. |
@FrankBakkerNl Sadly it does not help on code reviews... unless you use the IDE for this matter. |
The built-in code highlighting doesn’t provide a way to indicate intent. Just because a variable doesn’t change currently doesn’t mean it shouldn’t be reassigned in a future code change. Being able to mark variables as non-reassignable provides protection and ease of mind against future changes to the code-base. When updating a variable to allow reassignment, it is obvious that more prudence is necessary to avoid potential regressions. After all, the original developer went out of their way to mark the variable non-reassignable. For full stack developers working with multiple programming languages, lacking this feature increases dissonance. This is just one more thing which doesn’t have a one-to-one mapping from other modern popular multi-paradigm languages. |
We sometimes have to read code written a long time ago or by others in other than IDEs or VS Code. GitHub does not track variables until we click them. Other similar services do not at all. |
Why not use the in keyword for the parameter as with methods? Doesn't it have the right semantics already? |
The |
2024 here, I need
|
Sorry for the bluntness, but it is insane that between
(2) is preferred. As a C++ developer that uses C# from time to time, not being able to use Let me explicitly mark what the moving parts or non-moving parts of my algorithms and functions are. |
Why not use the same as record class. Some init properties. |
@Strandedpirate, |
There are many in the community that agree with you. However, as the language team at Microsoft like to remind us on occasion, C# is not a democracy. The team do not agree with us and so they do not implement it. I will disagree with you though that it is insane. They do have logical reasons for this position as mutable locals and parameters aren't a significant source of bugs therefore marking them readonly adds to the "noise" and makes the code less readable. Clearly though, mutable locals and parameters do significantly add to the cognitive load when reading code and so also make the code less readable. Which is the worst offender of reducing code readability is - as this thread shows - a huge source of debate. For me, the whole debacle of releasing non-recordPC parameters without readonly support has thrown an interesting curveball into the argument. People have responded by producing analyzers that report modifying PC parameters as a warning (easily promoted to an error). As there doesn't seem to be any meaningful use cases for mutating those parameters, such analysers solve the issue completely. And that therefore could well just be the end of the matter. But not everyone wants to use an analyzer and so the team may remain under pressure to allow those parameters to be explicitly marked as readonly. If they give in to that pressure, what then happens? The I suspect the same will happen when readonly PC parameters are supported: even though it's not technically necessary to mark them readonly, people will, and new analyzers will appear that warn on not specifying them. The reason is simple: specifying that For me, this is the killer argument for readonly locals and parameters. It's not about "sanity" and it's not about "noise". It's simply that explicit intent trumps saving a few characters and the use of Despite this, I'm sure the team will stick to their "absolument pas" response on this topic. And as C# is not a democracy, nothing will change regarding readonly locals and (non PC) paramters... |
The thing I don't understand in the whole discussion about readonly-ness (when it comes to both PC parameters and normal parameters / locals) is that the team's position seems to be "Being able to mark locals and parameters as readonly is a low-value feature". I'm not going to argue with that assessment (although I disagree) but instead I'm going to ask a question in reply: "Why does C# have readonly fields?" Obviously the team decided that the juice was worth the squeeze on that one (with the caveat that it's a very old feature and the LDT might be made up of different people now than it was then). As far as I can tell the arguments both for and against readonly fields are the same as they are for readonly locals, but one was added and one was not? I simply don't understand the reasoning. It could well be that I'm simply missing something here, so I'm happy to be enlightened. |
An important difference between locals and fields is their scope. A field can be (re)assigned by any line of the type, while a local by definition only by the member in which it is defined. For most cases this makes it easier to track what happens to a local than to a field. Therefore I do think readonly for locals has smaller benefit than for fields. Which helps flip the scale in this tradeoff. Ofc now PC parameters kind of blur this distinction. |
Also the timing. Readonly fields have been there from the start, it's not a question of justifying a language modification after the fact. Given the inspiration/influence on the CLR from Java, it's more notable that .NET/C# included |
See also dotnet/roslyn#115
Design review
The text was updated successfully, but these errors were encountered: