开发者

Bug-free coding with closures (in a non pure virtual environment)

Last days, I am struggling to understand closures. I am very big fan of C# so my main testbed is this language, so I would like to learn about it's closure support. As I studied and experimented, I found out that many people, when trying to blog about closures, they do it by following a completely wrong direction. They project a certain buggy use of closures like the well known for-stateme开发者_JAVA百科nt and they are trying to explain it. Instead I would like to see a mathematical approach (First-class citizen, free/bound variables, lambdas, etc). However this makes me consider that I would like to know what bugs may come up when coding without closures in mind.

Furthermore, do all languages have the same interpretation of the mathematical construct of closures?

I didn't have an FP course nor an advanced programming languages in uni. But I know the role of side effects in the procedural code and their inexistent one at pure virtual languages. Are closures in C# just a trick? What (e.g.) F# closures have more than C# closures?


first of all, I think there is some confusion on what should be called closure and what should be called lambda function. I believe that the correct approach is to call the syntactic element in the language (e.g. (a, b) => a + b in C#) lambda function. The created value is function value or a delegate in C#.

As implemented in .NET (both F# & C#), the delegate is actually a reference to some method in some class. The reason is that the delegate constructed using a lambda function syntax may need to keep some state:

Func<int, int> CreateAdder(int num) {
  return arg => arg + num;
}

The returned delegate references some (unnamed) object, which stores the num value and also the body of the lambda function. So, what is a closure? Closure is the object, which keeps the state needed to run the function value (or delgate). In this case, it is the unnamed object, which is referenced from the delegate and keeps the value of num.

You also mentioned free and bound variables. If you look at the lambda function in the example above, it works with two variables. The variable arg is declared as part of the lambda function. This would be called bound variable (in the lambda function), because it is declared as part of the lambda function. The num variable would be called a free variable in the lambda function, because it is only used (but not declared!) in the scope of the lambda function. It comes from the outer scope (in this case, the method declaration).

The closure needs to capture all free variables in the lambda function. This means that all variables that are used inside the body, but are declared somewhere else are captured. However, the C# compiler doesn't just copy the current value. It turns it into a mutable field, so that it can be accessed (and also mutated) from all functions that may access it (and this is also the place where it becomes tricky). This would be a topic for a long blog post, but here is a brief example that you can use to experiment with this (using Tuple from .NET 4.0, if you're using VS 2008, you can get C# implementation here in Chapter03/FunctionalCSharp):

Tuple<Func<int>, Action<int>> CreateReaderAndWriter(int initial) {
   int state = initial;
   return Tuple.Create( (() => state),
                        (newState => { state = newState; }) );
}

When you'll call this method, you'll get two functions as a result. The first one allows you to read the current state and the second one allows you to modify it. Note that the state is shared (because it is the same mutable variable)!

There has been a proposal by Don Syme from MSR to add support for closures directly to .NET. It is a bit academic, but it may help clarifying things a bit.


The concept of a closure is fairly consistent across languages, although in imperative languages there's some disagreement about how constructs such as continue, break, and return within a closure should be handled (e.g. some of the proposals for adding closures to Java behave differently in this respect than C# does). The main subtlety which catches people is that in non-pure languages, closures "close over" variable bindings rather than values, which means that things like variable scoping are very important (and this is where unexpected difficulties arise in the for-loop examples, since the scope of the loop variable is not always what people expect).

F#'s behavior is quite similar to C#'s, but there are a few things that make closures slightly nicer to work with in F#. For one, although F# is impure, mutation is discouraged, so it's harder to write a closure which inadvertently closes over a variable which is later modified in a way that breaks expectations. In particular, the F# compiler will not allow a normal mutable binding to be used inside of a closure - the compiler's error message suggests that you either make the binding immutable or use an explicit reference cell (type 'a ref in F#) if you actually intend to close over a binding which can be mutated. This forces the user to think carefully about what is trying to be achieved.


It sounds to me like you want to learn functional programming in general. Doing that, you can't avoid learning the "right" way to use closures because they are so central to functional programming.

Unfortunately I don't know a good functional programming reference for C#. Searching a bit turns up this intro article: Introduction to Functional Programming in C#.

If you don't mind working with another language, you might consider The Little Schemer. It uses Scheme, but uses only the parts you actually need for the book. It is easy to follow but delves directly into to the hard parts of functional programming.

As for your other question, I have found that if you don't mutate variables, closures behave the same in most languages--even Java's anonymous inner classes. (Although, like kvb said, it is true that functional languages like F# and Haskell prevent you from mistakenly mutating a variable when you didn't mean to.)


Have you read Martin Fowler's discourse on this topic? It seems to cover your concerns and comes from an fairly authoritative figure: http://martinfowler.com/bliki/Closure.html

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