Reputation: 8123
Why is C# does not support alpha-conversion?
int n = 3;
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.Out.WriteLine("N value = " + n);
Yield:
A local variable named 'n' cannot be declared in this scope because it would give a different meaning to 'n', which is already used in a 'parent or current' scope to denote something else
Is there any particulate reason that I am not aware of because it sound very silly?
Upvotes: 1
Views: 642
Reputation: 10643
The proper term is "shadowing", as in "C# does not allow one local variable to shadow another."
There are no technical reasons for the restriction. Shadowing does not cause problems for type checking or code generation.
I believe the motivation for this restriction is purely ergonomic. I expect that when the folks at Sun were designing Java (note: Java had this restriction too, and MS probably said eh sounds reasonable and put it in C# too) they figured that shadowing would trip up the programmers that they wanted to steal from the C and C++ communities. And they probably figured that the Lispers and Smalltalkers and MLers and whatnot would either live with it or refuse to use such a low-level pedestrian language anyway.
And I kind of agree with their assessment of the ergonomic issues. When you're modifying a large block of code it's possible to inadvertently capture variable references if you add a new local binding. In other words, textual program edits are not properly lexically scoped.
Upvotes: 0
Reputation: 659956
First off, your test for oddness is wrong.
The answer to your question is to deny the premise of your question. This has nothing whatsoever to do with alpha conversion.
Nor does it have anything to do with "lexical scoping", by which leppie seems to mean something different than my understanding of lexical scoping. C# is a lexically scoped language.
Now, I want to emphasize that it is illegal to declare two locals in C# where one hides the other. It is perfectly legal to hide in other scopes; a type parameter may hide an outer type parameter (though doing so is really, really dumb; do not do that.) A field may hide a base class field (though you should mark the hiding field as 'new', to emphasize that fact.) A local may hide a method. And so on.
But a local may not hide another local, because (1) doing so makes bug farms, and (2) it violates the more general rule about usage of simple names.
That rule about names is the interesting rule here. You would get a similar error if you did this:
class C
{
int n;
void M()
{
Console.WriteLine(n); // n means this.n
Func<double, double> f = n=>n; // n means the formal parameter
}
}
The error you are getting is because you are violating the rule of C# that a simple name must have a consistent meaning throughout the local scope in which it is first used.
Programs where 'n' means one thing on one line and something completely different on the next are confusing and bug-prone, and therefore illegal.
If you want to do that then the two meanings of 'n' have to be in non-overlapping scopes:
class C
{
int n;
void M()
{
{
Console.WriteLine(n); // n means this.n
}
Func<double, double> f = n=>n; // n means the formal parameter
}
}
That would be legal because now the two usages of n are in non-overlapping scopes.
The problem has nothing whatsoever to do with alpha conversion. C# does alpha conversion just fine when it needs to.
And it is because C# is lexically scoped that the compiler can determine that you are violating this rule. This is not evidence that C# lacks lexical scoping; it is evidence that it has lexical scoping.
For more thoughts on this rule, see my article on the subject:
http://blogs.msdn.com/b/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx
Upvotes: 11
Reputation: 21521
The reason why this occurs is in C# lamda functions are not declared in a scope. They are able to access variables inside the parent scope. For instance, the following code allows your lambda to access the variable n:
int outerN = 3;
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(localN => localN % 2 == outerN);
Console.Out.WriteLine("N value = " + outerN);
You should treat variables inside a lambda function as being in the same scope as the method they are declared in.
From MSDN:
Lambdas can refer to outer variables that are in scope in the enclosing method or type in which the lambda is defined. Variables that are captured in this manner are stored for use in the lambda expression even if variables would otherwise go out of scope and be garbage collected. An outer variable must be definitely assigned before it can be consumed in a lambda expression.
The following rules apply to variable scope in lambda expressions:
A variable that is captured will not be garbage-collected until the delegate that references it goes out of scope.
Variables introduced within a lambda expression are not visible in the outer method.
A lambda expression cannot directly capture a ref or out parameter from an enclosing method.
A return statement in a lambda expression does not cause the enclosing method to return.
A lambda expression cannot contain a goto statement, break statement, or continue statement whose target is outside the body or in the body of a contained anonymous function.
Upvotes: 0
Reputation: 117220
This is not really alpha conversion.
The problem is that C# does not have proper lexical scoping, which would imply that variables could be shadowed in inner scopes.
Upvotes: 1