tags:

views:

180

answers:

5

I have the following code (generates a quadratic function given the a, b, and c)
Func<double, double, double, Func<double, double>> funcGenerator = (a, b, c) => f => f * f * a + b * f + c;
Up until now, lovely.
But then, if i try to declare a variable named a, b, c or f, visual studio pops a "A local variable named 'f' could not be declared at this scope because it would give a different meaning to 'f' which is used in a child scope."
Basically, this fails, and I have no idea why, because a child scope doesn't even make any sense.

Func> funcGenerator = (a, b, c) => f => f * f * a + b * f + c;
var f = 3; // Fails
var d = 3; // Fine

What's going on here?

+8  A: 

It means what it says. You're not allowed to use the same variable name in the lambda scope and the scope containing the lambda. The child scope is the scope of the lambda.

Matthew Flaschen
How does that make any sort of sense? There is no child scope. Child scope implies another set of {} in which a different variable is introduced, but that doesn't happen here. The function definition ended already.
Rubys
if you introduce f in the outer scope, it is available inside the lambda, which does give a different meaning to f. In what way is this unclear?
flq
Rubys, there *is* a child scope. A scope does not require a `{}` in C#. It doesn't matter that the lambda definition textually above the `var f`.
Matthew Flaschen
Well, the variable is declared after the lambda. I was going to say that it's the same in a for loop, but I looked into it and it seems I'm wrong. Learn something new every day, I guess. Cheers.
Rubys
A: 

A variable is declared inside the scope of a function. Since the lambda is compile/rewritten to some code inside that same function, it makes some kind of sense. I think the for loop where you define a variable as "first argument" of the for loop is the only exception.

Although I can imagine that it would be handy if you could reuse the variable names.

Henri
If by "handy" you mean "an endless source of hard to spot bugs", then I agree.
Iceman
+11  A: 

I think what you're misunderstanding is that the order of declarations does not matter to the C# compiler with respect to scoping rules.

This:

Func<double, double, double, Func<double, double>> funcGenerator =
    (a, b, c) => f => f * f * a + b * f + c;  
var f = 3;
var d = 3;

Is exactly the same as this:

var f = 3;
Func<double, double, double, Func<double, double>> funcGenerator =
    (a, b, c) => f => f * f * a + b * f + c;  
var d = 3;

Scopes aren't order-sensitive. You have a local variable named f, and you are trying to declare another variable named f inside the lambda. This is illegal according to the C# spec.

Specifically, it would conflict with the ability of lambdas to do variable capturing. For example, this code is legal:

int x = 3;
Func<int> func = () => x + 1;

This is completely legal and executing func() will return 4. That's why you can't declare another variable x here inside the lambda - because the lambda actually needs to be capable of capturing the outer x.

Just change the name of one of the f variables.

Aaronaught
+1  A: 

The reason the lambda's parameters and variables from the enclosing scope are in the same "namespace" (in the loose name-binding sense, not the language-feature sense) is because lambdas can close over (refer to) variables from the enclosing scope. The lambda's parameters must be distinguishable from the variables that the lambda can see:

int x;
Action a = () => { x = 3; };

That's okay, it assigns 3 to the outer x.

int x;
Action<int> a = x => { x = 3; };

That's no good - which x are we assigning 3 to?

In your example the only difference is the order of declaration. This would produce an error

Action a = () => { x = 3; };
int x = 2;

The compiler would say you can't refer to x before it's declared. If you then make the lambda take a parameter with the same name, we arrive at roughly your example:

Action<int> a = x => { x = 3; };
int x;

If that worked, the compiler would basically be following a rule of the form: "try the various possible interpretations of the code, and whichever one isn't erroneous, assume it's the intended meaning". C# takes a slightly safer approach, and expects you to be specific and unambiguous rather than relying on such rules to "pick a winner".

Daniel Earwicker
+5  A: 

It can be quite tricky to work out exactly which rule of C# you've violated. As a public service I've written this handy guide that explains the differences between some of the more easily-confused scoping rules:

http://blogs.msdn.com/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx

http://blogs.msdn.com/ericlippert/archive/2009/11/05/simple-names-are-not-so-simple-part-two.aspx

Also, you seem a bit unclear on the concept of "scope" -- which is not surprising, since in most books the word is used to mean pretty much whatever the author wants. In C# we carefully define "scope" as "the region of program text in which a particular entity may be referred to by its unqualified name". So for example, in

namespace A 
{
  public class B 
  {
      private int c;
      protected int d;
      public void E(int f)
      {
         int g = f;
         Func<int, int> h = i => g * i;
      }
  }
  public class K : B { }
}

The scope of A is everywhere. The scopes of B and K are everywhere inside a declaration of A. The scope of c is everywhere inside B. The scopes of d and E are the contents of B, K and any class derived from B or K. The scopes of f and g are the body of E. The scope of h is the body of the lambda.

Notice that there is a difference between scope and accessibility domain. B, K and E are accessible everywhere, but are only in scope in particular locations.

Eric Lippert