Skip to content
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

Open
5 tasks
gafter opened this issue Feb 26, 2017 · 780 comments
Open
5 tasks

Champion "readonly for locals and parameters" #188

gafter opened this issue Feb 26, 2017 · 780 comments

Comments

@gafter
Copy link
Contributor

gafter commented Feb 26, 2017

  • Proposal added
  • Discussed in LDM
  • Decision in LDM
  • Finalized (done, rejected, inactive)
  • Spec'ed

See also dotnet/roslyn#115

Design review

@gulshan
Copy link

gulshan commented Mar 23, 2017

Any plan for readonly types(classes and structs)?

@Thaina
Copy link

Thaina commented Mar 27, 2017

should support

readonly i = 0; // shorthand for readonly var
const j = 0; // shorthand for const var

@jnm2
Copy link
Contributor

jnm2 commented Mar 27, 2017

Wasn't val going to be short for readonly var?

@Thaina
Copy link

Thaina commented Mar 28, 2017

@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

readonly might be a bit longer but we already preserved it from the start. We should reuse it. And to be shorter, just let we use readonly without var

At least I have seen some suggestion to use let that would still better than val because we already have it as keyword, even for linq scope

Especially because val was not keyword. I really have my code var val = 0; and I bet there are many people have val as variable or field name in their code like me. I think val is a bad choice

@jnm2
Copy link
Contributor

jnm2 commented Mar 28, 2017

@Thaina Yes, I'm inclined to agree.

@HaloFour
Copy link
Contributor

HaloFour commented Mar 28, 2017

@Thaina

Especially because val was not keyword. I really have my code var val = 0; and I bet there are many people have val as variable or field name in their code like me. I think val is a bad choice

And you could continue to. Like var, val would be a contextual keyword, in that it only behaves like the keyword when it doesn't make sense for it to behave like anything else. So var val = 0; would remain perfectly legal.

Although I do prefer let to val, mostly because I think it looks sufficiently different.

@jnm2
Copy link
Contributor

jnm2 commented Mar 28, 2017

Oh yes! let was the one I liked. Thanks!

@Thaina
Copy link

Thaina commented Mar 28, 2017

@HaloFour It not breaking change I understand but it still ambiguous

BTW I still don't like let. I prefer readonly. But at least let is better than val

@benaadams
Copy link
Member

benaadams commented Mar 28, 2017

let is a bit early basic; also was read write. Not sure how let implies readonly?

@HaloFour
Copy link
Contributor

HaloFour commented Mar 28, 2017

@benaadams

let is a bit early basic; also was read write. Not sure how let implies readonly?

let is also F# where it is the readonly (by default) binding of an identifier. let is also C# LINQ where it is the declaration of a readonly range variable.

Personally the latter reason is enough for me. It's already a contextual keyword, and in that existing context it creates a readonly identifier.

@benaadams
Copy link
Member

benaadams commented Mar 28, 2017

let is also C# LINQ where it is the declaration of a readonly range variable.

Fair enough 😄

@Richiban
Copy link

Richiban commented Mar 30, 2017

@gulshan

Any plan for readonly types(classes and structs)?

Do you mean immutable types? If so, that's a completely separate proposal (I'm pretty sure it's been made before).

@soroshsabz
Copy link

soroshsabz commented Mar 30, 2017

ITNOA

@Richiban where I can found it?

@Richiban
Copy link

Richiban commented Mar 30, 2017

@soroshsabz

ITNOA

That's a new one!

dotnet/roslyn#7626 and https://github.com/dotnet/roslyn/issues/159 are probably what you're looking for.

@HaloFour
Copy link
Contributor

HaloFour commented Mar 30, 2017

Something I like about final locals in Java is that it's possible to declare one as not assigned and then have branching logic to assign it. The Java compiler uses flow analysis to ensure that for each branch that the local is assigned exactly once.

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 try block.

@soroshsabz
Copy link

soroshsabz commented Mar 30, 2017

