4 ms·
Here's yet another stab at explaining closures using C. Hopefully this is clear. The confusion about closures happens because it doesn't exist in other languag
by rickmode 15y ago
Here's yet another stab at explaining closures using C. Hopefully this is clear.
The confusion about closures happens because it doesn't exist in other languages. (There isn't a way to express it.)
Let's look at a simple C function that uses a global variable:
int a = 1;
int a_plus_2() {
return 2 + a;
}
This always returns 2 more than "a". Initially it will return 3. If "a" is set to 5 at some point in the future, "a_plus_2" will then return 7. Simple. No closure here.
In Lisp, "a" would be called a "dynamic variable" because it changing its value affects all code that uses "a" over time, including usage within other scopes; "a" has a dynamic binding. But Lisp also has static binding which means the value of the variable is captured each time a scope is created and remains static over time within that scope.
Imagine a variation of C where variable make this distinction explicit by prefixing with with <dynamic> and <static>. <dynamic> would be the default and in fact is how the real C behaves. <static> now means Lisp notion of static binding (forget C's notion of static, which is totally different). Let's use it.
<static> int b = 1;
int b_plus_2() {
return 2 + b;
}
So here "b" is marked as static. When "b_plus_2" is defined at compile time, the current value of "b" is captured - statically bound - to its current value of 1. So "b_plus_2" will always return 3 _even if the global variable "b" is later modified.
b = 5; /* modifying b */
int new_b_plus_2 {
return 2 + b;
}
Calling "new_b_plus_2" return 7. No surprise here. The twist is that the original "b_plus_2" still returns 3. The value of "b" is captured in a closure. Each new scope encloses the current value of "b", binding its current value statically (unchanging) within that scope. Closures become interesting when the block in question is a function body.
So, yeah - it's a simple concept in the end. It's tricky because it doesn't exist many other languages. It provides a way to encapsulate state analogous to an object in OO languages [1].
The concept is more useful in Lisp with its higher order functions - functions that define other functions. This is very fluid in Lisp, while Python uses its lambda form for anonymous functions, Ruby has blocks and lambdas and...[2], and Java gets close with inner classes.
I hope this helps.
[1] "Objects are a poor man's closure" and "Closures are a poor man's object" http://stackoverflow.com/questions/2497801/closures-are-poor-mans-objects-and-vice-versa-what-does-this-mean http://stackoverflow.com/questions/2497801/closures-are-poor...
[2] I'm don't do Ruby - but's here's a ton of stuff on closures in Ruby: http://innig.net/software/ruby/closures-in-ruby.rb http://innig.net/software/ruby/closures-in-ruby.rb
- Tycho 15y agoThanks very much for that. I get it better now. I think. The SO link helped also, but the Ruby one made my head hurt. What I'm taking from this so far is that 1. Closures preserve any enclosed variables from the original context where the closure was made (defined), rather than using the context of the call. 2. If you didn't have objects, you could use closures instead to capture the 'state' of different instances 3. With some trickery, you can use closures to introduce lazy evaluation. Which is good for computationally expensive functions that you want to forget about until needed. I'm not sure if I'm right about those though. And I'd probably steer clear of deliberately exploiting closures cause I get a whiff of 'clever code' from the whole subject