csharplang/meetings/2016/LDM-2016-11-30.md
2017-01-31 10:38:26 -08:00

5.1 KiB

C# Language Design Notes for Nov 30, 2016

Agenda

  • Scope of while condition expression variables
  • Mixed deconstruction
  • Unused expression variables
  • Declarations in embedded statements
  • Not-null pattern

Scope of while condition expression variables

We are compelled by the argument in #15529 about the scope of expression variables occurring in while conditions.

while (src.TryGetNext(out int x))
{
	// Same or different `x` each time around?
}
// x in scope here?

More broadly we seem to have the following options for scopes and lifetimes of such declarations:

  1. There is a single variable x for the whole while statement, which is initialized repeatedly, each time around. It is in scope outside the while statement.
  2. There is a single variable x for the whole while statement, which is initialized repeatedly, each time around. It is only in scope inside the while statement.
  3. There is a fresh variable x for each iteration of the while statement, initialized in the condition. It is only in scope inside the while statement.

There is a clear argument for the lifetime of the variable being a single iteration. We know from foreach that this leads to better capture in lambdas, but perhaps more importantly it avoids the odd behavior of the same variable getting initialized multiple times. (Pretty much the only way to otherwise achieve that in C# is through insidious use of goto).

This puts us squarely in option 3 above, where it is meaningless to put "the" variable in scope outside of a while loop. Indeed, from the outside there is no notion of "the" variable: there will be multiple xes over the execution of the loop.

Consequences in for loops

In light of a changed while loop we also need to examine what that means to the for loop, which, while not defined as such in the spec, can be informally understood as "desugaring" into a while loop.


for (<decl>; <cond>; <incr>) <body>

==>

{
    <decl>
    while(<cond>)
    {
        <body>
	cont:
        { <incr> }
    }
}

So with respect to scopes and lifetimes, <cond> should behave the same as the condition in a while loop.

Similarly, <incr> should be a fresh variable each time around. Furthermore it should occur in its own little nested scope, since variables introduced in it won't be definitely assigned until the end of each iteration.

Conclusion

We want to change to the narrow scopes and short lifetime for expression variables introduced in the condition of while and for loops, and in the increment of for loops.

This means that the if statement becomes more of a special case, with expression variables in its condition having broad scope. That is probably a good place to land. The main scenarios for this behavior are driven by the if statement to begin with, and it is also the one kind of statement where the lifetime and definite assignment situation for such variables makes it reasonable for them to persist outside of the statement.

Mixed deconstruction

The reinterpretation of deconstruction in terms of declaration expressions would let us generalize so that:

  • Would allow mixing existing and newly declared variables in a deconstruction
  • Would allow newly declared variables in deconstruction nested in expressions
  • No error when occuring as an embedded statement

Conclusion

From a language design point of view we are confident in this generalization of the deconstruction feature. It is doubtful whether the change can make it into C# 7.0 at this point. Even though it is a small code change in and of itself, it introduces testing work and general churn.

Warn about unused expression variables?

Should we warn when an expression variable goes unused? Typically such variables would be dummy variables, and with discards _ you no longer need them.

On the other hand, we could leave it to the IDE to suggest discards etc, rather than give a warning from the language.

Conclusion

Let's not add the warning. Keep the warning in place where it already is in existing C#.

Declarations in embedded statements

C# currently has a grammatically enforced restriction against declaring "useless" variables as "embedded statements":

if (x > 3) var y = x; // Error: declaration statement not allowed as an embedded statement

Of course expression variables now give you new ways of similarly declaring variables that are immediately out of scope and hence "useless". Should we give errors for those new situations also?

Conclusion

Let's not add errors for deconstruction situations. In principle we probably want to remove even the existing limitation, but it's work we won't push for in C# 7.0.

Pattern to test not-null?

It's been suggested to have a pattern for testing non-nullness (just as there is a null pattern for checking nullness).

Conclusion

No time in C# 7.0 but a good idea.