@Richiban thanks, but I hope to see comprehensive proposal about immutable object in csharplang project.

@soroshsabz
Copy link

soroshsabz commented Mar 30, 2017

@HaloFour Is conditional operator ( ?: ) not sufficient for this purpose?

@HaloFour
Copy link
Contributor

HaloFour commented Mar 30, 2017

@soroshsabz

Sometimes not. I amended my comment to mention try/catch scenarios where C# offers no single expression. You could extract the logic to a separate function but that's more verbose ceremony. Even if it could be expressed as a single conditional expression sometimes it's more readable expanded out into multiple statements.

Either way, Java supports this, and I make use of it frequently enough that I think it would be useful here.

@soroshsabz
Copy link

soroshsabz commented Mar 30, 2017

I think simple rule like "All local readonly variables must be initializing immediately after declaration." cause to improve simplicity and readability for programmers. In Java case some programmers maybe surprise to final variable has not initialize and must be track code to find the where is this variable initialize?

@DavidArno
Copy link

DavidArno commented Mar 30, 2017

@HaloFour,

That's yet another use-case for match:

let name = entity match (
    case Person person : $"{person.FirstName} {person.LastName}",
    case Company company : company.FirmName
);

@HaloFour
Copy link
Contributor

HaloFour commented Mar 30, 2017

@soroshsabz

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 readonly variable like this.

@DavidArno

It's just one exceptionally simple case. match won't handle the exception handling scenario. And again, forcing the developer to try to pack it all into a one-liner, or to extract that local logic elsewhere, does not improve the readability or maintainability of the program.

@Xyncgas

This comment was marked as off-topic.

@CyrusNajmabadi
Copy link
Contributor

CyrusNajmabadi commented Jul 3, 2022

It would be extremely beneficial for code organizing purpose if we can have these static local

@Xyncgas this is the issue for readonly-locals. If you want to discuss static-locals you can do so over at #832

@cordasfilip
Copy link

cordasfilip commented Jul 5, 2022

@TahirAhmadov The more I think about it the the more I don't like this feature because you will just end up with some people using it and some not using it. That will lead to analyzers being used to detect this and style enforcing analyzers are just annoying. This will only be useful if there will be benefits to using it and I feel like it's not that hard to detect if the variable is mutable for any optimization that will give you benefits needed. This will probably be messy the same way it's in JavaScript.

@TahirAhmadov
Copy link

TahirAhmadov commented Jul 5, 2022

The more I think about it the the more I don't like this feature because you will just end up with some people using it and some not using it. That will lead to analyzers being used to detect this and style enforcing analyzers are just annoying. This will only be useful if there will be benefits to using it and I feel like it's not that hard to detect if the variable is mutable for any optimization that will give you benefits needed. This will probably be messy the same way it's in JavaScript.

It is usually the choice of the team whether and which analyzers are used; nobody is forced to use them.
The chief motivation is code readability and bug reduction here, not optimization: you communicate the intent of "this local/param is supposed to be readonly"; in a large method, this comes in handy even for yourself, whereas regardless of method size, it's helpful when you have a team and people know what their mates meant.
I do hear your concern about the potential split in style, but that concern applies to many other language changes, and it hasn't stopped those changes. Also, the same analyzers who will warn about locals not being marked readonly, can have a quick fixer in it, making adoption easier for the ecosystem.
Thanks for your input.

@lucasteles
Copy link

lucasteles commented Jul 15, 2022

