Local constants

Topics: C# Language Design
Apr 4, 2014 at 1:53 PM
I'd love to see a construct in C# like Java's final for local variables. This would create an immutable local variable which would not only ensure safety, but filter unnecessary things out of the auto-complete list, and possibly provide the compiler a hint for optimizations.

Ideally it would work like the var keyword in that you don't need to specify the type (especially since it can only be created with a corresponding assignment), and I'd propose a syntax like let or def.

As an example:
void Foo(int x)
{
    def double = x * 2;
    DoWork(double);
    double = 5; //Would cause a compile time error
}
Apr 4, 2014 at 8:46 PM
So const locals then?

Maybe the feature request is const var, which I would reduce just having const without a type, although that could mess up autocomplete severely (trying to complete a type when you're typing a name). const var isn't that much weirder than having a static dynamic field or method, I guess.
Apr 5, 2014 at 12:17 AM
It's actually kinda embarrassing, for some reason I thought there was a problem with const locals.

Yes so const locals, but with a much easier syntax. I'd like to see it as easy to use as using var, so that there is no barrier to using it for everything.

Most local variables can very easily be constant, but seldom are done so. const var gives a good effect, but I wonder if having a syntax that's just as easy to type will encourage it's use more.

Either way const var should at least be created, but ideally something like let (to borrow F#'s syntax) would be nice.
Apr 5, 2014 at 2:12 AM
This actually sounds more like you want local readonly variables (const can only be assigned compile-time constant values). Scala has a similar concept called val that declares a local variable that cannot be reassigned once it has been assigned.
Apr 5, 2014 at 3:03 AM
I definitely for this, but I would prefer avoid integrating another keyword. There is already const and readonly keywords that can be reused inside a method declaration in exactly the same semantic way it is used in a member declaration.

This could also be used for method parameter as well local variable declaration:
void (readonly int x)
{
   readonly var y = x + 1;
   readonly string z = y.ToString();
   ...
}
Apr 5, 2014 at 10:51 AM
Edited Apr 5, 2014 at 11:02 AM
ByteMR wrote:
This actually sounds more like you want local readonly variables (const can only be assigned compile-time constant values). Scala has a similar concept called val that declares a local variable that cannot be reassigned once it has been assigned.
You're right, I missed that. readonly var would be perfect and being able to tag a parameter as readonly (given that it wasn't ref/out, naturally) would be a great addition. And yes, let would be a better short synonym, break an impossibly small amount of programs (like var when it was introduced) and fit exactly with the existing LINQ query comprehension meaning. Maybe it'd be annoying that it'd take on different semantics than let in ECMAScript 6 (which is basically equivalent to var with the scoping rules sane people expect but don't currently get).

Many more times than not, I don't intend on reusing variables, so the ability to choose between a variable or a name for a value would stop so many bugs. I suspect this is actually a very brief patch to implement.
Developer
Apr 5, 2014 at 7:48 PM
Edited Apr 5, 2014 at 7:51 PM
F# chose the "let" keyword because it's natural in an ML let environment. When F# wanted to extend this to mutable variables, they added a semantic qualifier to the existing variable syntax. This seems better than adding an unrelated keyword to the language. Alternatively, Scala's choice of "val", standing for "value", as opposed to "var" standing for "variable" uses the syntax to hint at the semantics.

I would take inspiration from these two approaches rather than just importing "let" from F#.
Apr 6, 2014 at 1:36 PM
Import? let is already in C# and means more or less the exact same thing (introduce something that's like a variable only you can't set it twice). It's also as short as var. Maybe there's a better alternative to it (both the idea and the keywords associated), but I like these strengths and I think I'll have a crack at implementing it.
Apr 9, 2014 at 9:17 PM
May 8, 2014 at 5:51 AM
Edited May 9, 2014 at 4:30 AM
angocke wrote:
F# chose the "let" keyword because it's natural in an ML let environment. When F# wanted to extend this to mutable variables, they added a semantic qualifier to the existing variable syntax. This seems better than adding an unrelated keyword to the language. Alternatively, Scala's choice of "val", standing for "value", as opposed to "var" standing for "variable" uses the syntax to hint at the semantics.

I would take inspiration from these two approaches rather than just importing "let" from F#.
angocke,

I agree that the addition of "let" with the meaning "const var" would be somewhat confusing because is adds an asymmetry to the flow of the code. "let" is perfect in Linq Expressions because it matches the declarative "voice" of the other components in the expression.

I think that Scala's var and val are a great example of an intuitive and symmetrical syntax. Additionally, adding "val" to C# (with the meaning "const var") would be a significant step towards the C# team's stated goal of making immutable code as easy to write and as idiomatic as mutable code.
May 8, 2014 at 7:01 AM
JesperTreetop wrote:
Update: Here's a working patch of a prototype implementation of let variables.
CONGRATULATIONS!

How we can apply the patch?
To witch version of Roslyn?
Does it work on VS or only command-line. I tried to make other changes and found VS fighting me back.

About let, I also think that is the perfect name: Already used with the same semantics, sort but different enough. val is too subtle, and readonly var, while clear, is way to long to use it
May 8, 2014 at 12:46 PM
One thing I'd like to see with a "let" syntax, though perhaps this would be too hard to implement, would be to allow the reuse of an identifier but with a compiler-enforced rules that (1) the thing being declared must also be a let identifier, and (2) between the place a let variable as set and where it is used, there must be no possibility of any intervening let statements.

The intended usage case would to have
let temp = someExpr;
[big expression involving temp multiple times]

let temp = someExpr;  // Effectively ending the scope of the earlier `let`
[big expression involving temp multiple times]
etc. replace
// Scoping-only blocks are ugly
{
  var temp = someExpr;
  [big expression involving temp multiple times]
}
{
   var temp = someExpr;
    [big expression involving temp multiple times]
}
or
// Having to use another name for `temp2`, when `temp1` should really leave scope, is ugly

var temp1 = someExpr;
[big expression involving temp1 multiple times]

var temp2 = someExpr;
[big expression involving temp2 multiple times]
With regard to the second point, it would be helpful if, let definitions in if/else branches or switch branches could be merged in cases where the second rule quoted above would hold. For example:
if (condition)
  let foo = 23;
else
  let foo = 57;
doSomethingWith(foo);
be permissible (after the let foo = 23 line the doSomethingWith foo would be guaranteed to see the value written), but
let foo = 23;
if (condition)
{    
  let foo = 57;
  doFirstThingWith(foo);
}
doSecondThingWith(foo);
if the foo identifiers were replaced with foo#1, foo#2, etc. but special handling was employed for the last let within a block of conditional code, I think the existing C# data-analysis engine would be able to determine what should be valid and not, based upon which variables were "definitely written".
May 8, 2014 at 1:08 PM
I don't think overuse of the "temp" identifier is very good. Even local throwaway variables should have some thought in their names, even if it's just a single letter (x,t,i are all pretty descriptive variables).

I think having a variable with the same name in the same scope is going to be confusing, both for the compiler and the user, and the only purpose would be re-using non-descriptive variable names, which shouldn't be encouraged.
May 8, 2014 at 1:40 PM
mirhagk wrote:
I don't think overuse of the "temp" identifier is very good. Even local throwaway variables should have some thought in their names, even if it's just a single letter (x,t,i are all pretty descriptive variables).
If a cached value "variable" is going to be used in places other than the immediate vicinity of its declaration, then it should have a meaningful name. If it isn't, I would suggest that in many cases the name should have minimal semantic content, to emphasize that anyone wanting to know what the name represents should look up a few lines to see where it's written. For example, I would consider:
let q = bigNastyExpression; // Adjusted scale factor; if the fnorble isn't foxed yet, assume a value of 100.
let it = currentPlayer.nextEnemy;
onScreenEnemy = new ScreenObject(q*it.X, q*it.Y, q*it.Z, q.it*XSize, q*it.YSize, q*it.ZSize);
much clearer than
let adjustedScaleFactor = bigNastyExpression;
let nextEnemy = currentPlayer.nextEnemy;
onScreenEnemy = new ScreenObject(adjustedScaleFactor*nextEnemy.X, adjustedScaleFactor*nextEnemy.Y, adjustedScaleFactor*nextEnemy.Z,
                adjustedScaleFactor*nextEnemy.XSize, adjustedScaleFactor*nextEnemy.YSize, adjustedScaleFactor*nextEnemy.ZSize);
Someone who sees q and it in the first case can look up a line or two and see the definitions. I don' think writing out adjustedScaleFactor or nextEnemy really adds any value to the code.
May 8, 2014 at 2:00 PM
Which Is why I wrote
even if it's just a single letter
The code would be something like
let s = bigNastyExpression;
let e = currentPlayer.nextEnemy;
onScreenEnemy = new ScreenObject(s*e.X, s*e.Y, s*e.Z, s*e.XSize, s*e.YSize, s*e.ZSize);
You see what s and e are, and looking through the expression it's easy to remember that s is scale, and e is enemy.

I'd certainly hate to see:
let tmp1 = bigNastyExpression;
let tmp2 = currentPlayer.nextEnemy;
onScreenEnemy = new ScreenObject(tmp1*tmp2.X, tmp1*tmp2.Y, tmp1*tmp2.Z, tmp1*tmp2.XSize, tmp1*tmp2.YSize, tmp1*tmp2.ZSize);
Even using a and b would be better than that. And re-using the tmp variable in the same scope? I can't imagine too many people enjoying working with that code.

Single letters can be very descriptive so long as they relate to what they describe. And even if they don't, as long as they are different.
May 8, 2014 at 4:00 PM
Edited May 8, 2014 at 4:02 PM
I'd prefer to have static local variables, like in C/CPP, and from there, add the readonly feature, like:
void SomeOp (readonly int x)
{
   static readonly var y = x + 1; // same as suggested
   static readonly string z = y.ToString(); // same as suggested

   static var w = 0; // read/write, but only initialized once.

   ++w; // the value modified here is contained in every call to SomeOp.
   ...
}
May 8, 2014 at 4:21 PM
Perhaps I should have used t rather than temp in my earlier example, but I couldn't decide which letter of the alphabet to use I fell back on the useful-for-examples-rather-than-reality temp.

The primary usage case I was thinking of for value recycling would be when it's necessary to have two or three almost-identical pieces of code within a method (e.g. to handle the first thing, then the middle ones (if any), and then the last one). If none of the differences in the three pieces of code relate to the common sub-expression, it seems icky to use different names, but it also seems icky to define a dummy-named variable at a broad scope and use the same variable in three places. Perhaps the real solution will be to use an expression with internal variable definitions.

The other usage case I was thinking of, with regard to conditional code, relates to scenarios where the setting of two or more value-caches is dependent upon a condition. I'm not a particular fan of:
bool f = ExpensiveThingToDetermine(Fred, George);
float x= f ? Fred.X : (Fred.X + George.X)*0.5f;
float y= f ? Fred.Y : (Fred.Y + George.Y)*0.5f;
since that doesn't show the relation between the assignments as clearly as it should; nor am I a huge fan of:
float x,y;
if (ExpensiveThingToDetermine(Fred, George) {
  x=Fred.X;
  y=Fred.Y;
}
else {
  x=(Fred.X + George.X)*0.5f;
  y=(Fred.Y + George.Y)*0.5f;
}
since that makes x and y look like bona fide "variables", even though each is really a value-cache that gets written exactly once.

Perhaps that usage case isn't really common enough to worry about, but I would think there should be some means of handling it. Perhaps what's really needed is a "loose aggregation" syntax which could participate in operators like ? :, so as to allow
{float x, float y} = ExpensiveThingToDetermine(Fred, George) ? { Fred.X, Fred.Y } : { (Fred.X + George.X)*0.5 , (Fred.Y + George.Y)*0.5 }
but that would introduce complications of its own.