Anonymous Method is Not Closure

C# 2.0 has *tried* to embrace part of functional programming concept, the closure, and was implemented as anonymous method, which then evolved into Lambda Expression in C# 3.0. But why aren’t they closure? Does C# have true closure? Most people know the answer: NO. But few understand why. Despite its similar concept, there are some subtle differences between closure and anonymous method.

Stateful vs Stateless

The main difference between closure and anonymous method is that closure is stateful. A code is worth a thousand words:

string name = “World”;

 

HelloDelegate hello = delegate {

Console.WriteLine(“Hello, “ + name); };

 

name = “Sheep”;

hello();

A simple hello-world application, using a C# 2.0 anonymous method. What would the output be? Unlike other hello-world applications, this code will actually print “Hello, Sheep”!

But on closure, the same pseudocode will output “Hello, World”! A quick explanation is that a true closure preserves the lexical state when it is created. It indicates that the method is “closed” from its lexical environments, hence “closure”.

Whereas in C# anonymous type, the value of name when the delegate was created is NOT preserved, and the last value of name (“Sheep”) is used instead.

Instance State

This is another way to look at stateful characteristic of closure compared to anonymous method.

string name = “World”;

 

HelloDelegate hello = delegate {

       name += “!”;

       Console.WriteLine(“Hello, “ + name); };

 

HelloDelegate hello2 = delegate

{

       name += “!”;

       Console.WriteLine(“Hello2, “ + name);

};

 

hello();

hello();

hello();

hello2();

hello2();

hello2();

Console.WriteLine(“Finally: “ + name);

In C#, it will print this output:

Hello, World!

Hello, World!!

Hello, World!!!

Hello2, World!!!!

Hello2, World!!!!!

Hello2, World!!!!!!

Finally, World!!!!!!

But in Ruby’s closure, the result is:

Hello, World!

Hello, World!!

Hello, World!!!

Hello2, World!

Hello2, World!!

Hello2, World!!!

Finally, World

Note that in Ruby, each closure instance remembers its own ‘local-state’. A change on name variable on hello will affect the name variable on neither hello2 nor the environment.

Currying

You can curry a closure, but you can’t with normal C# anonymous method. No, we are not talking about some kind of tasty Thai food. Straight to the example:

ConcatDelegate concat =

delegate (string a, string b)

{

       Console.WriteLine(a + b);

};

In Ruby, one can dynamically *customise* the closure by hardwiring the first parameter as “Hello” for example. Thus calling helloConcat(“world”) will print out “Hello, world”. There is no native support on C# 2.0 to let you derive method definitions in this fashion. Wesdyer wrote on his blog how we can achieve this using Lambda Expression in C# 3.0.

In C# 2.0, LinFu provides support for this closure feature on .Net delegate. LinFu is a fairly large general-purpose AOP-centric framework by itself, and closure is only a very small part of it. This is how you can achieve currying with LinFu:

ConcatDelegate concat =

delegate (string a, string b)

{

       Console.WriteLine(a + b);

};

 

Closure hello = new Closure(

concat, “Hello, “, Args.Lambda);

 

hello.Invoke(“world”);

In the code above, we derive a new method and set the first parameter as “Hello”. Args.Lambda tells the closure to replace with actual value passed to the Invoke method when called. More on LinFu closure can be checked out here.

I will post specific blogs about LinFu in due course. I’m pretty much a new fansboy of LinFu. LinFu rocks!

Advertisements

5 thoughts on “Anonymous Method is Not Closure

  1. Eh, no. In your first example, it preserves the original scope of name. If it printed “hello, world” it would be creating a new scope; ie., a copy of name. That’s not what a true lexical closure does.

    Same situation with your second example. Both delegates refer to the same outer scope.

    If you want to see what I’m talking about, create a function which takes name as a parameter, and returns a delegate that takes no parameters and returns a name. Then change the variable in the outer scope that you passed into your function. You’ll find that in this situation the delegate returns “world,” not “sheep.”

    There was a controversy a while back about whether C# had true lexical closures. It was resolved when people realized that C# closures work exactly like Lisp closures. Since Lisp invented the concept, that was a compelling argument. If Ruby works differently, then it’s Ruby that doesn’t have true closures.

    By preserving the original scope, rather than making a copy, you can have several functions that refer to a common variable. This is the feature that allows a Lisp programmer to easily implement object orientation, even though it’s not built into the language.

  2. p.s. I’m not a Ruby programmer, but I’ve read that Ruby has a variety of closure-like constructs, which don’t all act quite the same way…I think Ruby does in fact have true lexical closures in there somewhere.

  3. Jeo,
    I was a bit surprised to know the “true” closure (as introduced by Lisp) didn’t create a closed scope. Thus .net’s anonymous method (and lambda expression for that matter), is indeed… the true closure(?)
    I guess the title should have been “Ruby” closure…. which is easily the most widely known definition of closure in our urban dictionary these days 🙂
    Thanks for clarifying.

  4. It closes over the original scope, it doesn’t create a copy of the scope. This can accomplish what your ruby code does, if you create the scope you need yourself, as in:

    function HelloDelegate hi(name)
    return delegate {Console.WriteLine(“Hello, “ + name); };

    x = “world”;
    HelloDelegate hello = hi(x);
    x = “sheep”;

    hello();

    //prints “world”

  5. Well…. in this case, it’s actually the Hi method that actually “closes” the value. Or more precisely, it’s a pass-by-value by definition.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s