I would still with let, easy to write, same size as var, is familiar (used in other languages for immutable values like in F#), and is a keyword that already exists in the language

@cordasfilip
Copy link

cordasfilip commented Jul 15, 2022

@lucasteles Problem is no one uses F#, and two most popular languages TypeScript and JavaScript use let for locally mutable values, and most c# developers most likely use the two with C#. Maybe introduce a shorthand syntax like #var to denote immutable values. this would work with explicate types like #string? and class definitions as well.

@HaloFour
Copy link
Contributor

HaloFour commented Jul 15, 2022

@cordasfilip

Problem is no one uses F#, and two most popular languages TypeScript and JavaScript use let for locally mutable values, and most c# developers most likely use the two with C#.

C# already uses let for locally scoped range variables which are effectively readonly as they cannot be reassigned.

Maybe introduce a shorthand syntax like #var to denote immutable values. this would work with explicate types like #string? and class definitions as well.

This is a syntax that would be very foreign for C#. I personally don't think it's a good idea to harvest syntax ideas from Javascript.

@cordasfilip
Copy link

cordasfilip commented Jul 15, 2022

C# already uses let for locally scoped range variables which are effectively readonly as they cannot be reassigned.

No one uses real LINQ as well most people use method syntax.

This is a syntax that would be very foreign for C#. I personally don't think it's a good idea to harvest syntax ideas from Javascript.

TypeScript is probably the best language after C# and C# should use as much as it can from it. They already made a huge mistake by introducing F# like with keyboard rather than the awesome spread operator from TypeScript. But I agree that it would be foreign to C# but still better then let as it would confuse 90% people that actually use C# since they are using TypeScript with C#.

@lucasteles
Copy link

lucasteles commented Jul 15, 2022

@lucasteles Problem is no one uses F#

That's not true, it is smaller for sure, but I and a considerable number of developers use it.
but. other languages use let for Immutable locals too [ OCaml, Swift, Rust, Clojure, Haskell... etc...]
I cited F# because it is another language on .NET ecosystem which treats already treat immutable value locals

TypeScript and JavaScript use let for locally mutable values

True, but it was a collateral effect for already defined var which became a "bad" way to define variables and was need to be replaced, the majority of other languages treat var for variables, and another keyword for constants, we do not have this problem, because of that I don't believe they are good candidates to use as expiration for this

Maybe introduce a shorthand syntax like #var to denote immutable values.

this is not uniform with anything we have now, is longer and ugly than var, so would not be encouraged to be used, this kind of feature is better when is simpler or have the same complexity as the mutable. with less cognitive load and can be used to clearly define when a local is intended to change

and like @HaloFour mentioned

C# already uses let for locally scoped range variables which are effectively readonly as they cannot be reassigned.

Wich put let as the preferred way to go with, in my opinion

@HaloFour
Copy link
Contributor

HaloFour commented Jul 15, 2022

@cordasfilip

No one uses real LINQ as well most people use method syntax.

The team has done studies about this and found that it's split about 50%/50%. I personally use both, but when it comes to projecting range variables I most definitely use the let clause and prefer that over the Select equivalent.

@cordasfilip
Copy link

cordasfilip commented Jul 15, 2022

@HaloFour I personally use it a lot when I can, but usually I find that people use the method syntax but that is probably just my personal experience. Could be just me again but I also have the overall impression that the real linq syntax was abandoned by the language team since no new features have been introduced since it was added to the language. It still lacks even the basic features link skip, take, aggregate... So it's strange to me that you would use it to go against the most popular language in the world.

@jnm2
Copy link
Contributor

jnm2 commented Jul 15, 2022

No one uses real LINQ as well most people use method syntax.

In cases where you would use let, method syntax is particularly terrible to read and even worse to update.

The readonlyness of variables introduced by let seems to me to not be bound to the keyword let but rather bound to the fact that the variable is introduced in a LINQ clause. from and group introduce new readonly variables too. LINQ doesn't make sense any other way. So if let appeared outside of a LINQ expression, I'd have no expectations on whether it meant 'readonly' or not. This is after having used it somewhat regularly with LINQ for over ten years. If so, cool. If not, cool.

@HaloFour
Copy link
Contributor

HaloFour commented Jul 15, 2022

@jnm2

The readonlyness of variables introduced by let seems to me to not be bound to the keyword let but rather bound to the fact that the variable is introduced in a LINQ clause.

Agreed, but it's still a side-effect of the use of the feature, and let probably makes more sense here than foreach. 😄

@cordasfilip
Copy link

cordasfilip commented Jul 15, 2022

@lucasteles the languages mentioned are fringe as well except maybe Rust but even Rust isn't that popular, and all of them are immutable first c# is not and shouldn't be.

this is not uniform with anything we have now, is longer and ugly than var, so would not be encouraged to be used, this kind of feature is better when is simpler or have the same complexity as the mutable. with less cognitive load and can be used to clearly define when a local is intended to change

As I said # would be a shorthand for readonly var/string, the readonly should still be available. As to ugly and 'cognitive load' it's a matter of opinion. it could be $var or var$ like in Excel or -var or `var I am not really fixed on the symbol. Important thing is that it works with explicate types as well and that it could be used in classes as well.

@lucasteles
Copy link

lucasteles commented Jul 16, 2022

The readonlyness of variabls introduced by let seems to me to not be bound to the keyword let but rather bound to the fact that the variable is introduced in a LINQ clause.

Agree with this, let keyword does not have intrinsic relation with immutability, is just a very common way to bind a label to a value, exactly what we need here. It's even better because it is an existing keyword with very close meaning and restricted scope

const is not good for this, it means other thing, readonly var is too long, and would make this impractical (same as in instead of readonly ref in parameters)

So, let looks practical and the best candidate at this moment. Regardless of comparing with other languages and personal preferences

@WaiYanMyintMo
Copy link

WaiYanMyintMo commented Jul 17, 2022

We have readonly fields already and this would make it consistent in syntax and behavior.
It is not too long, and it is easier to spot if a variable is readonly or not.

@GitClickOk
Copy link

GitClickOk commented Jul 18, 2022

I came here to echo this comment:
"The experience of writing javascript using const made me look for this in c#. I've always been missing c# features when writing javascript. This is the first time I've been missing javascript features in c#."

I understand your arguments about functional languages and Linq using "let" keyword, but...

  1. It is far more natural to use const keyword instead;
  2. Sadly, and I repeat: sadly Javascript is the most used language in the world. And most C# developers will do cross development, using C# in the back-end and javascript in the front-end, then it is most natural to find similarities with JS than with functional languages;
  3. Instead of expanding the usage of "let" from Linq to everywhere, can you consider instead adding "const" keyword support in Linq as an alternative to "let"?

@HaloFour
Copy link
Contributor

HaloFour commented Jul 18, 2022

@GitClickOk

  1. Instead of expanding the usage of "let" from Linq to everywhere, can you consider instead adding "const" keyword support in Linq as an alternative to "let"?

C# already has const locals, and they work very differently (as all constants do in C#). That's not to say it would be impossible to reconsider how const works in C#, but it would create a number of inconsistencies within the language and potentially lead to breaking changes if currently legal code is interpreted in a slightly different manner.

@CyrusNajmabadi
Copy link
Contributor

CyrusNajmabadi commented Jul 18, 2022

That's not to say it wouldn't be impossible to reconsider how const works in C#, but it would create a number of inconsistencies within the language and potentially lead to breaking changes if currently legal code is interpreted in a slightly different manner.

I would say that this is such an issue as to make it a complete deal breaker. C# has had 'const' since the beginning to mean something very important, with very specific semantics. Suddenly upending that after 25 years is extremely unlikely to happen.

@zezba9000
Copy link

zezba9000 commented Jul 18, 2022

@GitClickOk

  1. Instead of expanding the usage of "let" from Linq to everywhere, can you consider instead adding "const" keyword support in Linq as an alternative to "let"?

C# already has const locals, and they work very differently (as all constants do in C#). That's not to say it would be impossible to reconsider how const works in C#, but it would create a number of inconsistencies within the language and potentially lead to breaking changes if currently legal code is interpreted in a slightly different manner.

'const' in C# has only ever meant something at compile time correct. Which is far better concept than what C/C++ use it for IMO. 'let' or 'readonly' make far more sense as they imply actual memory unique to that instance is being used but are just immutable. They are very different concepts & have different performance overhead.

@GitClickOk
Copy link

GitClickOk commented Jul 18, 2022

These are fair points. I hope this be released soon, it looks like a small addition, but it will surely be the default way to declare anything unless there is a very good reason to use var instead.

@crfrolik
Copy link

crfrolik commented Jul 18, 2022

That's not to say it wouldn't be impossible to reconsider how const works in C#, but it would create a number of inconsistencies within the language and potentially lead to breaking changes if currently legal code is interpreted in a slightly different manner.

I would say that this is such an issue as to make it a complete deal breaker. C# has had 'const' since the beginning to mean something very important, with very specific semantics. Suddenly upending that after 25 years is extremely unlikely to happen.

It's probably already been discussed in this issue, but it seems like it would be technically feasible to have both const int x = 5 to mean compile-time constants, and const int x = <some-expression> or const var x = <some-expression> to mean readonly locals. The compiler should be able to differentiate between those two, despite the same keyword being used in both places, no? And this wouldn't be a breaking change for existing codebases?

@TahirAhmadov
Copy link

TahirAhmadov commented Jul 18, 2022

it seems like it would be technically feasible to have both const int x = 5 to mean compile-time constants, and const int x = or const var x = to mean readonly locals.

Perhaps, but the technical possibility is not the main problem; it's the overall consistency of the language and readability of the code. Having const mean different things creates a divergence where one is not needed at all. Between readonly and let, there are other keywords in C# which can be used for "readonly locals", there is no need to use const which means something starkly different. With all due respect to JavaScript, it should not serve as a guideline for language development; for all of it's flexibility, it's a script language with many compromises.
Finally, if/when C# gets deterministic functions and we can assign to const the results of expressions which include function calls, making const mean "readonly" now would make that very difficult.

@CyrusNajmabadi
Copy link
Contributor

CyrusNajmabadi commented Jul 18, 2022

It's probably already been discussed in this issue, but it seems like it would be technically feasible to have both const int x = 5 to mean compile-time constants, and const int x = or const var x = to mean readonly locals.

That is what i mean by 'upending'. The expectation people would have after 25 years is that const int means constant int. Having it now mean "but sometimes it's actually readonly" is highly problematic. Consider, most basically, that in some locations only constant are allowed. Users would now be in the position of being told "hey... your const int can't be used here because it's not a constant!". That would certainly be a big WTF issue for users.

@crfrolik
Copy link

crfrolik commented Jul 18, 2022

That is what i mean by 'upending'. The expectation people would have after 25 years is that const int means constant int. Having it now mean "but sometimes it's actually readonly" is highly problematic. Consider, most basically, that in some locations only constant are allowed. Users would now be in the position of being told "hey... your const int can't be used here because it's not a constant!". That would certainly be a big WTF issue for users.

Sure. I was thinking that as a user, I don't actually care about whether it's a compile-time constant or a readonly local. I just want to tell the compiler "this is a value that can't/won't change", and let the compiler figure out which of those two scenarios it actually falls under. I suppose your example scenario of places where you can only use compile-time constants breaks that sufficiently.

@CyrusNajmabadi
Copy link
Contributor

CyrusNajmabadi commented Jul 18, 2022

Sure. I was thinking that as a user, I don't actually care about whether it's a compile-time constant or a readonly local. I just want to tell the compiler "this is a value that can't/won't change",

But that's the point :) 'const' today means that it can't change. But you are suggesting we now give it JS semantics, where it can change. Note: constant and read only are very very very different things.

@tldzyx
Copy link

tldzyx commented Jul 31, 2022

public double add(in double x, in double y)
{
    // some code ......
    readonly double z = x + y;
    // some code ......
}

readonly double z let me sure the z will set value in here, and not care set in other way, without readonly, I have to read add code in the method to be sure it, So I agree with readonly double z = x + y; in csharp like final double z = x + y; in java.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests