Disclaimer: This is particularly related to #16183 as the syntax has some overlaps with previously proposed pattern-matching statement (#6400). Also, I think if this is decided now, it might help if we prevent some syntactical forms to retain backward compatibility in the future (#13148).
Considering how common it will be to define read-only locals, readonly
is too verbose to be used as a modifier on local variables (#115). Instead, I suggest we use let
as the read-only counterpart of var
:
var x = e;
let x = e;
ref var x = e;
ref let x = e;
var (x, y) = e;
let (x, y) = e;
(var x, var y) = e;
(let x, let y) = e;
M(out var (x, y));
M(out let (x, y));
foreach(ref var x in arr)
foreach(ref let x in arr)
e is var x
e is let x
case var x:
case let x:
(x, y) => {};
(let x, let y) => {};
((x, y)) => {};
(let (x, y)) => {};
Some statements like foreach
and using
define read-only variables by default. That could be the case for an analyzer to suggest to use let
in those places to make that clear, visually.
However, parameters should use readonly
just like fields, as they are more high-profile than locals.
readonly object field;
void M(readonly object parameter) {}
Note, unlike var
, let
is able to be used as a modifier on explicitly-typed locals,
let X x = e;
Notice that this is ambiguous with pattern-matching statement (#6400). See #16183 for more on that.
On the whole, I support this proposal but, I have a few reservations.
However, parameters should use readonly just like fields, as they are more high-profile than locals.
I disagree. From the callers point of view, readonly
should not even be visible. It is just noise. If the callee wants to annotate their parameters to avoid accidental re-assignment this should not be surfaced in the interface at all. The semantics are nothing like fields.
I suspect that the desire to use readonly
for parameters, giving them a different notation and increasing the complexity of the mental model, is simply that
C#
void Log(let string x) => Console.WriteLine(x);
just looks stupid.
I want to make a final plea for val
over let
.
Please see my comment in the original issue https://github.com/dotnet/roslyn/issues/115#issuecomment-73829314
It works very well in both Scala and Kotlin, fits well into C#, and it reads much more naturally, especially in the context of existing language constructs.
Regardless, I want to reiterate that whatever the token is, it should not be surfaced to callers as it is the definition of an implementation detail.
From the callers point of view, readonly should not even be visible
I'm confused. This has nothing to do with callers, whatsoever.
I want to make a final plea for val over let.
We already have a let
in query expressions.. I'm indifference towards either keyword, as long as it's not readonly
. (edit: subsequent comments were making a good argument btw).
I'm confused. This has nothing to do with callers, whatsoever.
Indeed that was the point I was making. You say that parameters are more high profile and should use the readonly
keyword. Since the readonlyness of parameters doesn't impact callers I don't see how parameters would be more high-profile.
In the past I was in favour of let
mostly because the concerned that @HaloFour raised when it comes to scanning the code and because it's already used in the language but I'm leaning toward val
because the points raised by @aluanhaddad.
so basically c++ const ?
im afraid i dont see the need for this feature in c#.. especially considering let is a used keyword in linq expressions
@timopomer Can you elaborate? why do you think we don't need this in C#? we already have readonly
fields so why not parameters and locals? I fail to understand how LINQ has anything to do with this decision.
_p.s. This will probably make the cut for the next version of C# this post is more about the syntax._
@timopomer
especially considering let is a used keyword in linq expressions
That's why I prefer let
for a readonly local shorthand. It mirrors the existing semantics of that keyword in LINQ queries, which is the declaration of an implicitly-typed readonly range variable.
My preference is still let
over val
not only for readability reasons but also because it's already a contextual keyword used in an almost identical manner. I don't see why there should be yet another keyword that does a nearly identical thing. In my opinion scanning through Scala code it is quite easy to miss val
.
As for anything explicitly-typed such as parameters I think readonly
is fine. Java uses the same modifier for readonly fields, parameters and locals and that works well enough.
As for anything explicitly-typed such as parameters I think readonly is fine.
Then if you want to make a variable read-only, it would depend on whether it is explicitly-typed or not, and you may need to change the modifier. Also readonly
is too verbose to be present, possibly repeatedly, in type patterns, etc.
@eyalsk
I want to know the case for let, in c++ its thread safety, compiler optimizations and compiler based documentation. but what is it here?
Also does it need its own keyword? wouldnt an implementation like Immutable<T>
be enough? what with generic functions that take variables as references and change them? would it look like this?
void func<T>(T param1) where T : let
And what about unsafe code?
This would require more than just a compiler change, the JIT would need to be modified to make this work if im not wrong
and what if a variable is once passed as let and once normally and the normal variable is changed. do we get an exception?
Is it worth spending time on? i dont know. im just creating questions here that need answering
@HaloFour
My preference is still let over val not only for readability reasons but also because it's already a contextual keyword used in an almost identical manner.
In try/catch
block and switch
statement they added when
where they could reuse where
, why? _it does the same thing._
I don't see why there should be yet another keyword that does a nearly identical thing.
Because it looks better in all places and more _consistent_ with var
.
In my opinion scanning through Scala code it is quite easy to miss val.
IDE can improve the experience by providing a different color, I _think_ that this would be sufficient.
As for anything explicitly-typed such as parameters I think readonly is fine. Java uses the same modifier for readonly fields, parameters and locals and that works well enough.
Why we need to be as verbose as Java? when we have opportunities to make it better?
@eyalsk
Because it looks better in all places and more consistent with
var
.
We'll have to agree to disagree there. I don't think that val
looks better than let
, and I don't think the fact that val
and var
happen to share some letters makes them any more consistent with each other.
IDE can improve the experience by providing a different color, I think that this would be sufficient.
I have to scan code in git repos online frequently enough for me to have a strong opinion against that languages that rely on IDE syntax highlighting/coloring to be legible.
Why we need to be as verbose as Java? when we have opportunities to make it better?
Same number of tokens. A couple of extra characters. The argument of verbosity is so thin there that I don't think that it applies. Are you also suggesting that val
be used for readonly fields?
@timopomer
The readonly
parameter/local proposal doesn't intend to take things as far as C++ const
. It would only prevent reassignment of the variable. It would not prevent the callee from mutating a reference:
void Foo(readonly MyClass obj) {
obj.Value = "12345"; // legal
obj = new MyClass(); // not legal
}
The feature exists specifically to allow a function author to prevent accidentally overwriting variables. It doesn't have any effect on callers.
@HaloFour
In that case all this should be is a sign that you cannot assign anything to the let? as if the "=" operator would be overloaded and gave a compiler error when attempted to be used?
I guess thats ok, but i dont think it would give us any form of optimization
i dont think it would give us any form of optimization
@timopomer It does not have anything to do with optimizations, specifically that Roslyn generally do not do optimizations and it is usually deferred until runtime.
@timopomer
I want to know the case for let, in c++ its thread safety, compiler optimizations and compiler based documentation. but what is it here?
You're wrong the C++11 specification does not say anything about const
being thread-safe, however, the STL specification does.
So if you're consuming something from the STL you can have _some_ guarantees about thread-safety given that your own objects satisfy the requirements.
_You can watch the video by Herb Sutter to verify this_
In C# readonly
means that you simply put cannot reassign a value to a readonly
variable unless you do that from the constructor.
Also does it need its own keyword? wouldnt an implementation like Immutable
be enough? what with generic functions that take variables as references and change them? would it look like this?
I don't know what Immutable<T>
would mean because this could only guarantee that the wrapped value isn't mutated but the variable that holds the instance of Immutable<T>
itself can still be mutated so this would be _useless_.
And what about unsafe code?
Don't know, don't care, this is something that the design team need to decide on.
This would require more than just a compiler change, the JIT would need to be modified to make this work if im not wrong
Again, we're not speaking about implementation here so whatever it takes.
@HaloFour
We'll have to agree to disagree there. I don't think that val looks better than let, and I don't think the fact that val and var happen to share some letters makes them any more consistent with each other.
The _consistency_ doesn't lie in their shared letters but in the fact that they _complement_ one another and by that I mean one represents a _variable_ that can change and the other represents a _value_ that cannot change.
Now, I agree that this isn't a reason to _tipped the scales_, I'm just stating my opinion here.
I have to scan code in git repos online frequently enough for me to have the strong opinion against that languages that rely on IDE syntax highlighting/coloring to be legible.
Fair point, in fact, I think I wrote this exact reason back then but dunno I changed my mind and the more I think about it I just like val
better.
Same number of tokens. A couple of extra characters. The argument of verbosity is so thin there that I don't think that it applies. Are you also suggesting that val be used for readonly fields?
No, I don't suggest that val
would be used for fields and I agree that the argument of verbosity _doesn't hold much water_ but I dunno I just think that if we can make it terser and still clear then why not?
@eyalsk
I don't know that the argument still stands but it had been mentioned in comments earlier that var
makes no implications regarding the mutability of the variable. Whether let
or val
it would just be shorthand for readonly var
which would be shorthand for readonly <type>
.
Given the IDE argument, you'd be typing the same amount of characters to add either val
or readonly
modifier. It's literally f<tab>
in IntelliJ for Java today.
Either way, that's also just my opinion, and it's not that strong of an opinion. Certainly not my hill to die on. I think it's wise to start with readonly
as a general modifier and then work on whatever shorthand would be reasonable.
@HaloFour Why do you think we should start with something that requires a shorthand in the first place?
@alrz Same reason preoptimization is bad.
@HaloFour
Either way, that's also just my opinion, and it's not that strong of an opinion. Certainly not my hill to die on. I think it's wise to start with readonly as a general modifier and then work on whatever shorthand would be reasonable.
You convinced me with this! Sorry I'm torn! 馃槅
@HaloFour
It pushes more complexity to the syntax and the compiler rather than simplifying things. It doesn't really make sense to me. It's mentioned before that anonymous methods were a mistake. But that was when the actual use cases were not identified. This is just like that except now we know it will be very common to use read-only locals.
@alrz
Readability is significantly more important than either syntax or compiler complexity. Humans have to read and parse that code too. I'm also of the opinion that readonly locals will end up being relatively rare. A tool to make functional developers feel more at home at best. Java only required it to make closures simpler but as of 8 they made the keyword optional.
So you intend for this proposal to supplant #115 rather than augment it?
@HaloFour
The reason I'm so torn on this is because I think that what you propose make sense for the _short-term_ but not the _long-term_ if in the _long-term_ it's going to be viral enough to justify a syntactic sugar and we're going to choose either let
or val
then it would be a matter of _style_ and I don't think we need to introduce such a choice into the language.
@HaloFour
I'm also of the opinion that readonly locals will end up being relatively rare.
I disagree. you should always use read-only locals unless they are not read-only. Actually it will be a good candidate for an analyzer to suggest to make locals read-only if they are not assigned to, exactly because humans have to read and parse that code. It's a good practise in JS too that you use let
or const
for variables unless you need mutability.
So you intend for this proposal to supplant #115 rather than augment it?
I made this proposal considering the overlap it entails with pattern-matching statement (#6400). See the disclaimer at the beginning of the OP.
@eyalsk
What does "short-term" or "long-term" have to do with anything? var
doesn't completely replace local declarations. There are times where you want to be explicit, either because you want the variable to be a different type or because you simply dislike implicit typing (there have been several proposals to this repo to allow for banning the use of var
, and that's supported as a style.) I don't think that the more explicit form is irrelevant, and I don't think it's necessary to have completely different explicit forms for fields vs. locals vs. parameters.
@alrz
I disagree. you should always use read-only locals unless they are not read-only.
Indeed, a mindset of functional programming. I even agree with it, for the most part. But I don't think that's particularly relevant. When it comes to adoption existing C# programmers aren't likely to reach for something new that doesn't buy them some obvious benefits that makes their immediate lives easier. This won't reduce the amount of code that anyone needs to type and the argument that it makes code more maintainable is an abstract one.
I made this proposal considering the overlap it entails with pattern-matching statement
The #6400 proposal is dead in the water. It relied on pattern variables being immutable which is no longer the case. Use of it with readonly locals was also only a form of shorthand for let var x = y;
. I expect at this point that #115 would be closer to consideration than something that depends on the now-defunct pattern matching behavior. I'm not at all opposed to a form of readonly pattern variable, though, to complement var
.
@HaloFour
The #6400 proposal is dead in the water.
Unless it is just moved to pattern-matching spec draft?
It relied on pattern variables being immutable which is no longer the case.
The form let x
as the shorthand for let var x
relies on pattern variable being immutable. But the use cases remain intact. Actually with #16183 and #16182 you can have the best of both worlds.
I expect at this point that #115 would be closer to consideration than something that depends on the now-defunct pattern matching behavior.
The two proposal are irrelevant to each other (pattern-matching and readonly locals). But they can be used together e.g. case Point { X: let x, Y: 0 } = e else return
.
Indeed, a mindset of functional programming.
How is that a mindset of functional programming and from when it is a taboo to adopt functional programming's good practice?
something new that doesn't buy them some obvious benefits that makes their immediate lives easier.
That was your own argument, because humans have to read that code? You claim verbosity is irrelevant to the readability but also using a read-only local in the first place is irrelevant too?
This won't reduce the amount of code that anyone needs to type
It does if you are willing to use read-only locals. At this point I'm positive your password is leaked.
@alrz
That proposal died when the scoping rules and mutability of pattern variables changed. The spec hasn't been changed to reflect that nuclear bomb but I expect what is proposed for C# 8.0 will barely resemble it.
I make the argument that any and all code should be easily readable, based on it's grammar. That has nothing to do with my assertion that readonly locals will find limited adoption amongst the C# audience. This isn't because readonly locals are bad or that its somehow wrong to follow functional practices. I just don't think most C# programmers will care and I don't think you'll be able to convince . I think that the changes to the pattern matching spec regarding the mutability of pattern variables reflects this reality.
@HaloFour
What does "short-term" or "long-term" have to do with anything?
What I meant by _short-term_ and _long-term_ is if after some time readonly var
is going to be viral and then a syntactic sugar _might_ get introduced in the form of val
or let
it would be just a matter of _style_ and nothing more.
var doesn't completely replace local declarations. There are times where you want to be explicit, either because you want the variable to be a different type or because you simply dislike implicit typing
I wasn't speaking about readonly <type>
but readonly var
! I think that an explicit choice should be a decision that the developer makes and not something that is decided by the language.
So to make it crystal clear what I meant is as follow:
readonly int x = 1; // C# X
val x = 1; // C# X
Introducing val early as opposed to having it in a future version.
readonly int x = 1; // C# X
readonly var x = 1; // C# X
val x = 1; // C# X+1: syntactic sugar to readonly var
We are not contemplating any additional feature work for C# 7.
@gafter This is not a proposal for C# 7, although I think there will be a compat-issue similar to #13148. If read-only locals are planned for a point release, (per https://github.com/dotnet/roslyn/issues/16155#issuecomment-269801946) wouldn't it make sense to settle that at this point?
@alrz There are currently no such plans (for read-only locals or readonly refs) for a point release. We have a longish list of things we might or might not do in the future. readonly refs has been tentatively categorized by the @dotnet/ldm to be _considered_ for possible inclusion in the timeframe of C# 8 (i.e. not a point release). I suspect @jaredpar was hinting that he has other ideas for prioritizing that work that may change when we consider them.
That feature hasn't been designed yet; it is an open question whether the relevant keyword would be readonly
, let
, val
, or some combination of them. Absent that design work, it would be premature to make changes in C# 7 responsive to that. It is also very very late in the C# 7 schedule, so making any changes is risky, if even possible.
I do not agree with comments on that thread that a ref
foreach variable would necessarily need to be a ref readonly
. All ref
variables are readonly
today in the sense that the ref
cannot be reassigned to refer to something other than what it initially referred to. However, ref
variables can (today) always be used to reassign the thing they refer to, and I think that would be a reasonable design point for a foreach
ref
feature, if we ever did such a thing.
@eyalsk
I agree. I'd like to see let
(or val
) implemented alongside and at the same time as any general readonly
modifier. It would mean the same thing as readonly var
.
If read-only locals are planned for a point release, (per #16155 (comment)) wouldn't it make sense to settle that at this point?
In that comment I was referring to readonly ref
being a candidate for a point release off of C# 7. It's definitely not firmly planned yet, nor did it include readonly
locals in general. That's a different feature than readonly ref
.
@gafter,
We have a longish list of things we might or might not do in the future. readonly refs has been tentatively categorized by the @dotnet/ldm to be considered for possible inclusion in the timeframe of C# 8 (i.e. not a point release).
Could this list be published please and maintained in the open, so we all have up to date info on the team's thinking on this matter?
@DavidArno I'll ask around about that.
@gafter
How much does that list align with the proposals on this repo? I don't see one specifically for "readonly ref". There are already more than 50 issues with the "Ready" tag and well over 400 others queued up behind that. It'd be nice to know that there is some motion behind those proposals rather than them being semi-permanently stuck in limbo.
Well, this list might be a good start: lots of pattern matching, records/ADTs, but nothing about immutability or non-nullability. I wish to know if they are still considered for vNext as well.
@orthoxerox I think this one is more reliable, however, it doesn't look comprehensive or longish.
Most helpful comment
@eyalsk
I agree. I'd like to see
let
(orval
) implemented alongside and at the same time as any generalreadonly
modifier. It would mean the same thing asreadonly var
.