C# Design Notes for Sep 3, 2014

Topics: C# Language Design
Coordinator
Sep 5, 2014 at 8:39 PM

C# Design Notes for Sep 3, 2014

Notes are archived here.

Quote of the day: “It’s a design smell. But it’s a good smell.”

Agenda

The meeting focused on rounding out the design of declaration expressions
  1. Removing “spill out” from declaration expressions in simple statements <yes, remove>
  2. Same name declared in subsequent else-if’s <condition decls out of scope in else-branch>
  3. Add semicolon expressions <not in this version>
  4. Make variables in declaration expressions readonly <no>

“Spill out”

The scope rules for variables introduced in declaration expressions are reasonably regular: the scope of such a variable extends to the nearest enclosing statement, and like all local variables, it may be used only after it has been defined, textually.

We did make a couple of exceptions, though: an expression-statement or a declaration-statement does not serve as a boundary for such a variable – instead it “spills out” to the directly enclosing block – if there is one.

Similarly, a declaration expression in one field initializer is in scope for neighboring field initializers (as long as they are in the same part of the type declaration).

This was supposed to enable scenarios such as this:
GetCoordinates(out var x, out var y);
… // use x and y;
to address the complaint that it is too much of a hassle to use out and ref parameters. But we have a nagging suspicion that this scenario – pick up the value in one statement and use it in the next – is not very common. Instead the typical scenario looks like this:
if (int.TryParse(s, out int i)) { … i … }
Where the introduced local is used in the same statement as it is declared in.

Outside of conditions, probably the most common use is the inline common-subexpression refactoring, where the result of an expression is captured into a variable the first time it is used, so the variable can be applied to the remaining ones:
Console.WriteLine("Result: {0}", (var x = GetValue()) * x);
The spill-out is actually a bit of a nuisance for the somewhat common scenario of passing dummies to ref or out parameters that you don’t need (common in COM interop scenarios), because you cannot use the same dummy names in subsequent statements.

From a rule regularity perspective, the spilling is quite complicated to explain. It would be a meaningful simplification to get rid of it. While complexity of spec’ing and implementing shouldn’t stand in the way of a good feature, it is often a smell that the design isn’t quite right.

Conclusion

Let’s get rid of the spilling. Every declaration expression is now limited in scope to it nearest enclosing statement. We’ll live with the (hopefully) slight reduction in usage scenarios.

Else-if’s

Declaration expressions lend themselves particularly well to a style of programming where an if/else-if chain goes through various options, each represented by a variable declared in a condition, using those variables in the then-clause:
if ((var i = o as int?) != null) { … i … }
else if ((var s = o as string) != null) { … s … }
else if
This particular pattern looks like a chain of subsequent options, and even indents like that, but linguistically the else clauses are nested. For that reason, with our current scope rules the variable I introduced in the first condition is in scope in all the rest of the statement – even though it is only meaningful and interesting in the then-branch. In particular, it blocks another variable with the same name from being introduced in a subsequent condition, which is quite annoying.

We do want to solve this problem. There is no killer option that we can think of, but there are a couple of plausible approaches:
  1. Change the scope rules so that variables declared in the condition of an if are in scope in the then-branch but not in the else-branch
  2. Remove the restriction that locals cannot be shadowed by other locals
  3. Do something very scenario specific

Changing the scope rules

Changing the scope rules would have the unfortunate consequence of breaking the symmetry of if-statements, so that
if (b) S1 else S2
No longer means exactly the same as
if (!b) S2 else S1
It kind of banks on the fact that the majority of folks who would introduce declaration expressions in a condition would do so for use in the then-branch only. That certainly seems to be likely, given the cases we have seen (type tests, uses of the Try… pattern, etc.). But it still may be surprising to some, and downright bothersome in certain cases.

Worse, there may be tools that rely on this symmetry principle. Refactorings to swap then and else branches (negating the condition) abound. These would no longer always work.

Moreover, of course, this breaks with the nice simple scoping principle for declaration expressions that we just established above: that they are bounded (only) by their enclosing statement.

Removing the shadowing restriction

Since C# 1.0, it has been forbidden to shadow a local variable or parameter with another one. This is seen as one of the more successful rules of hygiene in C# - it makes code safe for refactoring in many scenarios, and just generally easier to read.

There are existing cases where this rule is annoying:
task.ContinueWith(task => … task …); // Same task! Why can’t I name it the same?
Here it seems the rule even runs counter to refactoring, because you need to change every occurrence of the name when you move code into a lambda.

Lifting this restriction would certainly help the else-if scenario. While previous variables would still be in scope, you could now just shadow them with new ones if you choose.

If you do not choose to use the same name, however, the fact that those previous variables are in scope may lead to confusion or accidental use.

More importantly, are we really ready to part with this rule? It seems to be quite well appreciated as an aid to avoid subtle bugs.

Special casing the scenario

Instead of breaking with general rules, maybe we can do something very localized? Some combination of the two?
It would have to work both in then and else branches; otherwise, it would still break the if symmetry, and be as bad as the first option.

We could allow only variables introduced in conditions of if-statements to be shadowed only by other variables introduced in conditions of if-statements?

This might work, but seems inexcusably ad-hoc, and is almost certain to cause a bug tail in many tools down the line, as well as confusion when refactoring code or trying to experiment with language semantics.

Conclusion

It seems there truly is no great option here. However, we’d rather solve the problem with a wart or two than not address it at all. On balance, option 1, the special scope rule for else clauses, seems the most palatable, so that’s what we’ll do.

Semicolon expressions

We previously proposed a semicolon operator, to be commonly used with declaration expressions, to make “let-like” scenarios a little nicer:
Console.WriteLine("Result: {0}", (var x = GetValue(); x * x));
Instead of being captured on first use, the value is now captured first, then used multiple times.

We are not currently on track to include this feature in the upcoming version of the language. The question is; should we be? There’s an argument that declaration expressions only come to their full use when they can be part of such a let-like construct. Also, there are cases (such as conditional expressions) where you cannot just declare the variable on first use, since the use is in a branch separate from later uses.

Nevertheless, it might be rash to say that this is our let story. Is this how we want let to look like in C#? We don’t easily get another shot at addressing the long-standing request for a let-like expression. It probably needs more thought than we have time to devote to it now.

Conclusion

Let’s punt on this feature and reconsider in a later version.

Should declaration expression variables be mutable?

C# is an imperative language, and locals are often used in a way that depends on mutating them sometime after initialization. However, you could argue that this is primarily useful when used across statements, whereas it generally would be a code smell to have a declaration that’s only visible inside one statement rely on mutation.

This may or may not be the case, but declaration expressions also benefit from a strong analogy with declaration statements. It would be weird that var s = GetString() introduces a readonly variable in one setting but not another. (Note: it does in fact introduce a readonly variable in a few situations, like foreach and using statements, but those can be considered special).

Conclusion

Let’s keep declaration expressions similar to declaration statements. It is too weird if a slight refactoring causes the meaning to change. It may be worth looking at adding readonly locals at a later point, but that should be done in an orthogonal way.
Sep 5, 2014 at 10:10 PM
In my opinion, all variables declared inside a command (like an if) should work for the entire scope (like happens in using clauses, for clauses and the like).
In the case of ifs with else it is a more complicated. After all the "else" is related to the if, yet in many cases the variable will only have a meaning if we enter the if. Yet, I can see some exceptions to this, so, my conclusion:

Keep things local like it happens in fors, using clauses etc.
The variables declared in an if may be valid in the else.

About being read-only or not...
are we going to support readonly for local variables? If we are, we could specify how we want the variable, so I can't say at this moment.
Sep 5, 2014 at 10:44 PM
About the "Removing the shadowing restriction" can't we use some attribute or something to tell the compiler that the variable passed as parameter will be the object itself?

In fact, a think that I always wanted was to declare a lambda with less parameters than the delegate. This happens very frequently on events. I don't want to use the event parameters (sender, e), but I must declare "(a, b) =>", then (c, d) => etc. Why not declare: () => something(), and let it create a delegate with 2 parameters, ignoring them?
Sep 6, 2014 at 12:10 AM
The rule I would have liked to see with regard to shadowing would have said that shadowing an outer-scope variable by an inner-scope variable would cause the value of the outer scope value to become indeterminate. Actually, I would have liked to see the rule extended to even allow shadowing within the same scope, such that
var it = someExpression;
blah = it.x*it.x + it.y*it.y;
....
var it = someOtherExpression;
quack = it.x*it.x + it.y*it.y;
If the shortest explanation for what a variable means would be the code on the line preceding its use, having a name which is used as a "throwaway" may be better than trying to assign a "real" name. If an earlier-defined variable were considered to have an indeterminate value at the point where its name was reused, the restrictions on the use of indeterminately-valued variables would flag any cases where the reuse might cause unintentional results.

[Incidentally, while I know neither C# nor VB is ever apt to do such a thing, I think case-sensitivity should be approached similarly: if Foo is declared e.g. as a class-scope field, and foo is a method-scope variable, then within that method, I would suggest that Foo should refer neither to to the variable name (VB.NET behavior) nor the field name (C# behavior), but should instead be considered a non-existent identifier. Such rules would allow improved IntelliSense behavior, and IMHO provide the best of both worlds].
Sep 6, 2014 at 12:12 AM
paulozemek wrote:
After all the "else" is related to the if, yet in many cases the variable will only have a meaning if we enter the if. Yet, I can see some exceptions to this, so, my conclusion...
What about allowing extern within an expression to indicate that the variable should be hoisted outside the expression? I believe that extern has always been a reserved word, so there's no risk of it colliding with any identifier name, but it presently has no assigned meaning.
Sep 6, 2014 at 3:32 AM
Edited Sep 6, 2014 at 3:43 AM
This may or may not be the case, but declaration expressions also benefit from a strong analogy with declaration statements. It would be weird that var s = GetString() introduces a readonly variable in one setting but not another. (Note: it does in fact introduce a readonly variable in a few situations, like foreach and using statements, but those can be considered special).
I would actually argue the opposite. If you consider that by far and away the most common use case for this feature is almost to extend the foreach and using syntax to other statements like if, then it should actually come with the same immutability as is already in place for foreach and using statements. It would be weird that this:
using (var x = Blah()) { x = Bleh(); }
would fail to compile, whereas this:
if (int.TryParse(x, out int y)) { y = 42; }
would successfully compile.

EDIT: although now that I'm looking back at this again, I'm not as convinced :-)
Sep 6, 2014 at 3:39 AM
Edited Sep 6, 2014 at 3:40 AM
Another comment. If the main use case which this feature unlocks is inlining out/ref parameters, wouldn't it be better to think of introducing tuple decomposition as an alternative means of achieving the same goal? Elsewhere I've seen comments for an automatic syntax sugar which treats out parameters simply as additional return values, so you could do something like this:
int myMethod(out int x, out int y) { ... }
...
// existing syntax
int x, y;
int w = myMethod(out x, out y);
...
// new syntax
(int w, int x, int y) = myMethod();
Or something like that, but you get the idea.

I'm kind of anticipating better syntax for tuples to be inevitable, and it would be strange to have two different syntaxes for dealing with essentially the same problem. Then again, perhaps it'll be a bit like the old anonymous delegate instantiation syntax versus the newer lambda expression syntax: two different syntaxes for the same problem.
Sep 6, 2014 at 1:39 PM
Well first thing, like above poster I think the out parameters should go (go away). Nothing is more disturbing then to have function output coming from both sides. That is not counting current implementation with pre-declaring of variables. Even if this inline declaration will help with typing annoyance you still cannot do proper piping of outputs from function to function.
Second may be it's just me, but what is the problem with the spill? Is not it how it is working now? Consider the current code:
int x;
if(int.TryParse("1",out x))
{
   //Do something with "x"..
}
Console.WriteLine(x.ToString()); //Still available.
So why change the functionality?
Sep 6, 2014 at 3:37 PM
What Apple Swift does for conditional assignment is that the declared variable only resolves within the then-statement but not in the else-statement. That might be a little weird given that you're trying to define a more general-purpose syntax and some people might expect a wider scope but I think it does make sense to have slightly different rules depending on where the variable is declared.


Apple Swift:
let x : Int? = 0;

if let y = x {
    var s = "The value of y is \(y)";
}
else {
    var s = "The value of y is \(y)"; // compiler error, y is not defined
}
C#:

// Example 1

dict.TryGetValue(key, out var value);
// value is available here


// Example 2

if (dict.TryGetValue(key, out var value))
{
    // value is available here
}
else
{
    // value is not available here
}
// value is not available here


// Example 3

while (dict.TryGetValue(key, out var value))
{
    // value is available here
}
// value is not available here


// Example 4

if (dict.TryGetValue(key1, out var value))
{
    // value is available here
}
else if (dict.TryGetValue(key2, out var value))
{
    // value is available here, but is a different local and could be of a different type
}
else
{
    // value is not available here
}
// value is not available here


// Example 5

var found = from key in dict.Keys
    where dict.TryGetValue(key, out var value)
    select new { key, value }; // value is available here

// value is not available here

I don't know what complexity that would add, nor do I expect everyone (anyone?) to agree, but to me this feels like the most intuitive behavior and probably what people would expect the most.
Sep 6, 2014 at 3:38 PM
I'm not sure about the removal of "spilling out". It's mentioned removing this will make it easier for situations like COM Interop, where you don't actually care about the parameters so you'd like to reuse names. A few reactions to this:
  • In a world that is increasingly focused on mobile, web, or server-side development, how common is COM Interop? Should you really still be basing language-design around this vanishing use-case?
  • I think one of the suggestions mentioned in the pattern-matching thread make more sense here than reusing variable names for unwanted parameters - add the ability to use _ for unwanted parameters. This seems like a feature useful in many different cases.
Sep 6, 2014 at 3:59 PM
MgSam wrote:
I'm not sure about the removal of "spilling out". It's mentioned removing this will make it easier for situations like COM Interop, where you don't actually care about the parameters so you'd like to reuse names. A few reactions to this:
  • In a world that is increasingly focused on mobile, web, or server-side development, how common is COM Interop? Should you really still be basing language-design around this vanishing use-case?
  • I think one of the suggestions mentioned in the pattern-matching thread make more sense here than reusing variable names for unwanted parameters - add the ability to use _ for unwanted parameters. This seems like a feature useful in many different cases.
Wasn't COM interop also trumpeted as the reason for adding support for optional arguments? Are we finding a lot of COM interfaces that have required ref/out parameters? COM is still huge and a common denominator in Windows desktop development, so I doubt it will be going away any time soon.

I kind of like the idea of handling unwanted ref/out parameters. _ isn't a keyword, though, and is a valid identifier name, so perhaps reusing a keyword like null might be appropriate? Althrough if (dict.TryGetValue(key, out null)) { ... } does look a little strange.
Sep 6, 2014 at 4:08 PM
Halo_Four wrote:
Wasn't COM interop also trumpeted as the reason for adding support for optional arguments? Are we finding a lot of COM interfaces that have required ref/out parameters? COM is still huge and a common denominator in Windows desktop development, so I doubt it will be going away any time soon.
I agree COM is still crucial for desktop development- but that itself is diminishing in importance, and we are talking about C# here- which mainly uses .NET and not COM. What percent of C# desktop applications are using COM interops with out parameters?
I kind of like the idea of handling unwanted ref/out parameters. _ isn't a keyword, though, and is a valid identifier name, so perhaps reusing a keyword like null might be appropriate? Althrough if (dict.TryGetValue(key, out null)) { ... } does look a little strange.
I think context-sensitive keyword + diagnostic to warn you if a variable with that name is in scope would suffice here. I'm not in love with _, but to me, whatever the solution is it should be a single character keyword. You want to draw attention away from these unwanted parameters, not force you to litter your code with the 4 characternullkeyword.@` could be a good alternative as it's not a valid variable name.
Sep 6, 2014 at 4:26 PM
MgSam wrote:
Halo_Four wrote:
Wasn't COM interop also trumpeted as the reason for adding support for optional arguments? Are we finding a lot of COM interfaces that have required ref/out parameters? COM is still huge and a common denominator in Windows desktop development, so I doubt it will be going away any time soon.
I agree COM is still crucial for desktop development- but that itself is diminishing in importance, and we are talking about C# here- which mainly uses .NET and not COM. What percent of C# desktop applications are using COM interops with out parameters?
Until Office exposes a pure .NET API which is cleaned up, probably a lot. I think a lot of the problem is that in COM it was more common for parameters to be ref, especially since VB5/6 arguments were ref by default.
I kind of like the idea of handling unwanted ref/out parameters. _ isn't a keyword, though, and is a valid identifier name, so perhaps reusing a keyword like null might be appropriate? Althrough if (dict.TryGetValue(key, out null)) { ... } does look a little strange.
I think context-sensitive keyword + diagnostic to warn you if a variable with that name is in scope would suffice here. I'm not in love with _, but to me, whatever the solution is it should be a single character keyword. You want to draw attention away from these unwanted parameters, not force you to litter your code with the 4 characternullkeyword.@` could be a good alternative as it's not a valid variable name.
Maybe take a cue from wildcard pattern matching here. In several languages like F# and Apple Swift they use the _ character as you suggest. The C# pattern matching proposal went with * instead which avoids that whole valid-identifier problem and I think looks about right:
var hasValue = dict.TryGetValue(key, *);
Sep 6, 2014 at 4:35 PM
Edited Sep 6, 2014 at 4:37 PM
Possible solution to the Else-if problem:

