Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why the second writeline print out 12 when using lambda?

code below

    int factor = 2;
    Transformer sqr = x => x * factor;
    Console.WriteLine(sqr(3)); // 6
    factor = 4;
    Console.WriteLine(sqr(3)); // 12

I think lambda should capture factor in the compile time, so the result should be the same for the two writeline.

However, when I run, I get 6 and 12, so does the C# lambda use dynamic scoping? I thought lambda should use something called "lexical scope"

like image 274
Adam Lee Avatar asked Mar 18 '23 07:03

Adam Lee


2 Answers

First of all you cannot capture anything at compile time. The environment for the lambda can only be captured at runtime, because that's the only time when it exists.

When a variables is captured by a lambda then the lambda has in effect direct access to that variable for the entirety of its lifetime; changes to the value from outside will be seen by the lambda (google "access to modified closure") and the reverse is also true.

It follows that if you want to isolate the lambda from outside interference you have to make it capture something that noone from the "outside" will have access to -- that means a variable whose scope has been sufficiently limited.

Consider:

int factor = 2;
Func<int, Func<int, int>> generateTransformer = f => x => x * f;
Func<int, int> sqr = generateTransformer(factor);

Console.WriteLine(sqr(3)); // 6
factor = 4;
Console.WriteLine(sqr(3)); // 6

What happens here is that sqr is the result of calling generateTransformer and it is capturing the value of the local variable f inside the body of generateTransformer. That value can never be changed by anyone because it cannot be seen by anyone outside the body of generateTransformer. Calling generateTransformer makes a copy of the current value of factor and gives that "frozen" copy to sqr to use.

like image 53
Jon Avatar answered Apr 01 '23 05:04

Jon


It does capture factor. It captures it as a member variable of a hidden closure class, along with a method for the lambda. You end up with something approximately similar to

private class sqrClosure
{
    public int factor;
    public int srq(int x)
    {
        return x * factor;
    }
}

...

var c = new sqrClosure();
c.factor = 2;
Console.Writeline(c.sqr(3));
c.factor = 4;
Console.Writeline(c.sqr(3));
like image 22
Rawling Avatar answered Apr 01 '23 05:04

Rawling