One thing that I'm not sure is, in code like this:
if(int.TryParse(s1, out var num1) || int.TryParse(s2, out var num2)
{
   //What happens here??
}
The possibilities are:
  1. That both are declared and num2 could be default(int)
  2. That the code does not compile
  3. That the code compiles, but a reference to num2 doesn't because is not 'definitely declared'.
Assuming that un-evaluated expressions should not be able to declare variables, then this code should work
if (o is int && (var a = (int)o) != null) { //a is int  }
else {  //a is not declared  }
Of course this is a hack for two reasons:
  • Comparing an int to null just to continue the expression
  • The compiler will need to know that the second part of the and is a true constant.
But the other day I was trying to convince Neal to overtake the pattern obj is Type ident that is proposed for Pattern Matching to Visual Studio 2014, since it looks simpler to implement, incredibly useful and a perfect partner of declaration expressions.

So instead of having to write the horrible:
if ((var i = o as int?) != null) { … i … }
else if ((var s = o as string) != null) { … s … }
else if
Or even worst
if (o is int &&  (var a = (int)o) != null) { … a of type int … }
else if (o is string && (var a = (string)o) != null) { … a of type string … }
else if
We could write something that nice:
if (o is int a) { … a of type int … }
else if (o is a) { … a of type string … }
else if
And the compiler can know that 'a' is only declared when the result is 'true', and Refactoring tools could manipulate the code safely:
if (!(o is int a)) 
{
   // a is not declared
   if (o is a) { … a of type string … }
   else if …
} 
else {  … a of type int … }
And even more
if (!(o is int a)) 
{
   // a is not declared
   if (!(o is a)) 
   { 
       // a is not declared 
       ...
   }
   else { ... a of type string … }
} else {  … a of type int … }
Is there any cons to this solution?

I think it will be wonderful to be able to use this next year, not in three years.
Sep 6, 2014 at 4:56 PM
Edited Sep 6, 2014 at 7:27 PM
You can start with the general principle:
if (int.TryParse("1", out var x) {
  // A
} else if (int.TryParse("2", out var y) {
  // B
} else {
  // C
}  
x is available at A, B and C. y is available at B and C. It mirrors the compiler's desugaring:
int x;
if (int.TryParse("1", out x)) { // if 1
    // A
} else {
    int y;
    if (int.TryParse("2", out y)) { // if 2
        // B
    } else {
        // C
    }
}
This assumes that the declaration site of y is hoisted to the least possible elevated position, which is just outside if 2 and not before if 1. Otherwise, there would have to be special logic to walk all the way up to the initial if in large if-else-chains, and this would also be illegal:
// X
if (int.TryParse("1", out var x)) { // if 1
    // A
    var y = 42;
} else if (int.TryParse("2", out var y)) { // if 2
    // B
}
because B's y would be hoisted to point X and block A's variable y. Which is clearly insane. y's (the one used in B) bounding statement isn't if 1, it's either if 2 or the else itself (of which more later).

So let's say you also should be able to redeclare x in this example:
// Z
if (int.TryParse("1", out var x)) {
    // A
} else if (int.TryParse("2", out var x)) {
    // B
} else {
    var z = x; // C
    var x = 42; // D
}
With x being extended to both else's through hoisting to Z for the purposes of allowing out var x, even if redeclaration is perfectly valid everywhere, which x is in scope at C; which value will z get? (Or if both are in scope, which one will x refer to?) Both conditions have run and two different variables with the same name have been definitely assigned; both are seemingly as local but B's x is closer due to nesting - what's going on here?

So far for avoiding subtle bugs, I've been writing C# for most of the past decade and I have no intuition whatsoever for which should be used. I have vague guesses, but it reads as if you're using something you have previously declared which has then gone out of scope, which is clearly forbidden in every other place I can think of. I know that nothing else could be in scope for both A and B without being declared at least as far out as Z.

And if the answer is "none of the above, x is considered declared but isn't valid at C", will I be allowed to remove C and keep the local variable x declaration at D? A const x? Use a lambda with a parameter named x? Additionally, what happens if x already is defined as far out as Z, before any of the redefinitions?

My first example, the general principle, looks sort of OK, but it opens up for this kind of madness. If you're going to allow the above sort of code to make sense, the only way is to make the particular x be in scope for the condition it's declared in (and valid to use only "right of it"), as well as only the then branch. Even without the B branch, the only response that makes sense to me is a compiler error; there's no x here. Make the people who want the variable available in both branches define the variable so that it is clearly available to both branches - not magically available contrary to the rest of the rules of the language.
Sep 6, 2014 at 5:07 PM
Olmo wrote:
Possible solution to the Else-if problem:

One thing that I'm not sure is, in code like this:
if(int.TryParse(s1, out var num1) || int.TryParse(s2, out var num2)
{
   //What happens here??
}
The possibilities are:
  1. That both are declared and num2 could be default(int)
  2. That the code does not compile
  3. That the code compiles, but a reference to num2 doesn't because is not 'definitely declared'.
I don't think that would behave any differently than it would today, which is option #3:

int x, y;
if (int.TryParse(s1, out x) || int.TryParse(s2, out y))
{
    Console.WriteLine("{0},{1}", x, y);  // compiler error, use of unassigned local variable 'y'.
}

I think that is appropriate as the compiler could never guarantee that the second int.TryParse is called, but the first would always be called.

Of course, you can also solve this problem by using the Logical OR operator:

int x, y;
if (int.TryParse(s1, out x) | int.TryParse(s2, out y))
{
    Console.WriteLine("{0},{1}", x, y);  // perfectly fine, x and y are both guaranteed to have been assigned to something even if one call returned false.
}

// or

if (int.TryParse(s1, out var x) | int.TryParse(s2, out var y))
{
    Console.WriteLine("{0},{1}", x, y);
}

I do think that when the C# looks further into pattern matching, which I expect would be C#7 timeframe, that problems of this type will be revisited.
Sep 6, 2014 at 9:18 PM
Halo_Four wrote:
C#:
// Example 2

if (dict.TryGetValue(key, out var value))
{
    // value is available here
}
else
{
    // value is not available here
}
Can't say I'm a fan of it not being available in the else clause. What if you wanted to reverse it?
if (!dict.TryGetValue(key, out var value))) {
  // value not needed here.
}
else {
  // value required here.
}
Additionally, nothing says that the boolean returned by the function has to indicate that the out args are populated. That's just a TryXXX thing. I'd rather shadowing be allowed. Perhaps allow the new keyword when declaring locals.

As a side thought, how does this work with ref arguments? A ref argument needs to be initialized before being passed. I suppose it could just default to default, but then what's the point of it being ref instead of out? Will we be able to write them with an initial value, like so?
SomeFunc(ref int i = 1337);
Sep 6, 2014 at 10:19 PM
Removing “spill out” from declaration expressions in simple statements <yes, remove>
I thought that it would be awesome to do something like this:
public static void Desonstruct<T1, T2>(this Tuple<T1, T2> tuple, out T1 first, out T2 second)
{
    first = tuple.First;
    second = tuple.Second;
}
and then
tuple.Deconstruct(out var foo, out var bar);

Which is far shorter code than
var foo  = tuple.Item1;
var bar  = tuple.Item2;
especially in case of tuples with 3 or more items.
Sep 6, 2014 at 10:40 PM
kekekeks wrote:
Removing “spill out” from declaration expressions in simple statements <yes, remove>
I thought that it would be awesome to do something like this:
public static void Desonstruct<T1, T2>(this Tuple<T1, T2> tuple, out T1 first, out T2 second)
{
    first = tuple.First;
    second = tuple.Second;
}
and then
tuple.Deconstruct(out var foo, out var bar);

Which is far shorter code than
var foo  = tuple.Item1;
var bar  = tuple.Item2;
especially in case of tuples with 3 or more items.
Nice idea! Actually it's not much shorter, but it shows the intent better.
Sep 7, 2014 at 1:31 AM
Edited Sep 7, 2014 at 1:33 AM
kekekeks wrote:
Removing “spill out” from declaration expressions in simple statements <yes, remove>
I thought that it would be awesome to do something like this:
public static void Desonstruct<T1, T2>(this Tuple<T1, T2> tuple, out T1 first, out T2 second)
{
    first = tuple.First;
    second = tuple.Second;
}
and then
tuple.Deconstruct(out var foo, out var bar);
I suppose with the removal of the "spill-out" feature our best option is to abuse the using keyword by doing something like the following. Syntactically it still looks rather nice, I think, since the meaning of the English word "using" plays well with the usage.
public sealed class Dummy: IDisposable {
    private Dummy() {}
    private static readonly Lazy<Dummy> _instance = new Lazy<Dummy>(() => new Dummy());
    public static Dummy Instance { get { return _instance.Value; } }
    public void Dispose() { }
}

public static Dummy Untupled<T1, T2>(this Tuple<T1, T2> tuple, out T1 first, out T2 second) {
    first = tuple.Item1;
    second = tuple.Item2;
    return Dummy.Instance;
}

// ...

using (t.Untupled(out var foo, out var bar)) {
    // do stuff using foo and bar
}
Sep 7, 2014 at 1:41 AM
Edited Sep 7, 2014 at 1:44 AM
Another worry that I have is that the combination of the "spill-out" removal and the new if scoping rules for declaration expressions encourages nesting. My typical pattern of dealing with input validation, failures, exceptional situations, etc. is doing something like this:
public void DoStuff(string input) {
    int data;
    if (!int.TryParse(input, out data)) throw new ArgumentException("input");
    // use data in a long and elaborate algorithm
}
Previously declaration expressions would remove one ugly line from this code (which is nice), but otherwise would leave it unchanged. However, now if I want to use declaration expressions, I'm forced to write my code like this:
public void DoStuff(string input) {
    if (int.TryParse(input, out int data) {
        // use data in a long and elaborate algorithm
    }
    else throw new ArgumentException("input");
}
Why do I need to scroll through the entire logic of a function to get to the exceptional case? Why do I need to increase the nesting level of my code one more level, it's already hanging somewhere in the middle of the screen horizontally?
Sep 7, 2014 at 2:00 AM
Yota wrote:
Halo_Four wrote:
C#:
// Example 2

if (dict.TryGetValue(key, out var value))
{
    // value is available here
}
else
{
    // value is not available here
}
Can't say I'm a fan of it not being available in the else clause. What if you wanted to reverse it?
if (!dict.TryGetValue(key, out var value))) {
  // value not needed here.
}
else {
  // value required here.
}
Yeah, I was kind of back-and-forth over it as well. The thinking I had leading towards not permitting the declared variables to be in scope in the else-statement was the notion of reusing those variable names if nesting else-if expressions. Your example did cross my mind and I have followed that pattern myself.

Looking into it a little further I think that the answer can be found in C/C++ where variable declarations are legal in an if statement and their scope does extend to both the then-statement and the else-statement.
if (int i = getValue() < 0)
{
    printf("i is %d which is less than zero.\n", i);
}
else if (i > 0)
{
    printf("i is %d which is greater than zero.\n", i);
}
else
{
    printf("i is %d which is zero.\n", i);
}
// i is no longer in scope here

Additionally, nothing says that the boolean returned by the function has to indicate that the out args are populated. That's just a TryXXX thing.
Agreed, I didn't mean to imply that the return value had anything to do with the out parameters or that satisfying the condition implied that the out parameters were assigned to anything of value. The fact that the method returned without throwing an exception is all the compiler cares about and technically the runtime does nothing to guarantee that the parameters would be set at all.
Sep 7, 2014 at 2:01 PM
Yota wrote:
Perhaps allow the new keyword when declaring locals.
This is a good idea. Perhaps the new keyword isn't the best way to do it, but allowing locals to be shadowed via some explicit declaration operator makes much more sense than throwing out the old rule or keeping it totally unchanged.


The other examples people have shown in this thread clearly demonstrate why the loss of spill-over cripples the declaration expressions feature. As specified, it forces you to increase nesting of your code, which is something we should be trying to lesson, not increase. I'd rather have the one extra line where I explicitly declare my variable than add more nesting. If it's too complex to specify easily with spill-over I'd rather see it shelved than a weak version of it get implemented.

It also seems silly to be using if ((var i = o as int?) != null) { … i … } as a valid use-case scenario. That code is hideous and not something the language design team should try to be encouraging. The new syntax previously proposed is far nicer if(o is int i) { ... i ... }.
Sep 7, 2014 at 2:06 PM
It also seems silly to be using if ((var i = o as int?) != null) { … i … } as a valid use-case scenario. That code is hideous and not something the language design team should try to be encouraging. The new syntax previously proposed is far nicer if(o is int i) { ... i ... }.
Specially for value types, because i will be declared as int? in the first example.

And the fact that the compiler can associate declaring the variable and evaluating to true solves the if...else problem.
Sep 7, 2014 at 5:53 PM
MgSam wrote:
The other examples people have shown in this thread clearly demonstrate why the loss of spill-over cripples the declaration expressions feature. As specified, it forces you to increase nesting of your code, which is something we should be trying to lesson, not increase. I'd rather have the one extra line where I explicitly declare my variable than add more nesting. If it's too complex to specify easily with spill-over I'd rather see it shelved than a weak version of it get implemented.
Perhaps, but to me that's also the scoping that makes sense. It's what is expected in similar constructs currently in C# and there is also precedence within the language heritage for that behavior coming from C++.
It also seems silly to be using if ((var i = o as int?) != null) { … i … } as a valid use-case scenario. That code is hideous and not something the language design team should try to be encouraging. The new syntax previously proposed is far nicer if(o is int i) { ... i ... }.
Reading about C++'s implementation that very construct was held up as the idiomatic use of the feature, although being slightly less verbose due to C++ allowing arbitrary values as conditions:
if (CObj* obj = GetSomeObject())
{
    // use obj here
}
// obj out of scope here
I agree that the pattern matching syntax is nicer but that's intended to solve a different set of problems and I don't think that it's necessary to toss out inline declarations for the other use cases such as out parameters.
Sep 7, 2014 at 7:54 PM
Halo_Four wrote:
I agree that the pattern matching syntax is nicer but that's intended to solve a different set of problems
Pattern matching in general solves a different problem but this particular feature is just the first step in this direction, and when applied with simple types (instead of is operator) has really nothing to do with pattern matching. It is not as general as type declarations, but solves the particular problem of testing for type and storing the result in a declared variable much nicely.
and I don't think that it's necessary to toss out inline declarations for the other use cases such as out parameters.
I'm not proposing to replace declaration expressions, just to move this particular small feature one release earlier.
Sep 7, 2014 at 9:00 PM
Edited Sep 7, 2014 at 9:01 PM
The else-clause seems to be an ugly asymmetry to solve a problem that isn't there. We already can't re-use variable names in C#, so why introduce this for a problem that doesn't (yet) exist?

There's no reason that we should mandate this:
if (d.TryGetValue(key, out var value)) {  /* use value */ } else { /* throw */ }
Over this:
if (!d.TryGetValue(key, out var value)) { /* throw */ } else { /* use value */ }
Sep 7, 2014 at 9:22 PM
I really don't think scoping should be change or become less predictable.

No spill out.

If you really are thinking about introducing variable shadowing, allow the use of the new access modifier to be applied to parameters and variables. And while you are at it, allow the same for the readonly modifier.
Developer
Sep 7, 2014 at 11:43 PM
PauloMorgado wrote:
I really don't think scoping should be change or become less predictable.

No spill out.
Local declaration statements do spill out into the enclosing block. Local declaration expressions do not. In this way they are different.
int i = 12; // spills into the enclosing block

(int j) = 12; // not available in the enclosing block
Sep 8, 2014 at 12:00 AM
Edited Sep 8, 2014 at 12:25 AM
nmgafter wrote:
Local declaration statements do spill out into the enclosing block. Local declaration expressions do not. In this way they are different.
I mant no spill out of the enclosing block and not no spill into the enclosing block.
Sep 8, 2014 at 12:18 AM
Edited Sep 8, 2014 at 12:20 AM
Why cannot we make declaration expressions always available in the enclosing block, regardless of whether it is a statement or an expression? Looks like it has a number of advantages:
  1. It is consistent with the desugaring, which I would expect to have exactly the same behavior as the equivalent C# 4 code that I write now. Currently it's not the case, and that is sometimes confusing. Compare the three snippets of code:
    /* ======= C# 4 without declaration expressions ======= */
    int i;
    if (int.TryParse(s, out i)) {
        // i is available and initialized
    }
    // i is available but may be uninitialized
    int x;
    if (!int.TryParse(s, out x)) return false;
    // x is available
    int y, z;
    t.Untuple(out y, out z);
    // y and z are available
    
    /* ======= C# 5 currently ======= */
    if (int.TryParse(s, out int i)) {
        // i is available and initialized
    }
    // i is unavailable (!)
    if (!int.TryParse(s, out int x)) return false;
    // x is unavailable (!)
    t.Untuple(out int y, out int z);
    // y and z are unavailable (!)
    
    /* ======= C# 5 with the proposed amendment =======  */
    if (int.TryParse(s, out int i)) {
        // i is available and initialized
    }
    // i is available but may be uninitialized
    if (!int.TryParse(s, out int x)) return false;
    // x is available
    t.Untuple(out int y, out int z);
    // y and z are available
    
  2. It is an internally consistent rule that does not have any exceptions in the spec (which, I believe, is also easier to implement than the removed partial "spill-out" behavior). A declaration expression is equivalent to the local variable declaration statement at a sequence point directly preceding the current statement in the immediately enclosing block. No more, no less. This, I believe, is also equivalent to the C++ handling of the "if-else" problem.
  3. We also get rid of the surprising differences like the one mentioned by nmgafter.
Developer
Sep 8, 2014 at 12:24 AM
Skiminok wrote:
Why cannot we make declaration expressions always available in the enclosing block, regardless of whether it is a statement or an expression?
We can. We just don't like it.
  1. It is consistent with the desugaring, which I would expect to have exactly the same behavior as the equivalent C# 4 code that I write now.
The simplest desugaring requires the introduction of a block, so that it can be used, for example, as the controlled statement of an if where only a single statement is allowed. In that case the simplest desugaring does not have these declarations spill into the enclosing block.
  1. It is a consistent rule that does not have any exceptions in the spec (which, I believe, is also easier to implement than the removed partial "spill-out" behavior).
The only "exception to the spec" that we've added is the distinction between the two controlled statements of an if statement. That is separate from the "spill out" issue.
  1. We also get rid of the surprising differences like the one mentioned by nmgafter.
You won't be surprised for long. In fact, I believe you'll get used to and like the principle that if you intend to share a variable across statements within the same block, you should declare it using a declaration statement in that block.
Sep 8, 2014 at 12:26 AM
Hiow would you compare this with the using statement in terms of variable scoping?
Developer
Sep 8, 2014 at 12:35 AM
PauloMorgado wrote:
How would you compare this with the using statement in terms of variable scoping?
Similar, in the sense that it is restricted to the statement in which it is declared.

Here's another interesting case
int i = int j = 12;
// i available here; j not available here
Sep 8, 2014 at 12:40 AM
nmgafter wrote:
PauloMorgado wrote:
How would you compare this with the using statement in terms of variable scoping?
Similar, in the sense that it is restricted to the statement in which it is declared.
Similar in the way that variables declare inside the if statement are not available outside it, right?
Here's another interesting case
int i = int j = 12;
// i available here; j not available here
Makes sense to me.
Sep 8, 2014 at 12:48 AM
Edited Sep 9, 2014 at 12:54 PM
Update: I misread the notes and thought that the decision was to permit limited shadowing in the else-statement. That is not the case, the variable declared in the if clause is only scoped to the then-statement so it can be redefined in a nested if statement under the else-statement without shadowing.

Rereading the notes a little more closely I think I get the rational behind the decisions. I do like the exception to the shadowing restrictions on the else-statement as it solves my issue with the scoping nicely. I'm kind of ambivalent about the spill-out to the enclosing block when the declaration is not contained within a control flow statement as I can understand the argument from the others but I do agree that it is kind of ugly.

So, as it stands, the following would be legal C# 6.0, correct?

IDictionary<string, string> dict1 = ...;
IDictionary<string, int> dict2 = ...;
string key = ...;

if (dict1.TryGetValue(key, out var value))
{
    Debug.Assert(value is string);
}
else if (dict2.TryGetValue(key, out var value) && value > 5)
{
    Debug.Assert(value is int && value > 5);
}
else
{
    Debug.Assert(value is int && value <= 5);
}

Has there been any discussion in the team regarding how this form of declaration would be interpreted within LINQ queries? For example, I can see the following being a not-uncommon scenario:
IDictionary<string, int> dict = ...;
IEnumerable<string> keys = ...;

var values = from key in keys
    where dict.TryGetValue(key, out var value)
    select new { key, value };
It's my opinion that this be treated kind of like the syntax candy of let where the equivalent extension method query would be as follows:
var values = keys
    .Select(key => {
        int value;
        bool temp1 = dict.TryGetValue(key, out value);
        return new { temp1, key, value };
    })
    .Where(tuple => tuple.temp1)
    .Select(tuple => new { tuple.key, tuple.value });
Although I would totally understand if such behavior had to be restricted to within a let clause as follows:
var values = from key in keys
    let found = dict.TryGetValue(key, out var value)
    where found
    select new { key, value };
Developer
Sep 8, 2014 at 3:00 AM
Halo_Four wrote:
So, as it stands, the following would be legal C# 6.0, correct?
Yes
Has there been any discussion in the team regarding how this form of declaration would be interpreted within LINQ queries?
For example, I can see the following being a not-uncommon scenario:
IDictionary<string, int> dict = ...;
IEnumerable<string> keys = ...;

var values = from key in keys
    where dict.TryGetValue(key, out var value)
    select new { key, value };
Yes, we've thought about it, and it won't work. Since Linq queries are defined by translation into method calls with lambda expressions, and variable-declaration-expressions are specified not to "leak" out of a lambda, this does not work. And if you think about the semantics, they could not possibly work since .Select might call its lambda without .Where's lambda ever being called.
Sep 8, 2014 at 6:46 AM
Edited Sep 8, 2014 at 6:47 AM
nmgafter wrote:
Halo_Four wrote:
So, as it stands, the following would be legal C# 6.0, correct?
Yes
Wait, I am confused. How can it be legal? According to the latest change of "if-else" scoping rules (point #2 of this thread), value should not be accessible in the second else block, hence the assertion value is int && value <= 5 would fail to compile. Am I missing something?
Sep 8, 2014 at 9:58 AM
Edited Sep 8, 2014 at 10:00 AM
IMHO, removing the spill out and changing the if scope rules are both very sad moves.

As I understand, declaration expression were introduced to simplify usage of out variables. And now lack of spill out and changed if scope makes it nearly impossible to use declaration variables together with out arguments. The purpose of "declaration" is to use it later after all. If I cannot use declared variable after it was declared, then what is the point of the whole feature?
If I cannot write:
if (!int.TryParse(stringValue, out var count))
  throw new FooException("blah");

// use count variable
or
if (!dictionary.TryGetValue(key, out var cached))
{
  cached = new Foo();
  dictionary.Add(key, cached);
}

// use cached here
then the feature is unusable for me. And the fact I am pretty much restricted with my refactorings (cannot reverse if condition) doesn't help here either. For me it looks like you want to fix something which is perfectly working now.
Let’s get rid of the spilling. Every declaration expression is now limited in scope to it nearest enclosing statement. We’ll live with the (hopefully) slight reduction in usage scenarios.
The reduction is not that slight. Current decision chops off half of its usability. For me it looks like you made somewhat false assumption that declaration expression used inside condition of the if statement are used in "positive" manner only. And that refering to declared variable inside if branch is enough. It's not - "negative" conditions are as much frequent. And thus declared variable should be visible in else branch as well as after the if statement.

Regarding the shadowing in nested if-else: I see the issue you want to address here. But I don't see why this is an issue in the first place. If the variable has the same type in both places then we don't need to use declaration expression in the second place. We can just refer to the same variable introduced earlier:
if (var i = GetValue())
{
// use of i
}
else if (i = GetValue2()) //<-- no need for redeclaration of i
{
// use of i
}
If both variables have different type, then using the same name is a serious code smell for me. Naming is hard. One should pick descriptive names to avoid potential confusion. Using the same generic "value", "x" or "i" for multiple things will bite us back, sooner or later.
The one exception to this are dummy names. But dummy names are, well, dummy. They are intended to be used as placeholders for things we don't want to refer after they are declared and eventually used (in the same expression). And to address such scenarios I would see something different in c#, e.g. single underscore:
if (int.TryParse(stringValue, out var _)) // <-- _ 
{
 // _ is just a placeholder, you cannot refer to _ here
}
else if (DateTime.TryParse(stringValue, out var _))
{
 // _ is just a placeholder, you cannot refer to _ here
}
internally compiler could translate each "_" into something like "__$1", "__$2" etc.
Or, even better - allow to skip variable declaration entirely:
if (int.TryParse(stringValue, out)) // <-- no dummy variable declared at all.
{
}
else if (DateTime.TryParse(stringValue, out))
{
}
In such case compiler could inject "__$1", "__$2" where necessary.

And to address the original code:
Console.WriteLine("Result: {0}", (var x = GetValue()) * x);
Console.WriteLine("Result: {0}", (var x = GetValue2()) * x);
I can see you want to be able to write code like above - to use x in both expressions.
But again, if the type of x is same in both places, then we can just write:
Console.WriteLine("Result: {0}", (var x = GetValue()) * x);
Console.WriteLine("Result: {0}", (x = GetValue2()) * x);
If the types are different, or if you really want to make "x" unavailable after the statement it was declared in, then again - dummy _ can help to address such scenario:
Console.WriteLine("Result: {0}", (var _ = GetValue()) * _);
Console.WriteLine("Result: {0}", (var _ = GetValue2()) * _);
this could be rewritten by compiler as:
Console.WriteLine("Result: {0}", (var __$1 = GetValue()) * __$1);
Console.WriteLine("Result: {0}", (var __$2 = GetValue2()) * __$2);
One issue I can see here - sometimes you want to have multiple dummy names in single expression. Compiler could special case, say _, __ and ___ names for such purpose. Not pretty, but if you have 4+ dummy names, then probably expression is so complex that you want to use descriptive names anyway. Eventually, compiler could special case names like _, _1, _2, _3 etc.
Sep 8, 2014 at 10:30 AM
Przemyslaw wrote:
If I cannot write:
if (!int.TryParse(stringValue, out var count))
  throw new FooException("blah");

// use count variable
or
if (!dictionary.TryGetValue(key, out var cached))
{
  cached = new Foo();
  dictionary.Add(key, cached);
}

// use cached here
then the feature is unusable for me.
I initially agreed with the proposed changes, but I'm beginning to have doubts... The use cases showed by Przemyslaw are very common, and it would be a shame to be unable to use declaration expressions in them.
Sep 8, 2014 at 11:13 AM
tom103 wrote:
Przemyslaw wrote:
If I cannot write:
if (!int.TryParse(stringValue, out var count))
  throw new FooException("blah");

// use count variable
or
if (!dictionary.TryGetValue(key, out var cached))
{
  cached = new Foo();
  dictionary.Add(key, cached);
}

// use cached here
then the feature is unusable for me.
I initially agreed with the proposed changes, but I'm beginning to have doubts... The use cases showed by Przemyslaw are very common, and it would be a shame to be unable to use declaration expressions in them.
I think that would cause more confusion. Those examples the perfect use cases for not using declaration expressions. If you want the variable to be available outside of the if-else statement, declare it outside.

This feature is about scoping down the declaration of variables when you don't want to pollute the outer scope, not an easier way to do it.
Sep 8, 2014 at 11:19 AM
Przemyslaw wrote:
The one exception to this are dummy names. But dummy names are, well, dummy. They are intended to be used as placeholders for things we don't want to refer after they are declared and eventually used (in the same expression). And to address such scenarios I would see something different in c#, e.g. single underscore:
That would be breaking change because _ is a valid simple name.

But, yes, it would be nice to have a "don't care about this" call argument.
Sep 8, 2014 at 11:40 AM
Edited Sep 9, 2014 at 12:55 PM
Skiminok wrote:
nmgafter wrote:
Halo_Four wrote:
So, as it stands, the following would be legal C# 6.0, correct?
Yes
Wait, I am confused. How can it be legal? According to the latest change of "if-else" scoping rules (point #2 of this thread), value should not be accessible in the second else block, hence the assertion value is int && value <= 5 would fail to compile. Am I missing something?
That's what I missed in the meeting notes at first, too. The entire conversation over revisiting shadowing locals was over the reuse of variable names in the else-statement and the decision, at least at this time, is to allow a special case where the variable name could be redefined and reused but only as a declaration within the else clause.

Update: Oops, no it's not, the decision is to limit the variable to the then-statement which allows it to be redeclared in the else-statement without shadowing.
Sep 8, 2014 at 11:51 AM
nmgafter wrote:
Yes, we've thought about it, and it won't work. Since Linq queries are defined by translation into method calls with lambda expressions, and variable-declaration-expressions are specified not to "leak" out of a lambda, this does not work. And if you think about the semantics, they could not possibly work since .Select might call its lambda without .Where's lambda ever being called.
This is true, but I don't think that it's that dissimilar to the syntax candy provided by the let clause. That's also why I mentioned that it might make sense to restrict the behavior to only within the let clause.

For what it's worth I'd like to see this revisited. There have already been people posting questions about this exact functionality in the forums, which sorta-kinda worked because of "spill-out". I've seen this sort of pattern used in production code, with a temporary variable declared outside of the query and used to carry the out parameter from a method, which is a brittle solution that I've seen broken unexpectedly with the introduction of parallelization or sorting.
var values = from key in keys
    let found = dict.TryGetValue(key, out int value) // quietly constructs an anonymous type { string key, bool found, int value }
    where found
    select new { key, value };
Sep 8, 2014 at 12:06 PM
PauloMorgado wrote:
Przemyslaw wrote:
The one exception to this are dummy names. But dummy names are, well, dummy. They are intended to be used as placeholders for things we don't want to refer after they are declared and eventually used (in the same expression). And to address such scenarios I would see something different in c#, e.g. single underscore:
That would be breaking change because _ is a valid simple name.

But, yes, it would be nice to have a "don't care about this" call argument.
you're right. I should choose other character in my samples.
Sep 8, 2014 at 12:11 PM
Edited Sep 8, 2014 at 12:16 PM
I thought the introduction of new variables via out was restricted to block the are defined in?`` Type Inference (via Greatest Common Type)
In the example below the variable V is both attempted to be defined as an Int and a Double. So would the inference engine infer the type to be Object?
As Object would be the greatest common type for Int and Double.
if (int.TryParse(s, out var v)) Console.WriteLine("Got integer {0}", v);
else if (double.TryParse(s, out var v)) Console.WriteLine("Got double {0}", v);  /* Would the type be object? as it the Greatest Common Type of int and double  ? */
else Console.WriteLine("Ain't got nuffink");
// Do you expect you can re-use the name "v" in both clauses?
if ((var v = o as string) != null) Console.WriteLine(v.Length);
else if ((var v = o as Exception) != null) Console.WriteLine(v.Message);
// Do you expect you can re-use the name "v" in both clauses?
on the scoping rule I eluded to wouldn't the compiler for the above code just be syntaxic sugar for the following rewrite?
/* start of IF BLOCK */
object v;

if ( int.TryParse( s, out var v ) ) 
{
  Console.WritleLine("Got Integer {0}",v) ;
}
else if (  double.TryParse( s, out var v) )  )
{
Console.WriteLine("Got double {0}", v);
}
else
{
Console.WriteLine("Ain't got nuffink");
}
/* end of IF BLOCK */
/* start of IF BLOCK */
// Do you expect you can re-use the name "v" in both clauses?
if ((var v = o as string) != null)
{
 Console.WriteLine(v.Length);
}
else if ((var v = o as Exception) != null)
{
 Console.WriteLine(v.Message);
// Do you expect you can re-use the name "v" in both clauses?
}
/* End of IF BLOCK */
Would another rules help?
  • If the introduced variable already exists, then try to use that first before create a new one..
  • If possible change the type of the introduced variable to the greatest common type.
  • Type Cast it to the type.
  • Already defined error
Sep 8, 2014 at 12:13 PM
PauloMorgado wrote:
This feature is about scoping down the declaration of variables when you don't want to pollute the outer scope, not an easier way to do it.
This is how it evolved, unfortunately. I may be wrong, but I think the whole scoping thing surfaced as an implementation detail. I think it wasn't the primary goal behind the feature in the beginning. At least it wasn't advertized this way.
Sep 8, 2014 at 12:39 PM
Edited Sep 8, 2014 at 12:50 PM
Why not just make it a compile error? Then suggest that they (the coder) use a variable in an outer scope?

An alternative way to look at is imagine that C# had no if statement but only match expression (akin to Nemerle)

Example using vb.net (CTP) Guarded Clauses, plus stealing C# declaration expression syntax)
Select Case True
  Case True When   Integer.TryParse( s, out var v )
    Console.WritleLine("Got Integer {0}",v) ;
  Case True When   Double.TryParse( s, out var v) )  
    Console.WriteLine("Got double {0}", v);
  Case Else
    Console.WriteLine("Ain't got nuffink");
End Select 
Firstly would it be valid? What you expect the code to do?
Sep 8, 2014 at 12:51 PM
nmgafter wrote:
Here's another interesting case
int i = int j = 12;
// i available here; j not available here
Interesting indeed. While it can be simply rewritten as:
int i = 12, j = 12;
Things get even more interesting for code like:
int i = ShouldBeEvaluatedOnce();
int j = i;
Previously, this code could be replaced with:
int i = int j = ShouldBeEvaluatedOnce();
Aside from which version is actually more readable, code cannot be compacted this way after recent changes.
Sep 8, 2014 at 1:10 PM
Edited Sep 9, 2014 at 12:56 PM
AdamSpeight2008 wrote:
I thought the introduction of new variables via out was restricted to block the are defined in?`` Type Inference (via Greatest Common Type)
In the example below the variable V is both attempted to be defined as an Int and a Double. So would the inference engine infer the type to be Object?
As Object would be the greatest common type for Int and Double.
if (int.TryParse(s, out var v)) Console.WriteLine("Got integer {0}", v);
else if (double.TryParse(s, out var v)) Console.WriteLine("Got double {0}", v);  /* Would the type be object? as it the Greatest Common Type of int and double  ? */
else Console.WriteLine("Ain't got nuffink");
// Do you expect you can re-use the name "v" in both clauses?
if ((var v = o as string) != null) Console.WriteLine(v.Length);
else if ((var v = o as Exception) != null) Console.WriteLine(v.Message);
// Do you expect you can re-use the name "v" in both clauses?
on the scoping rule I eluded to wouldn't the compiler for the above code just be syntaxic sugar for the following rewrite?
/* start of IF BLOCK */
object v;

if ( int.TryParse( s, out var v ) ) 
{
  Console.WritleLine("Got Integer {0}",v) ;
}
else if (  double.TryParse( s, out var v) )  )
{
Console.WriteLine("Got double {0}", v);
}
else
{
Console.WriteLine("Ain't got nuffink");
}
/* end of IF BLOCK */
/* start of IF BLOCK */
// Do you expect you can re-use the name "v" in both clauses?
if ((var v = o as string) != null)
{
 Console.WriteLine(v.Length);
}
else if ((var v = o as Exception) != null)
{
 Console.WriteLine(v.Message);
// Do you expect you can re-use the name "v" in both clauses?
}
/* End of IF BLOCK */
Would another rules help?
  • If the introduced variable already exists, then try to use that first before create a new one..
  • If possible change the type of the introduced variable to the greatest common type.
  • Type Cast it to the type.
  • Already defined error
Update: Oops, again, misread. No scoping in the else-statement, and no shadowing.

The rule is simply that you can reuse the variable name in the else clause (or a nested else clause) only if the variable is declared in the if clause. We're dealing with multiple variables here, each with their own strong type, they just reuse the same name in the scope where only one would be available. If the variable were to be defined in the outside scope in another manner that would be a compiler error. This cannot quite be described by C# syntax today, but it's something akin to the following:

string s = ...;
object o = ...;
// start of first IF block
{
    int v$1; // v defined for the first time here
    if (int.TryParse(s, out v$1)
    {
        Console.WriteLine("Got Integer {0}", v$1);
    }
    else
    {
        double v$2; // v redefined here as a double for the else-scope
        if (double.TryParse(s, out v$2)
        {
            Console.WriteLine("Got Double {0}", v$2);
        }
        else
        {
            Console.WriteLine("Got nothing.");
        }
    }
}
// end of first IF block

// start of second IF block
{
    string v$1 = o as string;
    if (v$1 != null)
    {
        Console.WriteLine(v$1.Length);
    }
    else
    {
        Exception v$2 = o as Exception;
        if (v$2 != null)
        {
            Console.WriteLine(v$2.Message);
        }
    }
}
// end of second IF block
Sep 8, 2014 at 1:32 PM
AdamSpeight2008 wrote:
Why not just make it a compile error? Then suggest that they (the coder) use a variable in an outer scope?

An alternative way to look at is imagine that C# had no if statement but only match expression (akin to Nemerle)

Example using vb.net (CTP) Guarded Clauses, plus stealing C# declaration expression syntax)
Select Case True
  Case True When   Integer.TryParse( s, out var v )
    Console.WritleLine("Got Integer {0}",v) ;
  Case True When   Double.TryParse( s, out var v) )  
    Console.WriteLine("Got double {0}", v);
  Case Else
    Console.WriteLine("Ain't got nuffink");
End Select 
Firstly would it be valid? What you expect the code to do?
It looks like the proposal for VB.NET type cases and guarded clauses already permit variable re-use. Of course those both apply to Select Case for which C# really has no equivalent. Apart from the very limited comparisons available today the scoping rules are also different in that the entire switch shares a single scope across all of the case labels. I've not seen any syntax proposal for declaration expressions for VB.NET and it's still in the "Maybe" category on the Language Status page.

As for pattern matching, the proposal for C# seems to allow what you describe.
object o = ...;
switch (o)
{
    case Square(int length):
        Console.WriteLine("Square with a length of {0}.", length);
        break;
    case RectangleF(float length, float width):
        Console.WriteLine("Rectangle with a length of {0}.", length);
        break;
}
Sep 8, 2014 at 1:34 PM
Edited Sep 8, 2014 at 1:40 PM
@Halo_Four

Sounds like the variables are lifted into closures.

nemerle-like
 
var _ifcond_ = ()=>
  {
   int v;
   var _if_   = ()=>{ console.WriteLine("Got Integer {0}",v); };
   var _else_ = ()=>{
                      double v;
                      var _if_   = ()=>{ Console.WriteLine("Got double {0}", v);};
                      var _else_ = ()=>{ Console.WriteLine("Got nothing");};
                      match ( Double.TryParse( s, v ) ) with
                      {
                        | true  => _if_();
                        | false => _else_();
                      }
                    };
   match( Integer.TryParse( s, v ) ) with
   { 
     | true  => _if_();
     | false => _else_();
   };
 }
_ifcond_();
edit: formatting code.
Sep 8, 2014 at 3:34 PM
Edited Sep 8, 2014 at 3:35 PM
Halo_Four wrote:
The rule is simply that you can reuse the variable name in the else clause (or a nested else clause) only if the variable is declared in the if clause.
But the variable isn't being declared in any of the clauses but is being declared in condition expression. which isn't the same scoping block of either clause.
c# grammar
if_statement ::=
if ( boolean_expression ) embedded_statement
if ( boolean_expression ) embedded_statement else embedded_statement
In LISP-like land the two statements are.
lisp
[IF [ cond : Expr<bool> ] [ true_clause : Expr ] ]
[IF [ cond : Expr<bool> ] [ true_clause : Expr ] [ false_clause : Expr ] ]

If the if-statement can only accept an Boolean Expression (Expr<Bool>) as it condition. eg It can be anything so long as it evaluates to a Boolean.
Which implies
if (int.TryParse(s, out var v)) 
{
 Console.WriteLine("It is an Integer: {0}",v );
}
is turn into this.
if( { int v;
      return Int.TryParse( s , v );
    }.Invoke()
  )
{
  Console.WriteLine("It is an Integer: {0}",v );
}
Which as mean the life time of the variable v is restricted to only the condition., hence needs to be captured in a closure for usage in the other clauses.
Developer
Sep 8, 2014 at 3:38 PM
Skiminok wrote:
nmgafter wrote:
Halo_Four wrote:
So, as it stands, the following would be legal C# 6.0, correct?
Yes
Wait, I am confused. How can it be legal? According to the latest change of "if-else" scoping rules (point #2 of this thread), value should not be accessible in the second else block, hence the assertion value is int && value <= 5 would fail to compile. Am I missing something?
No, you're right. And its good that it doesn't compile, as there is no reliable value in the variable "value".
Sep 8, 2014 at 3:53 PM
Edited Sep 8, 2014 at 4:04 PM
No, the more correct conversion is:
string s = "123";

// beginning of IF statement
{
   int v;
   if (int.TryParse(s, out v))
   {
       Console.WriteLine("It is an Integer: {0}", v);
   }
}
// end of IF statement
// v no longer in scope
The constructs of if and scoping are syntax candy provided by C#, the underlying IL being much more primitive. The decisions here state that the declaration of v within the if clause will allow v to be remain in scope within the then-statement. The local exists for the entire duration of the method, as all locals do (except in the situation of closure capture, of course). If the variable v is reused, either in a completely unrelated scope a completely different local might be defined in the IL depending on the type. The compiler may decide to reuse a local of the same type for two different scopes.

The IL for both current C# and C# 6.0 with declaration expressions will very likely be identical, it's only the compiler semantics which are changing.
Sep 8, 2014 at 4:00 PM
Edited Sep 8, 2014 at 4:17 PM
nmgafter wrote:
Skiminok wrote:
nmgafter wrote:
Halo_Four wrote:
So, as it stands, the following would be legal C# 6.0, correct?
Yes
Wait, I am confused. How can it be legal? According to the latest change of "if-else" scoping rules (point #2 of this thread), value should not be accessible in the second else block, hence the assertion value is int && value <= 5 would fail to compile. Am I missing something?
No, you're right. And its good that it doesn't compile, as there is no reliable value in the variable "value".
Oof, I do need to bone up on my reading comprehension. I took that section to mean that the decision was that shadowing would be permitted in the limited scenario, not that the variable would fall out of scope in the else-statement. So then the following code would be legal?
if (dict.TryGetValue(key, out var value))
{
    Console.WriteLine("The value exists and is {0}", value);
}
else
{
      // value doesn't exist here, we can define a new variable in this scope
    var value = 1234;
    dict.Add(key, value);
}
Sep 8, 2014 at 5:40 PM
Edited Sep 8, 2014 at 6:25 PM
Why not have another level of scope? That doesn't extend to enclosing block.

Basic Structural Layout
if {  // start of IF-BLOCK
   // introduced variables   note: may be needs enclosing { } ?
     ( cond  ) { // if clause   }
       else  { // else clause   }
   } // end of IF-BLOCK
Example
if
{ // start of IF-BLOCK
  // introduced variables
  int x;
  ( Int.TryParse( s, x ) )
  {
    Console.WriteLine( "Integer {0}", x );
  }
  else 
  {
    if
    { // start of IF-BLOCK
      // Introduce Variables
      double x;
      ( Double.TryParse( s, x ) )
      {
        Console.WriteLine("You got a double {0}", x );
      }
      else
      {
        Console.WriteLine("You got nowt!");
      }
    } // end of IF-BLOCK
  }
} // end of IF-BLOCK
Now the introduce variables are:-
  • only scoped to within the if-block
  • visible to inner if-block ( else if ( ) )
  • only alive within the if-block
  • possible to specially derestrict the no shadowing of local variables rule to only if-block
    usage of an introduced variable binds to most locally scoped definition first.
Edit Added
  • The brace of the if-block and introduce variables sections are not visible to the user.
  • This is what the compiler can write. unutterable code
Sep 8, 2014 at 5:54 PM
My opinion? Excessive, verbose and difficult to follow. Not to mention there is already the mechanism in C# to support this kind of variable scoping by declaring an arbitrary code block.
{
    double x;
    if (double.TryParse(s, out x))
    {
        // x is in scope here
    }
    else
    {
        // x is in scope here
    }
    // x is in scope here
}
// x is not in scope here
Note, for my previous comments on this thread I had misread the meeting notes and thought that the scoping of the declaration expressions also extended to the else-statement but that is not the case and additional declaration expressions in a nested if clause within the else-statement are not shadowing the previous declaration.
Sep 8, 2014 at 6:19 PM
@Halo_Four
I should explained that my code is what the compiler can write, not what a coder wouldn't be able to write code like that
It is a bit a like anonymous types, let's say it's called unutterable code / cthulhu code
Sep 8, 2014 at 6:42 PM
AdamSpeight2008 wrote:
@Halo_Four
I should explained that my code is what the compiler can write, not what a coder wouldn't be able to write code like that
It is a bit a like anonymous types, let's say it's called unutterable code / cthulhu code
Oh, well, in that case you're right. The following is probably as close as you'll get in describing the behavior using legal C# 5.0 code, and boy is it ugly.
// C# 6.0 Roslyn
// start if statement
if (int.TryParse(s, out int value))
{
    Console.WriteLine("Got integer {0}", value);
}
else if (double.TryParse(s, out double value))
{
    Console.WriteLine("Got double {0}", value);
}
else
{
    Console.WriteLine("Ain't got nuttin'");
}
// end if statement

// C# 5.0
// start if statement
{
    int value;
    bool temp = int.TryParse(s, out value);
    if (!temp) goto else_statement_1;
    Console.WriteLine("Got integer {0}", value);
    goto end_if_statement;
}
else_statement_1:
{
    double value;
    bool temp = double.TryParse(s, out value);
    if (!temp) goto else_statement_2;
    Console.WriteLine("Got double {0}", value);
    goto end_if_statement;
}
else_statement_2:
{
    Console.WriteLine("Ain't got nuttin'");
    goto end_if_statement;
}
end_if_statement:
// end if statement
Sep 8, 2014 at 8:40 PM
gzak wrote:
Another comment. If the main use case which this feature unlocks is inlining out/ref parameters, wouldn't it be better to think of introducing tuple decomposition as an alternative means of achieving the same goal?
It would have been nice if the Framework and languages had been designed to facilitate at least some form of tuple decomposition from the start e.g. (allowing functions to have a list of return types, rather than just a single type). As it is, however, functions used within generic interfaces can return a single value covariantly, but that's it. For example, while Dictionary<Key,Value> doesn't use this pattern (the examples behave as though it does, but some other type which does could be used instead), it would be possible to have ISearchableDictionary<in TKey, out TValue> inherit from ISearchableDictionary<out TValue>, and have them include methods:
TValue TryGetValue(TKey key, out bool Success);  // ISearchableDictionary<in TKey, out TValue>
TValue TryGetValue<TTKey>(TTKey key, out bool Success);  //  ISearchableDictionary<out TValue>
and then take advantage of .NET's support for covariance and contravariance. The second interface and type are necessary to allow for the legitimate possibility that code might e.g. want to look up Animal references in a Dictionary<Cat, Int32>; asking whether a Dictionary<Cat, integer> contains an instance of Dog shouldn't cause it to throw an "invalid type" exception; instead, it should simply indicate that the aforementioned object is not in the collection.

Note that with the TryGetValue methods defined as shown, there is no need for boxing when passing things into or out of the method. Even if one were to ask whether a Dictionary<Cat, Int32> contained the double value 3.7, it wouldn't have to do any boxing; it could simply observe that TTKey is a value type but TKey isn't.

If the interface methods could return a bool and a covariant TValue, that would be more elegant than relegating the bool to an out parameter, but the only way that could work within the Framework would be to have TryGetValue marked with an attribute that would instruct the compiler to allow something like:
if ({*,var value} = dict.TryGetValue(key) { ... do something with value ... }
to be used as syntax sugar for:
bool ok = false; // Allow for the possibility that an implementation of `TryGetValue` written in a non-C# language might not write it
var value = dict.TryGetValue(key, out ok);
if (ok) { ... do something with value ... }
Since the latter would support covariance, the former could just as well even though any type which could encapsulate a bool and a covariant TValue would require an extra heap allocation for every instance (like boxing, but the overhead would apply to reference types as well as value types).
Developer
Sep 8, 2014 at 10:17 PM
Halo_Four wrote:
So then the following code would be legal?
if (dict.TryGetValue(key, out var value))
{
    Console.WriteLine("The value exists and is {0}", value);
}
else
{
      // value doesn't exist here, we can define a new variable in this scope
    var value = 1234;
    dict.Add(key, value);
}
Yes.
Developer
Sep 8, 2014 at 10:26 PM
gzak wrote:
Another comment. If the main use case which this feature unlocks is inlining out/ref parameters, wouldn't it be better to think of introducing tuple decomposition as an alternative means of achieving the same goal? Elsewhere I've seen comments for an automatic syntax sugar which treats out parameters simply as additional return values
I do not think that treating out parameters as some kind of value on par with returned values is a good idea. I do think it would be worth adding better support for tuples, decomposition, and the related feature of pattern-matching, but that is too large a design area to be undertaken in the current round of changes. As long as these existing APIs exist, programmers will benefit from simpler ways of using them. Meanwhile, since we are adding scopes for variables introduced by declarations appearing in expressions, we should be sure those scope rules work well for tuple decomposition and pattern matching etc, as those features similarly introduce variables in expressions. That was one consideration that led to these latest scope rule changes.
Sep 8, 2014 at 11:46 PM
nmgafter wrote:
I do not think that treating out parameters as some kind of value on par with returned values is a good idea. I do think it would be worth adding better support for tuples, decomposition, and the related feature of pattern-matching, but that is too large a design area to be undertaken in the current round of changes. As long as these existing APIs exist, programmers will benefit from simpler ways of using them. Meanwhile, since we are adding scopes for variables introduced by declarations appearing in expressions, we should be sure those scope rules work well for tuple decomposition and pattern matching etc, as those features similarly introduce variables in expressions. That was one consideration that led to these latest scope rule changes.
Is this to say that the decision has been made not to include records/pattern-matching in C# 6.0? Even with your prototype already implemented?
Sep 9, 2014 at 12:07 AM
MgSam wrote:
Is this to say that the decision has been made not to include records/pattern-matching in C# 6.0? Even with your prototype already implemented?
As far as I understand, nealgafter's proposal is just this — a proposal. It isn't even being considered by the language committee at the moment. Its purpose is to be a prototype, which people can play with, review, and clean up to make look more natural and consistent. In some future it may be brought to the language committee table, when it's mature enough, but no one ever guaranteed us this, especially as early as C# 6.0.
Sep 9, 2014 at 11:16 AM
It strikes me that trying to work the scoping issue (which is definitely an issue for readability, though I'd argue not a big one) is introducing a lot of unintuitive bityness, and it feels like it's an attempt to salvage something that isn't actually broken with a focus on tidying up the common use of dummy out parameters (COM etc.), and as a result greatly limiting the usefulness of this feature without fixing the readability issue (I can never get behind local variable shadowing, and when you throw var into the mix it only gets worse, it's pain enough mixing up scopes with object initializers).

I feel it would be better to accept that it isn't immediately clear that a variable is in scope (because it has spilled out) and to provide an alternate solution for dummy out parameters, which seem to be one of the more emphasized reasons why the spill out is undesirable. I wouldn't consider variables you care about clashing as a problem with the language to be a real issue, because if you want to pour an out parameter into the same variable twice then you are either mixing the meaning of the variable, should be giving more meaningful names to your variables, or it probably makes more sense to declare it traditionally and make the absolutely scope clear.

I can appreciate the potential confusion for people who aren't familiar with the language, but it can't be worse than confusing what else if means for everyone. The proposed syntax isn't a good solution to dummying out parameters, and it doesn't achieve much more, while convolution if statements. I'd much rather not have this capability than this suggested subset of functionality.
Sep 9, 2014 at 12:53 PM
Skiminok wrote:
As far as I understand, nealgafter's proposal is just this — a proposal. It isn't even being considered by the language committee at the moment. Its purpose is to be a prototype, which people can play with, review, and clean up to make look more natural and consistent. In some future it may be brought to the language committee table, when it's mature enough, but no one ever guaranteed us this, especially as early as C# 6.0.
I thought that the original spec document said that it was for consideration in the next dev cycle- however, it's been changed now so perhaps I'm misremembering.
Sep 9, 2014 at 1:24 PM
Edited Sep 9, 2014 at 1:25 PM
nmgafter wrote:
Skiminok wrote:
nmgafter wrote:
Halo_Four wrote:
So, as it stands, the following would be legal C# 6.0, correct?
Yes
Wait, I am confused. How can it be legal? According to the latest change of "if-else" scoping rules (point #2 of this thread), value should not be accessible in the second else block, hence the assertion value is int && value <= 5 would fail to compile. Am I missing something?
No, you're right. And its good that it doesn't compile, as there is no reliable value in the variable "value".
I would argue that there is a reliable value in the variable value as that is guaranteed by the fact that the method returned without throwing. The fact that the method returned a value that could also be used in the condition which would then evaluate to true is a secondary concern. As mentioned, sometimes that condition would be to evaluate the inverse of the result using one of the various Try* methods. The method could return something entirely unrelated to the out parameters. I imagine that many of these are edge cases that have been discussed/dismissed.

Note that I'm sort of on the fence about the behavior and I'm not advocating for a position as much as I am trying to work through the pros and cons on both sides, which I imagine that the team has already done and it would be interesting to get further insight.

If this would be up for reconsideration I wouldn't advocate for any changes to scoping. Even though I liked the look of the very limited shadowing I think that it's dangerous to start adding footnotes into what is a well-established behavior in the language. If the declared variable would survive in scope through both the then-statement and else-statement I would say that nested declarations would have to use a new name. The inherited legacy from C/C++ is that the scope exists through both then-statement and else-statement, but then C/C++ was much more liberal about shadowing.
Sep 9, 2014 at 3:59 PM
Halo_Four wrote:
If this would be up for reconsideration I wouldn't advocate for any changes to scoping. Even though I liked the look of the very limited shadowing I think that it's dangerous to start adding footnotes into what is a well-established behavior in the language.
The purpose of the rule against shadowing is to prevent this situation:
void blah()
{
  int x;
  x=5;
  using(whatever)
  {
    int x;
    ...
    x=57;
  }
  y = x;       // What is the value of x here?
}
It looks like the using statement assigned a value of 57 to x, but the x it assigns is not the same x as is used in the last line. I would suggest that if use of a variable within an inner scope would cause the value of a similarly-named variable defined in an outer scope to be considered indeterminate, then shadowing which would create ambiguity (as above) would still be prohibited by the rule requiring that variables be definitely assigned before they are read, but shadowing which would not create ambiguity would be allowed.
Sep 9, 2014 at 4:48 PM
Edited Sep 9, 2014 at 4:52 PM
@supercat
var res = from x in xs
          where x >= 0
          select x
Are also not the same x on each line, but coders don't seem to have an issue with that.

The ide would also show (via highlighting) which x is assigned to y.
void blah()
{
  int x; // Highlighted
  x=5; // Highlighted
  using(whatever)
  {
    int x;
    ...
    x=57;
  }
  y = x;  // Highlighted
}
Rule: Most locally defined variable (eg x) is used first.

Limited Shadowing allowed only for "introduced variables" eg value below.
if (dict.TryGetValue(key, out var value))
So you (the coder) are aware of the possibility that shadowing could result.
Sep 9, 2014 at 5:37 PM
Edited Sep 9, 2014 at 6:59 PM
supercat wrote:
Halo_Four wrote:
If this would be up for reconsideration I wouldn't advocate for any changes to scoping. Even though I liked the look of the very limited shadowing I think that it's dangerous to start adding footnotes into what is a well-established behavior in the language.
The purpose of the rule against shadowing is to prevent this situation:
void blah()
{
  int x;
  x=5;
  using(whatever)
  {
    int x;
    ...
    x=57;
  }
  y = x;       // What is the value of x here?
}
It looks like the using statement assigned a value of 57 to x, but the x it assigns is not the same x as is used in the last line. I would suggest that if use of a variable within an inner scope would cause the value of a similarly-named variable defined in an outer scope to be considered indeterminate, then shadowing which would create ambiguity (as above) would still be prohibited by the rule requiring that variables be definitely assigned before they are read, but shadowing which would not create ambiguity would be allowed.
Oh I know, and I agree that preventing such shadowing in C# is a good thing despite the C heritage. The "limited shadowing", as proposed (and rejected) as option 3 for the "if-else" scenario only applied if the declared variable remained in scope through both the then-statement and the else-statement and only permitted the variable to be shadowed as a declaration expression within the nested if statement, e.g.
if (dict1.TryGetValue(key, out string value))
{
    Console.WriteLine("Value is a string: \"{0}\".", value);
}
else if (dict2.TryGetValue(key, out int value) && value > 0)) // int value shadows string value
{
    Console.WriteLine("Value is a positive integer: {0}", value);
}
else // int value still in scope
{
    Console.WriteLine("Value is either not in either dictionary or is a negative integer: {0}", value);
}
Of course this only would've mattered if the declared value remained in scope in the else-statement which is not the case. If it was and this shadowing exception were not permitted the above would not compile as the string value would be shadowed by the int value. That variable would require a different name.

It's an interesting intersection to me as both features are rooted in C but the design decisions of the C# language force it to be treated differently.
Sep 9, 2014 at 5:56 PM
AdamSpeight2008 wrote:
@supercat
var res = from x in xs
          where x >= 0
          select x
Are also not the same x on each line, but coders don't seem to have an issue with that.
That's a completely different case: x is declared only once (in the from clause). Sure, when it's expanded to extension methods, it's a different x every time, but it's logically the same, and will actually take the same values.
Developer
Sep 9, 2014 at 10:17 PM
MgSam wrote:
I thought that the original spec document said that it was for consideration in the next dev cycle- however, it's been changed now so perhaps I'm misremembering.
Next, as in after the current cycle. This might affect the language in 18 to 24 months. Or it might not.
Sep 10, 2014 at 2:18 PM
nmgafter wrote:
Next, as in after the current cycle. This might affect the language in 18 to 24 months. Or it might not.
Ah, ok. Thanks. Maybe you should make that more explicit in the forum post and draft spec? I'm probably not the only one who misunderstood the timing.
Sep 13, 2014 at 2:18 PM
I don't feel that the "Else-If" is a problem. What's the problem with it?

Making a special change to how the scoping works makes it "look" like else isn't part of if, but in reality, it is, and this just makes everything more confusing.

If I had a vote, I would vote to leave it as it was, where a variable declared in the if expression is still in scope of the else block.
Sep 13, 2014 at 3:58 PM
bondsbw wrote:
I don't feel that the "Else-If" is a problem. What's the problem with it?

Making a special change to how the scoping works makes it "look" like else isn't part of if, but in reality, it is, and this just makes everything more confusing.

If I had a vote, I would vote to leave it as it was, where a variable declared in the if expression is still in scope of the else block.
Same here, the problematic example:
if ((var i = o as int?) != null) { … i … }
else if ((var s = o as string) != null) { … s … }
else if
Most of the time can be rewritten to return in every if block, and then you don't have the problems because you don't have else:).
if ((var i = o as int?) != null) { … return i … }

if ((var s = o as string) != null) { … return s … }
Sep 13, 2014 at 5:05 PM
bondsbw wrote:
I don't feel that the "Else-If" is a problem. What's the problem with it?
Just to be sure everyone is aware of this: there's no such thing as else-if in C#.

There is such a construct in Visual Basic but not in C#.
Sep 13, 2014 at 5:30 PM
Edited Sep 13, 2014 at 5:35 PM
What if these kind of block-based constructs inherited a common base., which can contains the introduced variables of that block.
Block
  .IntroducedVariables : IE<Variable>

ExprBlock <: Block
  .Code    : Expr

CondExpr
  .Cond    : Expr<Bool>
  .Code    : ExprBlock

IFBlock_VB <: Block
  .IF_Cond : CondExpr
  .ElseIfs : IE< CondExpr >?
  .Else    : ExprBlock?

IFBlock_C# <: Block
  .If_Cond : CondExpr
  .Else    : ( IFBlock | ExprBlock )?

if ( int.TryParse( s, out int x ) )   { }
else if ( Double.TryParse( s, out int d ) )   { }
else  { }
In the scheme the introduced variable x is defined within the IfBlock.IntroducedVariable. The introduced variable d is defined within the inner IfBlock.IntroducedVariable. x is available in all code blocks. d is only available on the inner if and else code block.

Because of where the variable is introduced it can also be utilised within the conditional of the if . eg
if ( int.TryParse( s, out int x ) || ( x > 0 ) )    { }

This idea of a block can be extend to cover other block-base constructs
Method <: Block
  .Name   : Method_Identifier
  .Params : ParameterList
  .Code   : ExprBlock

Function <: Method
  .ReturnType : TypeIdentifier
Sep 13, 2014 at 5:32 PM
I never thought I would ever say that, but C# is getting quite complicated. Hell C# 9 might get as complicated and loaded with history as C++!
Or is it just me getting older?
Is there an option of creating new language\CLR that is to C# as C# to java and C++? :) I mean I think one day, may be 20 years from now I will happen anyway, why not now?
Sep 15, 2014 at 1:21 PM
marhoily wrote:
Is there an option of creating new language\CLR that is to C# as C# to java and C++? :) I mean I think one day, may be 20 years from now I will happen anyway, why not now?
C# has a number of places where parts of the language made compromises for simplicity, but then other parts have abandoned the simplicity for which those compromises were made. For example, in the early days of the language, it was decided that the types of expressions must be determined without regard for the context in which they appear; when lambdas were added to the language, context-dependent type evaluation was added for them (but only for them). It would not be possible to change the way types are evaluated in other contexts without breaking some existing code, but a dialect with more helpful type-evaluation rules could be helpful going forward. Under existing rules, a statement like someLong = someInt * 2; may, if someInt equals 2,000,000,000, either set someLong to -294967296 or throw a run-time exception (depending upon whether arithmetic is checked or unchecked) but I would posit that it would be more helpful to either set someLong to 4,000,000,000 or else require that the result of an arithmetic operation which could overflow or wrap be explicitly cast to int or Int32 before it is converted to any other type, and refuse compilation of it is not. A compiler for the existing dialect of C# couldn't do either of those new behaviors, but one for a new dialect could.
Sep 17, 2014 at 6:15 PM
Przemyslaw wrote:
IMHO, removing the spill out and changing the if scope rules are both very sad moves.

As I understand, declaration expression were introduced to simplify usage of out variables. And now lack of spill out and changed if scope makes it nearly impossible to use declaration variables together with out arguments. The purpose of "declaration" is to use it later after all. If I cannot use declared variable after it was declared, then what is the point of the whole feature?
If I cannot write:
if (!int.TryParse(stringValue, out var count))
  throw new FooException("blah");

// use count variable
or
if (!dictionary.TryGetValue(key, out var cached))
{
  cached = new Foo();
  dictionary.Add(key, cached);
}

// use cached here
then the feature is unusable for me. And the fact I am pretty much restricted with my refactorings (cannot reverse if condition) doesn't help here either. For me it looks like you want to fix something which is perfectly working now.
I completely agree with you, the usefulness of the feature has been cut in half.
if and else should stay symmetric. Inverting the logic flow shouldn't change how the variables are declared.

So we're left with either "old-style" declaration before the if loop, or increased nesting just for the sake of using the feature.
Sep 17, 2014 at 10:18 PM
MrJul wrote:
I completely agree with you, the usefulness of the feature has been cut in half.
if and else should stay symmetric. Inverting the logic flow shouldn't change how the variables are declared.
I wish more languages would have different syntax for "I want to declare a variable whose value will initially be X, but might change", versus "I want to declare a storage location which will hold X for as long as it is exists" [executing such a statement within a loop would, semantically, create a new storage location each time and abandon the old one, but if the location wasn't captured by a closure the compiler could as an implementation detail simply keep reusing the same one]. I would see the exporting of the latter kind of identifiers from expressions or other scoping blocks as appropriate, but not the former. Unfortunately, without a syntax to distinguish the two uses, I don't see a nice way to allow the latter without the former causing problems.

Perhaps the best that could be done would be to apply the as-yet-unused reserved word "extern" to mark declarations which should be applied in the enclosing scope. A keyword "export" might be better, but "extern" would have the advantage that because it is a reserved word it shouldn't create any parsing ambiguities.
Sep 18, 2014 at 1:18 AM
supercat wrote:
MrJul wrote:
I completely agree with you, the usefulness of the feature has been cut in half.
if and else should stay symmetric. Inverting the logic flow shouldn't change how the variables are declared.
I wish more languages would have different syntax for "I want to declare a variable whose value will initially be X, but might change", versus "I want to declare a storage location which will hold X for as long as it is exists" [executing such a statement within a loop would, semantically, create a new storage location each time and abandon the old one, but if the location wasn't captured by a closure the compiler could as an implementation detail simply keep reusing the same one]. I would see the exporting of the latter kind of identifiers from expressions or other scoping blocks as appropriate, but not the former. Unfortunately, without a syntax to distinguish the two uses, I don't see a nice way to allow the latter without the former causing problems.
You mean like let in Apple Swift? I would like something like a readonly variable in C#, and let seems like the perfect partner to var. I think the same scoping concerns would apply.
Perhaps the best that could be done would be to apply the as-yet-unused reserved word "extern" to mark declarations which should be applied in the enclosing scope. A keyword "export" might be better, but "extern" would have the advantage that because it is a reserved word it shouldn't create any parsing ambiguities.
The extern keyword is currently used in two places in C#, to denote a method implemented externally, such as with P/Invoke, or combined with alias in order to bring a reference outside of the global namespace into scope. I don't really like the idea of being able to declare and "export" a variable, though. You can already move the declaration in order to expand its scope. If the location of the declaration became irrelevant to the effective scope I think that would become very confusing.
Sep 18, 2014 at 1:16 PM
Edited Sep 18, 2014 at 1:17 PM
MrJul wrote:
Przemyslaw wrote:
IMHO, removing the spill out and changing the if scope rules are both very sad moves.

As I understand, declaration expression were introduced to simplify usage of out variables. And now lack of spill out and changed if scope makes it nearly impossible to use declaration variables together with out arguments. The purpose of "declaration" is to use it later after all. If I cannot use declared variable after it was declared, then what is the point of the whole feature?
If I cannot write:
if (!int.TryParse(stringValue, out var count))
  throw new FooException("blah");

// use count variable
or
if (!dictionary.TryGetValue(key, out var cached))
{
  cached = new Foo();
  dictionary.Add(key, cached);
}

// use cached here
then the feature is unusable for me. And the fact I am pretty much restricted with my refactorings (cannot reverse if condition) doesn't help here either. For me it looks like you want to fix something which is perfectly working now.
I completely agree with you, the usefulness of the feature has been cut in half.
if and else should stay symmetric. Inverting the logic flow shouldn't change how the variables are declared.

So we're left with either "old-style" declaration before the if loop, or increased nesting just for the sake of using the feature.
Well let's not forget the fact that it will also make this statement unusable now.
var isAmountValid = int.TryParse("123",out  var amount);
We are using that a lot, since we need to evaluate around 5-6 conditions like this to perform the calculations and same type of data comes in different fields from the county data feeds. Creating nested if statements will be possible, but not readable or maintainable, since it doesn't map to business logic rules. It might look like anti-pattern, but it's not.
Sep 18, 2014 at 5:16 PM
Halo_Four wrote:
You mean like let in Apple Swift? I would like something like a readonly variable in C#, and let seems like the perfect partner to var. I think the same scoping concerns would apply.
From the tiny bit I know of Swift, yes.
You can already move the declaration in order to expand its scope. If the location of the declaration became irrelevant to the effective scope I think that would become very confusing.
How about allowing readonly var identifier; or readonly typename identifier; to be used in an outer scope if and only if the next executable statement would cause its value to be written on every possible code path; for things of type var, all values written must be the same exact type, and in any case the value could only be written in contexts where no previous value may have been observed. That might offer the best of all worlds. The identifier would be declared in its proper scope, but it would be clear that all places where the identifier's value could be observed would observe it as having the same value.
Sep 18, 2014 at 5:28 PM
dmikov wrote:
Well let's not forget the fact that it will also make this statement unusable now.
var isAmountValid = int.TryParse("123",out  var amount);
That's a nasty pattern anyway, which was developed before covariant interfaces and var became available. If the outputs are swapped, it could become:
bool ok;
...
var amount = int.AttemptParse("123", out ok);
if (ok) ...
Not only would this allow var to work, but it would also allow a covariant interface to contain an AttemptXX method which returns the generic type.
Sep 19, 2014 at 12:27 AM
supercat wrote:
Halo_Four wrote:
You mean like let in Apple Swift? I would like something like a readonly variable in C#, and let seems like the perfect partner to var. I think the same scoping concerns would apply.
From the tiny bit I know of Swift, yes.
You can already move the declaration in order to expand its scope. If the location of the declaration became irrelevant to the effective scope I think that would become very confusing.
How about allowing readonly var identifier; or readonly typename identifier; to be used in an outer scope if and only if the next executable statement would cause its value to be written on every possible code path; for things of type var, all values written must be the same exact type, and in any case the value could only be written in contexts where no previous value may have been observed. That might offer the best of all worlds. The identifier would be declared in its proper scope, but it would be clear that all places where the identifier's value could be observed would observe it as having the same value.
...
to be used in an outer scope if and only if the next executable statement would cause its value to be written once and only once on every possible code path
If this was an option for declaration with initialization or as an out/ref parameter, it would be great.
Sep 19, 2014 at 1:05 AM
PauloMorgado wrote:
If this was an option for declaration with initialization or as an out/ref parameter, it would be great.
I would expect the normal usage pattern to be to declare the name of the variable before the if containing the out expression; the out expression would then be considered the assignment that sets it.

BTW, I don't know if C# syntax could unambiguously allow
define x;
define typeName x;
define x = valueExpr;
define typeName x = valueExpr;
without creating any ambiguity for any possible meaning of identifier define, but the more I think about it, the more I like the idea of having such a thing, especially if def would cause any previous meaning the name might have had to be unavailable for the balance of the method. Code could legitimately do:
  define String foo = "George";
  define var bar = foo + " Bailey";
  define var foo;
  if (condition)
    foo = 1;
  else
    foo = 2;
  boz = foo*3;
without any ambiguity, but could not do...
    define var foo = something;
    ...
  }
  foo.doSomething();l
}
no matter where or how foo had been defined previously, since the define in the latter examine would invalidate any outer-scope definitions. BTW, I'd like the syntax better if define var x could be shortened to define x, but since C# does not alas use any useful punctuation or reserved words in most variable declarations, such a statement would be indistinguishable from the declaration of a variable x of type define.
Sep 19, 2014 at 1:11 AM
Edited Sep 19, 2014 at 11:17 AM
I would not like that variables could be redefined.
Sep 19, 2014 at 9:53 AM
PauloMorgado wrote:
I would like that variables could be redefined.
Yes, this would be ultimate solution.


And regarding var vs let vs readonly. I don't find using let to declare readonly locals as good. We have readonly keyword already - IMHO it should be possible to use readonly with local declaration. The same way we can currently use const. So readonly var x = GetValue(); is the way to go IMHO.

I could find let useful to drive scope. It could be used to declare variables scoped to current statement only. Similar as it works in LINQ now. And yes, it would mean you could write readonly let or const let as well. So - var or let would be about scoping, while const and readonly would be about mutability.

This way we could write code
if (int.TryParse(stringValue, out var intValue))
{
  // intValue is visible here
}
else
{
  // intValue is visible here
}
or
if (int.TryParse(stringValue, out let intValue))
{
  // intValue is visible here
}
else
{
  // intValue is NOT visible here
}
So IMHO var should still have its spill out capability. If one wants to define strictly scoped local, could use let instead. Best of two worlds, I think
Sep 19, 2014 at 11:20 AM
Przemyslaw wrote:
PauloMorgado wrote:
I would like that variables could be redefined.
Yes, this would be ultimate solution.
I should have been already in when I replied.

I meant: I would not like that variables could be redefined.
Sep 19, 2014 at 11:22 AM
Przemyslaw wrote:
if (int.TryParse(stringValue, out let intValue))
{
  // intValue is visible here
}
else
{
  // intValue is NOT visible here
}
The if-else statement is a single statement. Scoping rules should apply to the whole statement.
Sep 19, 2014 at 2:01 PM
PauloMorgado wrote:
I would not like that variables could be redefined.
When variables are defined "as variables", their values may be reassigned at will, and any read access may be reachable from any write access. The code:
Thing x;
x=createFirstThing();
doSomethingWith(x);
x=createSecondThing();
doSomethingElseWith(x);
is a very common pattern, but the "x" in the last line isn't really the same as the "x" that's assigned in the first. Alternatives might be:
{
  Thing x=createFirstThing();
  doSomethingWith(x);
}
{
  Thing x=createSecondThing();
  doSomethingElseWith(x);
}
or
Thing x1=createFirstThing();
doSomethingWith(x1);
Thing x2=createSecondThing();
doSomethingElseWith(x2);
The first alternate, however, has the disadvantage of not only adding visual clutter, but also not working if variables have overlapping lifetimes. The second alternate makes it look as though x1 and x2 have overlapping lifetimes, which they don't. I would posit that being able to reuse the name to refer to a different variable in cases where it wouldn't semantically matter whether it was the same variable or not would allow programs to be more expressive. The C# rule forbidding reuse of an identifier from an immediate outer scope was IMHO an ad hoc fix to avoid the possibility that exiting a scope would bring an old variable back into view; I would posit that if the possibility of bringing an old variable back into view is the problem, the solution is to have an inner-scope declaration invalidate outer-scope variables for the remainder of the method. Not really possible with existing-style variable declarations without a code-migration strategy, write-once variables need not be bound by the same rules as other identifiers.
Sep 21, 2014 at 10:14 PM
supercat wrote:
is a very common pattern, but the "x" in the last line isn't really the same as the "x" that's assigned in the first. Alternatives might be:
One could argue that the x is the same. It's its value that changed.

If your variables have meaning, then they have an implicit identity. An animal can be a dog or a cat but a dog can't be a cat.
{
  Thing x=createFirstThing();
  doSomethingWith(x);
}
{
  Thing x=createSecondThing();
  doSomethingElseWith(x);
}
... doSomethingElseWith(x2);

The first alternate, however, has the disadvantage of not only adding visual clutter (...)
Or visual scope separation.

I believe that, with declaration expressions and the semicolon operator scoping/overlapping problems will decrease.
Sep 22, 2014 at 5:02 PM
PauloMorgado wrote:
One could argue that the x is the same. It's its value that changed.
A typical optimizing compiler would split "x" into two variables which may or may not be allocated the same storage location. If the first x gets loaded into a register, then used before that register is needed for something else, and the second x gets likewise loaded into a register, then if both uses of x referred to the same variable, a compiler which wanted to use that register for something else between the two uses of x would have to save its value before such usage and restore it afterward. The fact that between the two uses there is no variable x allows the compiler to omit the save/restore step.
I believe that, with declaration expressions and the semicolon operator scoping/overlapping problems will decrease.
Consider the code:
int x;
x = function1();
action1(x);
...
x = function2(); // Assume this one has no side effects
action2(x);
...
action3(x);
Suppose that it becomes necessary to change action2 to be conditional. It's not hard to imagine the code being incorrectly changed to:
int x;
x = function1();
action1(x);
...
if (condition)
{
  x = function2(); // Assume this one has no side effects
  action2(x);
}
...
action3(x);
If a define keyword behaved as I described, and the code was written as:
define int x = function1();
action1(x);
...
define int x = function2(); // Assume this one has no side effects
action2(x);
...
action3(x);
then a similar attempted modification:
define int x = function1();
action1(x);
...
if (condition)
{
  define int x = function2(); // Assume this one has no side effects
  action2(x);
}
...
action3(x);
would result in a compilation error at the call to action3(x). Note that while one could in this particular example use scoping blocks without needing a new keyword to manage variable lifetime, scoping blocks impose a hierarchical structure which in many cases cannot really fit the overlapping lifetimes of variables in real applications. Frequently value1 will be computed, used in the computation of value2, and abandoned; value2 will be then be used in the computation of value3 and abandoned. Existing scoping rules provide no way to give each variable the proper lifetime in the above scenario.

From an implementation perspective, I would suggest that if a define statement for variable foo is the first statement on line 1234, the identifier should become an alias for an identifier foo_def@1234. In the (unlikely) event that a define for variable bar is the third statement on line 5678, it should be bar_def@1234#3. When compilation reaches the end of a scope for an identifier of the above form, the identifier should alias to something forbidden, to ensure a compilation error if it is used beyond that point. Further, any block which is controlled by a loop should keep track of whether the variable may have been read before it was written within the loop; if so, any redeclaration of the identifier within the loop should be forbidden.
Sep 22, 2014 at 5:15 PM
The C# compiler already does a lot of this. The IL method only has a flat local table with no concept of scoping within the method. The compiler will happily assign variables with the same name to different slots in this table, and it will also reuse slots in the table where scope does not overlap.

It seems that what you're asking is for keywords that allow for scope shadowing. The limitation in C# was added explicitly to prevent this as it is considered a source of confusion. I don't see why adding/repurposing a keyword to force it would make it less confusing. Proposing the additional behavior of allowing an existing variable to be redefined within the same scope would only add to the confusion. Why is it an issue to require two different names to describe two different variables that have overlapping life cycles?
Sep 22, 2014 at 7:31 PM
Halo_Four wrote:
It seems that what you're asking is for keywords that allow for scope shadowing. The limitation in C# was added explicitly to prevent this as it is considered a source of confusion. I don't see why adding/repurposing a keyword to force it would make it less confusing.
All of the places I'm aware of where scope shadowing would be confusing would entail situations where a particular usage of an identifier might have been intended to bind to a variable other than the most recent declaration in the present scope. If you can offer any exceptions, I'd be interested to hear them.

My philosophy of a good language design is that in cases where a construct would have a particular "natural" meaning in the absence of a rule prohibiting it, and where it is unlikely that the programmer could have intended anything else, the construct should be permitted. Given the code:
define int x=getFirstValue();
firstUsage(x);
if (someCondition)
{
  define int x=getSecondValue();
  secondUsage(x);
}
...
thirdUsage(x);
I would consider it clear that FirstUsage() is intended to act upon the result of getFirstValue(), and secondUsage() upon the result of getSecondValue(). I don't think the shadowing creates any ambiguity in either of those cases. The only ambiguity arises at thirdUsage(), where there are three things the programmer might have intended:
  1. Use the result from getSecondValue() if it was evaluated, or else getFirstValue.
  2. Always use the result from getFirstValue().
  3. Always use the result from getSecondValue().
If the programmer's intention was #1, then x should be declared as an "ordinary" variable. If the intention was #2, the programmer must use a different name to hold the result of getSecondValue. If the programmer's intention was #3, the programmer could not reasonably desire that the compiler to generate code consistent with such intention, but could quite reasonably desire that the compiler squawk if the semantics of the code as written could not be reconciled with that intention.

For situations not involving loops, simply creating a set-once variable every time a name is reused would suffice to avoid problems. Loops are slightly more complicated, but could be resolved by having any use of a variable in a looping scope that was entered after its creation freeze that identifier (preventing any redefinition) until the end of that looping scope. Basically, the issue would be that if a variable is not read before a redefinition within a loop, then every pass of the loop could semantically create a new variable (and it wouldn't matter whether it did or not), but if the variable has been read before the redefinition, then the binding of that read would become ambiguous.
Sep 22, 2014 at 7:51 PM
So what's the use case other than that you don't want to have to use two variable names? I don't see this solving any problems. C# was explicitly designed to not permit scope shadowing so introducing it in any form seems contrary to the language charter and that decision was based on decades of experience with languages that do allow this. The C# language specification is pretty clear that it is never permitted for two declarations of the same name within the same declaration space, with the only exception being overloaded methods.
Sep 22, 2014 at 8:28 PM
Edited Sep 22, 2014 at 9:14 PM
Hello everyone. I had some thoughts on semicolon operator. Actually, return just value of last statement is not so cool as "anonymous function" design. It would be really nice have something like:
if({var x = Foo(); var y = Bar(); Write(x); return x * y;} > 0)
{}
Removing "return" operator doesn't save much space but makes reading code more complex. In addition, we are saving all old designs features, multiples return for switch\if\else, locks\using\etc.
for(int i = 0, m = {var x = Foo(); Write(x); return x;}; i < m; i++)
{
 //...
}
int x = { 
    var y = AcquireResource(); 
    try{
        return y.Work(); 
    }
    finally{
        y.Dispose();
    }
};
int a = lock(dataInstance) {return dataInstance.Member1;};
For me this feature can make code be more structured and easy to read.
Sorry if it was proposed already.

Would be nice also mark variables in such blocks with new "out" word to make then visible in outer scope.
while ( 
    {
        out var buffer = new byte[100]; 
        return await stream.ReadAsync(buffer,0,100);
    } > 0
)
{
  // Work with buffer
}
Sep 22, 2014 at 9:52 PM
agat50 wrote:
It would be really nice have something like:
if({var x = Foo(); var y = Bar(); Write(x); return x * y;} > 0)
{}
It's a good way to obfuscate code... other than that, I can't see any good reason to do something like that.

Do you really find it more readable than the code below?
var x = Foo();
var y = Bar();
Write(x);
if(x * y > 0)
{}
I'll pick the second form every time
Sep 22, 2014 at 10:56 PM
Halo_Four wrote:
So what's the use case other than that you don't want to have to use two variable names? I don't see this solving any problems. C# was explicitly designed to not permit scope shadowing so introducing it in any form seems contrary to the language charter and that decision was based on decades of experience with languages that do allow this. The C# language specification is pretty clear that it is never permitted for two declarations of the same name within the same declaration space, with the only exception being overloaded methods.
Sometimes code duplication is unavoidable, e.g. if a test is required after each loop iteration which is similar to the one before a loop start, but sufficiently different to preclude factoring to a common method. I would posit that it is better to have duplicated code use the same identifiers everyplace it appears than to use different identifiers in the absence of a semantic reason for the identifiers to be different. Using different identifiers for a piece of code in an inner scope increases the number of opportunities for mistakes while not boosting clarity.

My personal opinion is that there are a number of places where the C# team came up with a rather poorly-focused rule to try to solve a particular problem, thus blocking what would otherwise have been some perfectly useful constructs while failing to totally solve the original problem. For example, C# presently allows:
class foo
{
  int bar;
  void boz()
  {
    if (condition)
    {
      int bar;
      ...
      bar=23;
    }
    Console.WriteLine("{0}",bar);
  }
}
If I had my druthers, the above would not have been legal, because of the probability that the programmer might have intended the variable which is set to 23 to be the same variable which is written to the console. Use of local identifiers which mimic field names, however, seems to be common in C#.
Sep 22, 2014 at 11:34 PM
Edited Sep 22, 2014 at 11:39 PM
tom103 wrote:
agat50 wrote:
It would be really nice have something like:
if({var x = Foo(); var y = Bar(); Write(x); return x * y;} > 0)
{}
It's a good way to obfuscate code... other than that, I can't see any good reason to do something like that.

Do you really find it more readable than the code below?
var x = Foo();
var y = Bar();
Write(x);
if(x * y > 0)
{}
I'll pick the second form every time
I'll definitely rather use
if(
    {
        var x = Foo(); 
        var y = Bar(); 
        Write(x); 
        return x * y > 0;
    }
)
{
    //
}
Pros - i see all variables and method calling related to this IF statement, no redundant scope in\out - these temp variables x,y,z,etc messing up intellisense both inside if statement and outer method. Short names are often in short supplies too.
Sep 23, 2014 at 12:47 AM
supercat

C# compiler and JIT optimizations are still possible no matter what name variables have.

Remember that written code is also a social interaction and must be understood by humans too. Reusing variable names in the same scope hinders readability.

Names like "x" should be obvious what they mean. Reusing it might make things less clear. I know that sometimes it just feels like it should be "x" all the time. For those cases, instead of having "x1", "x2", ..., "xn" I prefer to explicitly scope the xs:
int a = 1;

{
    int b = 2;
    a = b;
}

{
    int b = 3;
    a = b;
}
Sep 23, 2014 at 12:48 AM
supercat wrote:
Halo_Four wrote:
So what's the use case other than that you don't want to have to use two variable names? I don't see this solving any problems. C# was explicitly designed to not permit scope shadowing so introducing it in any form seems contrary to the language charter and that decision was based on decades of experience with languages that do allow this. The C# language specification is pretty clear that it is never permitted for two declarations of the same name within the same declaration space, with the only exception being overloaded methods.
Sometimes code duplication is unavoidable, e.g. if a test is required after each loop iteration which is similar to the one before a loop start, but sufficiently different to preclude factoring to a common method. I would posit that it is better to have duplicated code use the same identifiers everyplace it appears than to use different identifiers in the absence of a semantic reason for the identifiers to be different. Using different identifiers for a piece of code in an inner scope increases the number of opportunities for mistakes while not boosting clarity.

My personal opinion is that there are a number of places where the C# team came up with a rather poorly-focused rule to try to solve a particular problem, thus blocking what would otherwise have been some perfectly useful constructs while failing to totally solve the original problem. For example, C# presently allows:
class foo
{
  int bar;
  void boz()
  {
    if (condition)
    {
      int bar;
      ...
      bar=23;
    }
    Console.WriteLine("{0}",bar);
  }
}
If I had my druthers, the above would not have been legal, because of the probability that the programmer might have intended the variable which is set to 23 to be the same variable which is written to the console. Use of local identifiers which mimic field names, however, seems to be common in C#.
That's not legal C#. The attempt to use bar within Console.WriteLine fails with the message 'bar' conflicts with declaration 'foo.bar'.

The ability to shadow a class member with a local is frequently brought up but the rules regarding doing so are pretty explicitly laid out and enforced by the compiler and you can always continue to access the member through this..
Sep 23, 2014 at 4:10 PM
Halo_Four wrote:
That's not legal C#. The attempt to use bar within Console.WriteLine fails with the message 'bar' conflicts with declaration 'foo.bar'.
Hmm... I thought I tried that and it worked, to my chagrin. If C# squawks at it, that's great.

In any case, I think my main point is that I would like to see a concise way of saying essentially "Code in this method reads foo above this point (in source-code order) should only see values written to it above this point, and values written above this point should not be visible below." I think that allowing variable reuse only in those cases where it wouldn't alter program semantics, but where the reuse would serve as a compile-time assertion that reuse would not alter program semantics, would be the easiest way to achieve that. Upon some consideration, an alternative might be to allow a statement of the form let variable = expr; have that meaning [assuming the type of the variable has been established]. Although let is not a reserved word and could theoretically be the name of a type, the only situation in which that syntax could theoretically pose an ambiguity would be if there was a type in scope named let and either:
  1. An attempt was made to use let on a variable name that was not defined, and the right-hand side of the expression could be coerced to type let.
  2. An attempt was made to redeclare a variable of type let, in which case the compiler would accept such redeclaration in those cases where it wouldn't alter program semantics.
Neither risk seems particularly great, especially since it's unlikely that any type let would happen to be compatible with the right-hand side of a let expression.
Sep 25, 2014 at 3:45 PM
Regarding Semicolon expressions, C and C++ already provide a comma operator to separate expressions, the right-most expression being the final value. Why not just use that?
Console.WriteLine("Result: {0}", (var x = GetValue(), x * x));
Sep 25, 2014 at 7:48 PM
msauper wrote:
Regarding Semicolon expressions, C and C++ already provide a comma operator to separate expressions, the right-most expression being the final value. Why not just use that?
A pair of parentheses containing items separated by commas already has a different meaning (parameter lists). Additionally, the use of semicolons would make it possible for an expression to contain nested declarations and statements--something which would not be possible with the comma operator.
Sep 25, 2014 at 7:56 PM
I believe that by the time the comma is being interpreted, It is already know whether the compiler is collecting a parameter list. It is not ambiguous if a comma as a parameter separator has higher precedence than a comma as an expression operator. I'm also not sure whether it precludes nested declarations. It depends again on the precedence of the operator.
Console.WriteLine("Result: {0}", (var x = GetValue(), var y = GetValue(), x * y));
Sep 25, 2014 at 10:02 PM
msauper wrote:
I believe that by the time the comma is being interpreted, It is already know whether the compiler is collecting a parameter list. It is not ambiguous if a comma as a parameter separator has higher precedence than a comma as an expression operator. I'm also not sure whether it precludes nested declarations. It depends again on the precedence of the operator.
Since declaration statements can themselves contain commas, using a comma as a statement separator within a parentheses-enclosed expression could introduce ambiguity. What should be the effect of:
int d2,y;
y=12;
d2=(int x=getX(), y=getY(), x*x+y*y);
Using a semicolon as the statement terminator would eliminate ambiguity about whether the latter statement was intended to declare a new variable y or overwrite the existing one:
d2=(int x=getX(); y=getY(); x*x+y*y);  // Overwrites the existing variable
d2=(int x=getX(), y=getY(); x*x+y*y);  // Attempts to create new variable with name y (compiler will probably squawk).
Sep 26, 2014 at 8:18 AM
PauloMorgado wrote:
Przemyslaw wrote:
PauloMorgado wrote:
I would like that variables could be redefined.
Yes, this would be ultimate solution.
I should have been already in when I replied.

I meant: I would not like that variables could be redefined.
When I think about it more, I think you are right. Shadowing is not the best solution for locals in general. However, I think it should be possible for lambda parameters. The same way it is possible to use the same name for field and parameter in regular method. IMHO all snippets below represent similar code, just at different nesting level:
class Foo {
  int x;
  int Bar(int x) 
  { 
    return x; 
  }
}
class Foo {
  int x;
  Func<int, int> Bar = x => x; 
}
class Foo {
  int x;
  Func<int, int> Bar;
  public Foo()
  {
    Bar = x => x;
  } 
}
class Foo {
  Func<int, int> Bar;
  public Foo(int x)
  {
    Bar = x => x;
  } 
}
class Foo {
  Func<int, int> Bar;
  public Foo()
  {
    int x;
    Bar = x => x;
  } 
}
First 3 snippets are legal, two last not. But all are conceptually similar. Of course, if last snippets were legal, there is a question - how to make both 'x' variables available inside lambda body? With field it is simple - just use 'this.x' and you are done. Here I see 2 ways. One is to introduce or reuse a new keyword to access variable from outer scope, e.g. 'outer.x' or 'extern.x'. Second is to do nothing at all about it. If one wants to access outer variable, then he needs to name parameter differently, so that no shadowing occurs. I personally prefer the second approach.
Sep 26, 2014 at 10:06 AM
Edited Sep 26, 2014 at 10:07 AM
Przemyslaw wrote:
PauloMorgado wrote:
Przemyslaw wrote:
PauloMorgado wrote:
I would like that variables could be redefined.
Yes, this would be ultimate solution.
I should have been already in when I replied.

I meant: I would not like that variables could be redefined.
When I think about it more, I think you are right. Shadowing is not the best solution for locals in general. However, I think it should be possible for lambda parameters. The same way it is possible to use the same name for field and parameter in regular method. IMHO all snippets below represent similar code, just at different nesting level:
Lambdas aren't any different from anonymous methods and fields can always be accessed by qualifying them with this..

I would favor more something like:
public void MyMethod(string s)
{
    var lambda = (string new s) => s;
}
Sep 26, 2014 at 1:53 PM
Is this your personal taste or are there other arguments for being so explicit and verbose? IMHO c# should handle shadowing field and shadowing outer local the same way. We don't use new when we shadow field:
class Foo
{
  int x;
  public Foo(int y)
  {
    var lambdax = (int x) => x;  // no 'int new x'
    var lambday = (int y) => y;  // why 'int new y' here? both should look the same
  }
}
Sep 26, 2014 at 2:08 PM
Przemyslaw wrote:
Is this your personal taste or are there other arguments for being so explicit and verbose? IMHO c# should handle shadowing field and shadowing outer local the same way. We don't use new when we shadow field:
class Foo
{
  int x;
  public Foo(int y)
  {
    var lambdax = (int x) => x;  // no 'int new x'
    var lambday = (int y) => y;  // why 'int new y' here? both should look the same
  }
}
Of course it is my personal taste. But it's not out of the blue.
new Modifier (C# Reference)
When used as a declaration modifier, the new keyword explicitly hides a member that is inherited from a base class. When you hide an inherited member, the derived version of the member replaces the base class version. Although you can hide members without using the new modifier, you get a compiler warning. If you use new to explicitly hide a member, it suppresses this warning.
Sep 26, 2014 at 2:11 PM
Przemyslaw wrote:
IMHO c# should handle shadowing field and shadowing outer local the same way.
Lambdas or not the question of being able to shadow locals in C# is a very old one and the decision was to never permit locals to be shadowed. C# does allow shadowing fields because it is possible to explicitly reference the field despite the local shadowing, and the C# compiler is fairly strict about enforcing syntax to prevent that from being ambiguous, e.g. if you do define a local of the same name anywhere within the method, even a child scope, you can never refer to the field without this.
Sep 27, 2014 at 9:07 AM
PauloMorgado wrote:
Of course it is my personal taste. But it's not out of the blue.
new Modifier (C# Reference)
When used as a declaration modifier, the new keyword explicitly hides a member that is inherited from a base class. When you hide an inherited member, the derived version of the member replaces the base class version. Although you can hide members without using the new modifier, you get a compiler warning. If you use new to explicitly hide a member, it suppresses this warning.
IMHO the key here is the word inherited. When defining a method with the same signature without any modifiers, the compiler assumes new by default. I think the warning is emitted only to help dev fall into pit of success - there is very high chance he forgot to make the method virtual / overriden. Overriding members is much more common than shadowing after all. Hence the warning and the new modifier to explicitly tell the intent.

But when I shadow outer local or parameter with lambda parameter, there is nothing to override. The new is the only option left on the table. Hence it is redundant IMHO.

There is also another argument - lambdas are used to define anonymous methods in a very concised way. Very often inline. If I want to shadow x in my lambda, and if I am required to use new, then I need to add 6 additional characters - 3 for "new", one for space between "new" and shadowed name and 2 for parenthesis (ok, at least 4 as parens are sometimes already there):
(new x) => x instead of x => x
This would make the whole feature a bit inconvenient for me. I'd rather use other short name and not shadow at all, as it requires only 2 additional characters from me:
x2 => x2 instead of x => x

If we allow shadowing without the explicit new, then no additional characters are needed at all.
Sep 27, 2014 at 9:18 AM
Halo_Four wrote:
Przemyslaw wrote:
IMHO c# should handle shadowing field and shadowing outer local the same way.
Lambdas or not the question of being able to shadow locals in C# is a very old one and the decision was to never permit locals to be shadowed. C# does allow shadowing fields because it is possible to explicitly reference the field despite the local shadowing, and the C# compiler is fairly strict about enforcing syntax to prevent that from being ambiguous, e.g. if you do define a local of the same name anywhere within the method, even a child scope, you can never refer to the field without this.
This is all true. However, the "no shadowing" rule was established long before lambdas or anonymous methods came to the language. I do not advocate to change these rule entirely and allow shadowing everywhere. I'd like to just adapt them to play nicely with lambdas. Even Mads stated that no shadowing is annoying in such case:
madst wrote:
There are existing cases where this rule is annoying:
task.ContinueWith(task => … task …); // Same task! Why can’t I name it the same?
Here it seems the rule even runs counter to refactoring, because you need to change every occurrence of the name when you move code into a lambda.
Sep 27, 2014 at 12:33 PM
Przemyslaw wrote:
This is all true. However, the "no shadowing" rule was established long before lambdas or anonymous methods came to the language. I do not advocate to change these rule entirely and allow shadowing everywhere. I'd like to just adapt them to play nicely with lambdas. Even Mads stated that no shadowing is annoying in such case:
Lambdas don't change the problem, though. There would still be no syntax that could be used within the method that would eliminate the ambiguity of which variable might be used as there is no syntax through which to reference an outer scope. Lambda scoping rules behave the same as any other scope within a method.

I agree that it can be annoying that you have to choose different names, especially for arguments that you probably don't care about. I'd more advocate a wildcard dumping group for said variables. I already make use of a _ argument name in the above case, although it does suck if you end up with more than one in scope and have to break out something like __ and ___, although at that point I might start using throw-away single letter names instead.

Not sure where this whole conversation came from, though. When trying to figure out whether it was worth solving a similar naming/scoping problem with variable declarations it was decided to not change anything so I have a feeling that adding caveats to the written scoping rules is not on the table.
Sep 28, 2014 at 8:13 PM
Przemyslaw wrote:
PauloMorgado wrote:
Of course it is my personal taste. But it's not out of the blue.
new Modifier (C# Reference)
When used as a declaration modifier, the new keyword explicitly hides a member that is inherited from a base class. When you hide an inherited member, the derived version of the member replaces the base class version. Although you can hide members without using the new modifier, you get a compiler warning. If you use new to explicitly hide a member, it suppresses this warning.
IMHO the key here is the word inherited. When defining a method with the same signature without any modifiers, the compiler assumes new by default. I think the warning is emitted only to help dev fall into pit of success - there is very high chance he forgot to make the method virtual / overriden. Overriding members is much more common than shadowing after all. Hence the warning and the new modifier to explicitly tell the intent.

But when I shadow outer local or parameter with lambda parameter, there is nothing to override. The new is the only option left on the table. Hence it is redundant IMHO.
When a member in an inherited class shadows a member in its base class without explicitly doing so through the new keyword, a warning is issued, but the generated IL code is the same. This is to prevent mistakes, as you stated. But there's one mistake you left out: giving the wrong name to the member.

I can't understand why you assume that, because in the case of parameter names there's only one mistake you can make, you assume it's safe to ignore that possible mistake.
Sep 29, 2014 at 9:41 AM
PauloMorgado wrote:
When a member in an inherited class shadows a member in its base class without explicitly doing so through the new keyword, a warning is issued, but the generated IL code is the same. This is to prevent mistakes, as you stated. But there's one mistake you left out: giving the wrong name to the member.

I can't understand why you assume that, because in the case of parameter names there's only one mistake you can make, you assume it's safe to ignore that possible mistake.
That's because I didn't think about it as a source of such mistakes. I always saw this rather as a feature which can save my pain of introducing the new name for the same thing used inside lambda. A pain which I feel very often.
I agree that one can accidentally shadow outer variable. This would be an issue only when one actually intended to capture the outer variable. In other cases, such accidental shadowing would do no harm. But yes, it can be source of bugs.
The question is - are these situations often enough to demand the new modifier and sacrifice the simplicity of syntax? I don't know and I am not competent to answer this. And my opinion is not so strong now. Thank you for eye-opening discussion.
Sep 29, 2014 at 10:48 AM
Edited Oct 1, 2014 at 10:20 AM
Thought a little more on typical usecases of proposed feature.
var x = 
    lock(_dict){
        return {
            foreach(var pair in _dict)
            {
                yield return new {A = pair.Key, B = pair.Value.Member1};
            }
        }.ToList();
    }; //List<Anonymous type>
var y = 
    using(var conn = new SqlConnection(...))
    {
        return (await conn.QueryAsync<LongLongLongClassName>(
            "sql query"
        )).GroupBy(/* .... */)
        .Select(/* ... */)
        .ToList(); // Long long long final typename
    };
Without locks\using it would be easily implemented by linq, but when we need acquire some resource or use async\await in cycle - there are problems. In my humble opinion it's a very useful feature.

P.S. Who are interested in such feature please vote here http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/6504697-block-code-as-value-another-syntax-for-semicolon .
Sep 29, 2014 at 11:32 AM
Przemyslaw wrote:
PauloMorgado wrote:
When a member in an inherited class shadows a member in its base class without explicitly doing so through the new keyword, a warning is issued, but the generated IL code is the same. This is to prevent mistakes, as you stated. But there's one mistake you left out: giving the wrong name to the member.

I can't understand why you assume that, because in the case of parameter names there's only one mistake you can make, you assume it's safe to ignore that possible mistake.
That's because I didn't think about it as a source of such mistakes. I always saw this rather as a feature which can save my pain of introducing the new name for the same thing used inside lambda. A pain which I feel very often.
I agree that one can accidentally shadow outer variable. This would be an issue only when one actually intended to capture the outer variable. In other cases, such accidental shadowing would do no harm. But yes, it can be source of bugs.
The question is - are these situations often enough to demand the new modifier and sacrifice the simplicity of syntax? I don't know and I am not competent to answer this. And my opinion is not so strong now. Thank you for eye-opening discussion.
You to take in account that source code from C#-1.0 to C#-(n-1) should compile or not compile just the same on C$-n. This would break it in a way that code that doesn't compile on C#-1.0 to C#-(n-1) would compile on C#-n. With the use of the new modifier this wouldn't happen.
Sep 29, 2014 at 5:12 PM
PauloMorgado wrote:
You to take in account that source code from C#-1.0 to C#-(n-1) should compile or not compile just the same on C$-n. This would break it in a way that code that doesn't compile on C#-1.0 to C#-(n-1) would compile on C#-n. With the use of the new modifier this wouldn't happen.
The normal purpose of a new compiler is to allow code for the new compiler to make use of constructs that wouldn't work on older compilers. I think the main goals are normally:
  1. Very strongly avoid cases in which code which was legal on the old compiler, compiles cleanly on the new compiler but yields different behavior, except in cases where it is very unlikely that any production code would be affected. For example, while it's possible that some code which used event += newDelegate within event's class might have expected the += operator to act directly on the delegate rather than invoking the "add handler" method, it's likely that in most situations where the two operations are not equivalent, the latter would be the correct one. If the old behavior was required, the code could be written event = event + newDelegate;.
  2. Strongly avoid cases where code which was legal on the old compiler refuses to compile on the new compiler, except--as above--in cases where it is very unlikely production code would be affected and, in those cases where code would be affected, code may be easily modified so as to yield the same behavior as the old compiler [e.g. given float f; double d;, having the compiler squawk at f.Equals(d) (or any other attempt to pass a non-generic value type to the Equals method of a different non-generic value type) might break code which is meant to illustrate the danger of such comparisons, but if the boxing operation is actually desired, replacing the code with f.Equals((Object)d) would achieve exactly the same semantics as the original].
  3. Avoid cases where code which compiled cleanly on the old compiler would yield warnings on the new compiler, unless it is likely that the code was erroneous even on the old compiler, and there is an easy way to change the code so as to yield the same semantics but with no warning.
Having constructs be legal on the new compiler which were forbidden on the old compiler should generally be considered normal and expected. It may be helpful for a new compiler to include an option to behave like the old compiler (so that someone who is sharing code with a team that's using an older compiler can ensure the other team will be able to use his code) but in general compilers are expected to add new features.
Sep 30, 2014 at 12:14 AM
supercat wrote:
PauloMorgado wrote:
You to take in account that source code from C#-1.0 to C#-(n-1) should compile or not compile just the same on C$-n. This would break it in a way that code that doesn't compile on C#-1.0 to C#-(n-1) would compile on C#-n. With the use of the new modifier this wouldn't happen.
The normal purpose of a new compiler is to allow code for the new compiler to make use of constructs that wouldn't work on older compilers. I think the main goals are normally:
  1. Very strongly avoid cases in which code which was legal on the old compiler, compiles cleanly on the new compiler but yields different behavior, except in cases where it is very unlikely that any production code would be affected. For example, while it's possible that some code which used event += newDelegate within event's class might have expected the += operator to act directly on the delegate rather than invoking the "add handler" method, it's likely that in most situations where the two operations are not equivalent, the latter would be the correct one. If the old behavior was required, the code could be written event = event + newDelegate;.
  2. Strongly avoid cases where code which was legal on the old compiler refuses to compile on the new compiler, except--as above--in cases where it is very unlikely production code would be affected and, in those cases where code would be affected, code may be easily modified so as to yield the same behavior as the old compiler [e.g. given float f; double d;, having the compiler squawk at f.Equals(d) (or any other attempt to pass a non-generic value type to the Equals method of a different non-generic value type) might break code which is meant to illustrate the danger of such comparisons, but if the boxing operation is actually desired, replacing the code with f.Equals((Object)d) would achieve exactly the same semantics as the original].
  3. Avoid cases where code which compiled cleanly on the old compiler would yield warnings on the new compiler, unless it is likely that the code was erroneous even on the old compiler, and there is an easy way to change the code so as to yield the same semantics but with no warning.
Having constructs be legal on the new compiler which were forbidden on the old compiler should generally be considered normal and expected. It may be helpful for a new compiler to include an option to behave like the old compiler (so that someone who is sharing code with a team that's using an older compiler can ensure the other team will be able to use his code) but in general compilers are expected to add new features.
Brilliant explanation!

However, what I was trying to say was something like:
Having constructs be legal on the new compiler which were forbidden on the old compiler should generally be considered normal and expected. It may be helpful for a new compiler to include an option to behave like the old compiler (so that someone who is sharing code with a team that's using an older compiler can ensure the other team will be able to use his code) but in general compilers are expected to add new features.
But this would make code that previously did not compile due to the lack of some feature but because it was wrong. Wrong in the way that the programmer either didn't know what she/he was doing or made an unintentional mistake.

I would like you to explain me where introducing variable shadowing falls under 1, 2 or 3.

I wouldn't like this to de done without explicit intent of the programmer. Even if it just issues a warning that you might want to ignore if you're on that camp.
Sep 30, 2014 at 4:37 PM
An ideal compiler for an ideal programming language should, when given a piece of code, either produce an executable whose behavior is consistent with programmer intent, or else squawk if it cannot determine the programmer's intent based upon the code it is given. A well-crafted set of rules should thus allow code for which an intended behavior is clear and could not plausibly contradict the programmer's intention, while disallowing code in cases where a programmer might likely have intended behavior other than what the compiler would produce.

Consider the following pieces of code:
// #1
var temp = myList[0];
MoveTo(temp.X, temp.Y);
for (int i=0; i<myList.Count; i++)
{
  var temp = myList[i];
  LineTo(temp.X, temp.Y);
}

// #2
ClearPoints();
for (int i=0; i<myList.Count; i++)
{
  var temp = myList[i];
  AddPoint(temp.X, temp.Y);
}
var temp = myList[0];
AddPoint(temp.X, temp.Y);
DrawLines();

// #2
ClearPoints();
var temp = myList[0];
for (int i=0; i<myList.Count; i++)
{
  var temp = myList[i];
  AddPoint(temp.X, temp.Y);
}
AddPoint(temp.X, temp.Y);
DrawLines();

// #4
var temp1 = myList[0];
MoveTo(temp1.X, temp1.Y);
for (int i=0; i<myList.Count; i++)
{
  var temp2 = myList[i];
  LineTo(temp2.X, temp1.Y);
}

In the absence of the rule against shadowing, the actual behavior of all four code samples would be clear; because of the rule, however, only the fourth would actually be legal. I would suggest that the intended behavior for the first two examples would clearly coincide with the actual behavior; I can't think of anything else a programmer could have intended while writing the code as it was written [the fact that the first example draws an open collection of line segments rather than a closed one might be unintentional, nothing in the code would suggest an intention to close it]. With #3, even though I think the final use of temp would be intended to refer back to its earlier value, I would not fault a compiler for thinking a programmer might plausibly have intended to use the value set within the loop. Example #4 is the only one that compiles, but I would suggest the behavior of the code as written is the least likely of any to express the programmer's actual intentions. I see no particular reason for a compiler to squawk at #4, and think the compiler correctly squawked at #3, but would suggest that the rule which forbids scenarios like #1 and #2 makes scenarios like #4 more likely.
Oct 1, 2014 at 12:14 AM
For me, because I fail a lot and I'm just an average guy, I'd like shadowing to be explicit.

Every temp introduced in the for body that shadows the outer scope's temp should be explicitly shadowed with something like this:
  new var temp = myList[i];
This way it is clear to the writer and any reader that the intention was to introduce a new temp in that scope. The reason to have the same name is because they have the same logical meaning although being different instances.

It is also clear that I wan to scope the new temp to the for body. Meaning that outside of the body of the for the temp is the same unharmed old temp with the same body.

So, in #3, the temp being used after the for is, obviously, the one declare before the for and not the one declare in the scope that no longer exists (the body of the for).

Whatever rule we come up with, the compiler will implement it and the compiler couldn't care less about the name of things. The names are there for humans and, as with the member new modifier, it's for the sake of humans that the variable new modifier should be required.
Oct 1, 2014 at 12:54 AM
PauloMorgado wrote:
Whatever rule we come up with, the compiler will implement it and the compiler couldn't care less about the name of things. The names are there for humans and, as with the member new modifier, it's for the sake of humans that the variable new modifier should be required.
Considering that the C# team rejected even a special case for shadowing for this specific scenario something tells me that it's not a subject up for discussion. These conversations are all older than the language and the current rules based on the known confusion and logic problems caused by shadowing. The argument of intent is moot because you aren't in the head of the author, nor should you have to be,. Every exception is another line item in the spec and another rule that everyone else has to memorize. No exceptions keeps the behavior simple and predictable.

As it stands the only permitted shadowing is of members in a derived type or of locals of fields, and in both cases the developer can at any time access the shadowed item in an unambiguous manner. Per §7.6.2.1 of the C# 5.0 spec, all identifiers must remain invariant within a given block.
Oct 1, 2014 at 2:14 PM
This discussion has become incredibly hard to follow because there is a new feature, that I'm not sure the team has even commented on, buried in a thread about a design meeting.

But as it is here, i will respond here.

Creating unique variable names has never, to my knowledge, been identified as a programmer pain point.

I think any reuse of variables from an outer local scope is a bad idea.

Reusing variables, as today, in parallel logically nested structures is OK.

So, with or without new, I think this is a very bad idea and don't see any of these stated scenarios as compelling.

Kathleen
Oct 1, 2014 at 4:47 PM
PauloMorgado wrote:
This way it is clear to the writer and any reader that the intention was to introduce a new temp in that scope. The reason to have the same name is because they have the same logical meaning although being different instances.
The new qualifier doesn't address the real problem with such shadowing, which appears at the end of the scope: two appearances of the identifier, with nothing between them other than close-braces, could be associated with two different variables.
Oct 1, 2014 at 6:47 PM
With regards to shadowing.
In particular, it blocks another variable with the same name from being introduced in a subsequent condition, which is quite annoying.
It's not nearly as annoying as the subtle bugs created by accidental shadowing. The reason this design decision exists and even needs to be made is because of the introduction of declaration expressions. Declaration expressions are convenient, but provide at most a marginal gain for developers. (Saves you a couple lines of code when working with out)

There is no way that the marginal gains from declaration expressions warrant introducing implicit shadowing to C#. The pain from subtle bugs that would be possibly introduced far outweigh the benefits from saving one or two lines of code. (If I could have it my way, I'd remove declaration expressions entirely due to the pain they've already caused, but I think we're past that point). What happened to the pit of success?

I would vote for something scenario-specific. It sucks for the language designers, but at least it doesn't damage C# as a language.

Failing that, make shadowing explicit with the new keyword. (Even that doesn't fix the problem entirely, you'll still have devs scratching their heads as to why it's needed within if-else statements that appear as though they should)
Oct 2, 2014 at 1:36 AM
KathleenDollard wrote:
This discussion has become incredibly hard to follow because there is a new feature, that I'm not sure the team has even commented on, buried in a thread about a design meeting.

But as it is here, i will respond here.

Creating unique variable names has never, to my knowledge, been identified as a programmer pain point.

I think any reuse of variables from an outer local scope is a bad idea.

Reusing variables, as today, in parallel logically nested structures is OK.

So, with or without new, I think this is a very bad idea and don't see any of these stated scenarios as compelling.

Kathleen
I'd say that, without the modifier it's just bad, not very bad. :
Oct 2, 2014 at 1:39 AM
supercat wrote:
PauloMorgado wrote:
This way it is clear to the writer and any reader that the intention was to introduce a new temp in that scope. The reason to have the same name is because they have the same logical meaning although being different instances.
The new qualifier doesn't address the real problem with such shadowing, which appears at the end of the scope: two appearances of the identifier, with nothing between them other than close-braces, could be associated with two different variables.
There's no such thing as a close-braces between whatever in C#. The close-braces, per se means nothing. Now, the open-braces/close-braces pair means it encloses everything in the middle in it's own scope, which is an inner scope to the scope where they are.

So, what you call a close-braces is closing a scope and any variable declared there is now out of scope.
Oct 2, 2014 at 7:49 PM
PauloMorgado wrote:
So, what you call a close-braces is closing a scope and any variable declared there is now out of scope.
...and any variable which had been in scope before the corresponding open brace effectively comes back into scope.

I would posit that in a well-designed language where } represents an end of scope (as opposed to e.g. end-of-comment), if the code segment
...
    foo = 23;
  }
  bar = foo;
...
appears somewhere within a method that compiles, then the variable foo which is assigned to 23 should be the same as the variable foo which is copied to bar, regardless of anything that might have appeared earlier in the method. IMHO, good language constructs should make it possible to look at a very small portion of a program and know that there's only one thing it can possibly mean. Determining whether a particular piece of code will compile may require looking at a much larger context, but if one knows that code compiles, but knowing the meaning of code that compiles should not.
Oct 2, 2014 at 9:21 PM
supercat wrote:
PauloMorgado wrote:
So, what you call a close-braces is closing a scope and any variable declared there is now out of scope.
...and any variable which had been in scope before the corresponding open brace effectively comes back into scope.
The variables before the opening brace never left scope, so can't come back in to scope. If they did so how become "forgotten" you could do the following.
mymethod
{
  var y = 0;
  var z = 0;
  while pred
  {
    var x = 0;
    z = z + 1;  /* You couldn't do this */
  }
  y = x;
}
more on scoping issues.
while !{int.parse( Console.ReadLine ,out var v)
{
}
  • Is v scoped to just the while?
    Makes the assumption that the usage will always be followed by a new scoping block ( aka braces ).
var valid = Int.TryParse( s, out var value );
Console.WriteLine( value );
  • Is that just the predicate part?
    This would make any sense as how could you do anything else with the variable?
  • or is it scoped to the next outer scope?
    This one would be simplest to understand for beginners.
Coordinator
Oct 2, 2014 at 9:53 PM
KathleenDollard wrote:
Creating unique variable names has never, to my knowledge, been identified as a programmer pain point.
supercat identified it as a pain point, I find it a pain point, and I've raised it as a pain point in LDMs! Never enough to persuade others like you and JoshVarty who consider it a lesser pain than the alternatives, or not a pain at all. Here's a typical pain scenario:
Sub Button1_Click(sender as Object, e As EventArgs) Handles Button1.Click
End Sub

Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
   Foo(e)
   Bar(e)
End Sub
Now I want to refactor this so that button2 event handler is only signed up in response to Button1:
Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
   AddHandler Button2.Click, Sub(sender, e)
         ' copy+paste the content of Button2_Click into here
      End Sub
End Sub
The way I'm forced to do this is first name the lambda parameters incorrectly (sender, e), then paste the code, then do a rename-refactor on the lambda parameters to sender2, e2.

I think that sender2 and e2 are dumb names. I don't like being forced to type incorrect code as my first step for the sake of refactoring later on. I don't even care to do the refactoring. And if I was copy+pasting from a magazine article that didn't yet compile (rather than a working piece of code) then the chance of rename-refactoring to succeed is pretty low.
Oct 2, 2014 at 10:56 PM
Edited Oct 2, 2014 at 11:20 PM
lwischik wrote:
I think that sender2 and e2 are dumb names. I don't like being forced to type incorrect code as my first step for the sake of refactoring later on. I don't even care to do the refactoring. And if I was copy+pasting from a magazine article that didn't yet compile (rather than a working piece of code) then the chance of rename-refactoring to succeed is pretty low.
I entirely agree that they're dumb names. But the alternative solutions were considerably worse. Despite deriving a lot of syntax from C, the designers of C# specifically forbid local variable shadowing. I doubt this was an unintentional decision.

From my experience, it protects developers from really nasty bugs that crop up when variables are shadowed unintentionally, or developers don't realize they (or someone else) has shadowed a variable.

One more thing: No software project is limited by the time it takes to write code. The bottleneck is the time it takes to understand the software project and understand the impact one's changes have on it.

There's an excellent post on MSDN (by Peter Hallam) discussing how ~78% of developer time is spent reading old code.

I believe language features should seek to decrease the time it takes to understand code. Language features that make renaming/writing operations more convenient at the expense of introducing confusion should be avoided. Manually renaming variables you've pasted might be annoying, but the work is straightforward and only takes a couple of minutes.
Oct 2, 2014 at 11:28 PM
supercat wrote:
PauloMorgado wrote:
So, what you call a close-braces is closing a scope and any variable declared there is now out of scope.
...and any variable which had been in scope before the corresponding open brace effectively comes back into scope.
In fact, it was always in scope. It was just shadowed and became visible again.
I would posit that in a well-designed language where } represents an end of scope (as opposed to e.g. end-of-comment), if the code segment
...
    foo = 23;
  }
  bar = foo;
...
appears somewhere within a method that compiles, then the variable foo which is assigned to 23 should be the same as the variable foo which is copied to bar, regardless of anything that might have appeared earlier in the method. IMHO, good language constructs should make it possible to look at a very small portion of a program and know that there's only one thing it can possibly mean. Determining whether a particular piece of code will compile may require looking at a much larger context, but if one knows that code compiles, but knowing the meaning of code that compiles should not.
By looking at that piece of code I can only assume that 23 is assignable to bar. But, then again, you didn't say it compiled, so I can't be sure.

Welcome to the real world where context exists and is important to reason about the pieces you look at.

What you are describing can be looked at as someone that wrote code that can't read. That's why all recommendations say that one should be able o understand the code/algorithm just by looking at a screen length of code. The current diversity of screen sizes makes it challenging, but those recommendations were made when there were not that many screen sizes or code was read from paper - context.

You are offering arguments to never, ever, allow variable shadowing.
Oct 2, 2014 at 11:29 PM
AdamSpeight2008 wrote:
The variables before the opening brace never left scope, so can't come back in to scope. If they did so how become "forgotten" you could do the following.
z = z + 1;  /* You couldn't do this */
Nothing shadows z. When a outer-scope variable is shadowed by an inner-scope variable, the outer-scope variable will become invisible and inaccessible. If you don't like the phrasing "effectively comes back into scope", substitute "comes back into view".
}

more on scoping issues.
while !{int.parse( Console.ReadLine ,out var v)
{
}
  • Is v scoped to just the while?
I don't like the idea of out var directly creating a variable.
Makes the assumption that the usage will always be followed by a new scoping block ( aka braces ).
var valid = Int.TryParse( s, out var value );
Console.WriteLine( value );
  • Is that just the predicate part?
    This would make any sense as how could you do anything else with the variable?
  • or is it scoped to the next outer scope?
    This one would be simplest to understand for beginners.
If I had my druthers, reuse of a name within a method should be allowable in cases where having every reuse of the name declare a different variable would have the same effect as having a single storage location associated with the name. In other words, it would be allowable only in those cases where the exact scoping wouldn't matter much.
Oct 2, 2014 at 11:38 PM
lwischik wrote:
KathleenDollard wrote:
Creating unique variable names has never, to my knowledge, been identified as a programmer pain point.
supercat identified it as a pain point, I find it a pain point, and I've raised it as a pain point in LDMs! Never enough to persuade others like you and JoshVarty who consider it a lesser pain than the alternatives, or not a pain at all. Here's a typical pain scenario:
Sub Button1_Click(sender as Object, e As EventArgs) Handles Button1.Click
End Sub

Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
   Foo(e)
   Bar(e)
End Sub
Now I want to refactor this so that button2 event handler is only signed up in response to Button1:
Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
   AddHandler Button2.Click, Sub(sender, e)
         ' copy+paste the content of Button2_Click into here
      End Sub
End Sub
The way I'm forced to do this is first name the lambda parameters incorrectly (sender, e), then paste the code, then do a rename-refactor on the lambda parameters to sender2, e2.

I think that sender2 and e2 are dumb names. I don't like being forced to type incorrect code as my first step for the sake of refactoring later on. I don't even care to do the refactoring. And if I was copy+pasting from a magazine article that didn't yet compile (rather than a working piece of code) then the chance of rename-refactoring to succeed is pretty low.
I'm not that bothered by having to come up with names for what I use. What bothers me is having to come up with names for what I don't use, like EventArgs parameters and not used senders.

I think that what this discussion leads to is the need to ignore/hide parameters. The explicit intent of the developer that that parameter is to comply with the contract but not to be internally used.

But in the case you presented, if you wanted to make that general purpose and not bound to those two buttons, you would need both senders and sender and sender2 would be lousy names.
Oct 2, 2014 at 11:43 PM
PauloMorgado wrote:
What you are describing can be looked at as someone that wrote code that can't read. That's why all recommendations say that one should be able o understand the code/algorithm just by looking at a screen length of code. The current diversity of screen sizes makes it challenging, but those recommendations were made when there were not that many screen sizes or code was read from paper - context.

You are offering arguments to never, ever, allow variable shadowing.
I would disallow variable shadowing in cases where it would have different binding semantics from variable hoisting. Or, put another way, I would not allow variables to be shadowed within their lifetimes (the time between when they are first written and when they are last read), but would allow names to be duplicated within a method in cases where such duplication would not cause a variable to be shadowed within its lifetime, even if the identifier was shadowed within its scope.

An important part of being able to understand complex algorithms is to be able to, while looking at one part of the code, make assumptions about what aspects may or may not be affected by code which is above or below the screen. People argue about #region directives on the basis that they lead people to write overly long methods, when in many cases it is the lack of such features that makes longer methods a problem.
Oct 2, 2014 at 11:53 PM
PauloMorgado wrote:
lwischik wrote:
KathleenDollard wrote:
Creating unique variable names has never, to my knowledge, been identified as a programmer pain point.
supercat identified it as a pain point, I find it a pain point, and I've raised it as a pain point in LDMs! Never enough to persuade others like you and JoshVarty who consider it a lesser pain than the alternatives, or not a pain at all. Here's a typical pain scenario:
Sub Button1_Click(sender as Object, e As EventArgs) Handles Button1.Click
End Sub

Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
   Foo(e)
   Bar(e)
End Sub
Now I want to refactor this so that button2 event handler is only signed up in response to Button1:
Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
   AddHandler Button2.Click, Sub(sender, e)
         ' copy+paste the content of Button2_Click into here
      End Sub
End Sub
The way I'm forced to do this is first name the lambda parameters incorrectly (sender, e), then paste the code, then do a rename-refactor on the lambda parameters to sender2, e2.

I think that sender2 and e2 are dumb names. I don't like being forced to type incorrect code as my first step for the sake of refactoring later on. I don't even care to do the refactoring. And if I was copy+pasting from a magazine article that didn't yet compile (rather than a working piece of code) then the chance of rename-refactoring to succeed is pretty low.
I'm not that bothered by having to come up with names for what I use. What bothers me is having to come up with names for what I don't use, like EventArgs parameters and not used senders.

I think that what this discussion leads to is the need to ignore/hide parameters. The explicit intent of the developer that that parameter is to comply with the contract but not to be internally used.

But in the case you presented, if you wanted to make that general purpose and not bound to those two buttons, you would need both senders and sender and sender2 would be lousy names.
Technically in C# if you don't need the lambda variables and there is no ambiguity (e.g. overloaded methods that also accept lambdas) then you never have to specify the parameters or assign them names, by relying on the older C# 2.0 anonymous delegate syntax:

this.Click += delegate {
    // do stuff here
};

Would be nice if there was syntax like this for lambdas.
// Throwing syntax spaghetti against the wall

// omitting any parameters, sorta like CoffeeScript
this.Click += => {
    // do stuff here
};

// Borrowing from C# 7.0 potential wildcards
this.Click += (*, *) => {
    // do stuff here
};

// or with types
this.SomeMethod((int *, string s) => {
    // do stuff here, s is in scope
});

None of this would help if you needed to use those parameters, though, but given that anonymous-delegates and lambdas are lexically scoped I do think that the existing scope rules should apply. There's no way to force the developer to explicitly specify which variable they wanted to use either inside of outside of the lambda scope, as the C# compiler requires when a local shadows a field.
Oct 3, 2014 at 8:52 AM
PauloMorgado wrote:
I'm not that bothered by having to come up with names for what I use. What bothers me is having to come up with names for what I don't use, like EventArgs parameters and not used senders.
We should be bothered about both cases. I find myself quite often in position where I need to use names like 'foo2' in code similar to the original snippet:
task.ContinueWith(task => … task …); // Same task! Why can’t I name it the same?
And wildcard (or other syntax) for unused parameters would be also a very nice thing. It should be available not only for lambdas, but for regular methods as well. I would like to be able to declare method like:
void ButtonClickHandler(object *, EventArgs *)
{
}
Halo_Four wrote:
None of this would help if you needed to use those parameters, though, but given that anonymous-delegates and lambdas are lexically scoped I do think that the existing scope rules should apply. There's no way to force the developer to explicitly specify which variable they wanted to use either inside of outside of the lambda scope, as the C# compiler requires when a local shadows a field
I would still argue, that introducing lambda is like introducing anonymous member. And members should be allowed to pick whatever name they want for their parameters. If it happens to shadow the outer variable, so be it. One should not be forced to look for different name when he does not care for outer variable inside lambda body. If one wants to use both variables, then of course he needs to choose different name. I personally encounter such situations quite rare. But I understand this can be source of issues for others. Particularly if lambda is long lambda with many statements, not short expression lambda.
Oct 3, 2014 at 12:00 PM
Przemyslaw wrote:
I would still argue, that introducing lambda is like introducing anonymous member. And members should be allowed to pick whatever name they want for their parameters. If it happens to shadow the outer variable, so be it. One should not be forced to look for different name when he does not care for outer variable inside lambda body. If one wants to use both variables, then of course he needs to choose different name. I personally encounter such situations quite rare. But I understand this can be source of issues for others. Particularly if lambda is long lambda with many statements, not short expression lambda.
The problem is that the variables of the outer method are still fully in scope and there is no notation that would allow someone scanning the code quickly to say, "oh yes, this variable reference is definitely to a local declared within the lambda and not the local variable declared within the enclosed scope," nor a syntax for the compiler to force the developers intention. If something like that existed then maybe. Of course, I would argue that if the lambda is long then it shouldn't be a lambda anyway, particularly when you don't need to enclose those variables. Have the lambda grab the variables it needs and call a proper private method, in which you can rename things all you want.
Oct 3, 2014 at 4:21 PM
Halo_Four wrote:
The problem is that the variables of the outer method are still fully in scope and there is no notation that would allow someone scanning the code quickly to say, "oh yes, this variable reference is definitely to a local declared within the lambda and not the local variable declared within the enclosed scope," nor a syntax for the compiler to force the developers intention. If something like that existed then maybe.
I agree with you that a compiler should not allow cases in which there could be meaningful confusion about what variable is being used in what situation. On the other hand, present rules forbid constructs where no confusion would be possible, or where a new syntax feature could alleviate confusion more effectively than than the use of different names.

Further, the confusion created by closures is even worse than what would be caused by even liberal shadowing, and the language would really benefit from a means by which a lambda which never modifies a variable could use the outside name and specify that it wants to capture the value rather than hoist the variable.

I would consider something like:
void someMethod()
{
  Thing it;
  ...
  myControl.BeginInvoke((MethodInvoker) ()=>(imports const it; it.DoSomething(it.SomeProperty) }
}
cleaner than
void someMethod()
{
  Thing it;
   ...
  Thing it2=it;
  myControl.BeginInvoke((MethodInvoker) ()=>(it2.DoSomething(it2.SomeProperty) }
}
The latter construct creates many possibilities for mistakes (e.g. accidental use of it instead of it2 within the lambda, erroneous declaration of it2 within the lamda, etc.) If I had my druthers, variables could only be hoisted for use within a lambda if either the variables were marked to permit hoisting or the lambda contained an imports ref declaration; values of variables could be hoisted if either the variables were declared readonly or the lambda contained an imports const declaration.
Oct 3, 2014 at 4:54 PM
Edited Oct 3, 2014 at 4:55 PM
supercat wrote:
SNIP: these replies are getting long-winded
The latter construct creates many possibilities for mistakes (e.g. accidental use of it instead of it2 within the lambda, erroneous declaration of it2 within the lamda, etc.) If I had my druthers, variables could only be hoisted for use within a lambda if either the variables were marked to permit hoisting or the lambda contained an imports ref declaration; values of variables could be hoisted if either the variables were declared readonly or the lambda contained an imports const declaration.
I kind of like how Cpp11 added an explicit syntax to define the enclosed scope down to the individual variable and behavior if desired. Cpp14 added aliasing the captured variables.
void someMethod() {
    Thing it;
    myControl->BeginInvoke([it]() { it.DoSomething(it.Property); }); // capture it explicitly as a copy
}
It's probably too late to introduce something like that to C#. Not sure that changes the scoping discussion at all anyway. Even if the capture ignored a variable in the outer function it doesn't seem proper to still allow that lambda to define new variables of the same name. Of course Cpp does because Cpp doesn't care and allows shadowing at will, even of the captured variables.

Gotta love a language where the following is perfectly legal:
[](){[](){[](){[](){}();}();}();}();
Oct 3, 2014 at 8:40 PM
How many times do you do that refactoring?

More importantly for a change that has such a massive impact on readability - how many times a day?

I think Lucian articulates the big problem though - readability is the key. Redefining variables
  • a single variable is no longer defined in a single place
  • order dependency could become quite a nightmare
  • allows laziness in naming. In your example sender and e have different meanings and current deign demands the names be found (with Button1 and Button2, 1 and 2 can't be improved, but in a real scenario there would be logical names).
  • If alternate parameter names don't make sense, the entire add handler could be a new method
Kathleen
Oct 3, 2014 at 8:48 PM
KathleenDollard wrote:
How many times do you do that refactoring?

More importantly for a change that has such a massive impact on readability - how many times a day?

I think Lucian articulates the big problem though - readability is the key. Redefining variables
  • a single variable is no longer defined in a single place
  • order dependency could become quite a nightmare
  • allows laziness in naming. In your example sender and e have different meanings and current deign demands the names be found (with Button1 and Button2, 1 and 2 can't be improved, but in a real scenario there would be logical names).
  • If alternate parameter names don't make sense, the entire add handler could be a new method
Kathleen
Agree with this. Wasn't going to jump in as I think this debate is kind of silly. Allowing you to reuse variable names gives you one benefit- it saves you a few seconds of renaming. How is the potential for hard-to-find bugs later that could cost hours or days of productivity even close to being worth it?

C# is a pit of quality language. This rule is a big piece of that and should never be touched.
Oct 3, 2014 at 8:59 PM
supercat wrote:
myControl.BeginInvoke((MethodInvoker) ()=>(imports const it; it.DoSomething(it.SomeProperty) }
Well, this is not going to be helpful, as const doesn't really give you deep constness guarantee. It helps only for simple/immutable types like int/string. But for any complex object having a const reference to it doesn't prevent calling any mutating method on it, so const wouldn't ensure that the object is not modified. On the contrary, having const would falsely suggest that the object cannot be mutated, which would lead to subtle mistakes.
Oct 3, 2014 at 9:08 PM
KathleenDollard wrote:
  • a single variable is no longer defined in a single place
  • order dependency could become quite a nightmare
If variable names were allowed to be reused in a scope provided that they were not reused within a variable's lifetime, then the question of whether two pieces of code were using a "single variable" would be moot. I believe that a variable will only be written once in its lifetime, that value should be written at the point of declaration. I also believe that variables' syntactic lifetimes should be as short as possible. Unfortunately, the rules of scope do not allow both goals to be met, since there is no way for the scope of any variable used in a declaration's initialization expression to end before the scope of the newly-declared variable.

What's needed most fundamentally is a means of constraining the syntactic lifetime of a variable without having to constrain the syntactic lifetime of other variables which are computed using its value. Perhaps one could say that undef foo; will forbid the usage of the currently-defined scope's foo in code that comes later in either source-code or execution-path order [such a statement would create a variable foo of type undef only if no variable of type foo already exists, but a class undef does]. In that case, having to use multiple names for similar usages in different parts of a method would be ugly, but would at least avoid any possibility that downstream code might erroneously use a variable whose syntactic lifetime far exceeds the its intended lifetime.
Jan 7, 2015 at 11:32 AM
Love all these new features and there's one more that, in my opinion, should have been implemented since the introduction of the Generics: the possibility to include parameter types in constructor constraints. That is, I should be able to do something like this:
public static class Factory
{
     public static T Create<T>() where T: class, new(string,int) {
          return new T("this is a sample string value", 2015);
     }
}
This would be much cleaner that having to use Activator.CreateInstance of having to invoke the constructor through Reflection.