Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function pointers, Closures, and Lambda

I am just now learning about function pointers and, as I was reading the K&R chapter on the subject, the first thing that hit me was, "Hey, this is kinda like a closure." I knew this assumption is fundamentally wrong somehow and after a search online I didn't find really any analysis of this comparison.

So why are C-style function pointers fundamentally different from closures or lambdas? As far as I can tell it has to do with the fact that the function pointer still points to a defined (named) function as opposed to the practice of anonymously defining the function.

Why is passing a function to a function seen as more powerful in the second case, where it is unnamed, than the first where it is just a normal, everyday function that is being passed?

Please tell me how and why I am wrong to compare the two so closely.

Thanks.

like image 522
None Avatar asked Oct 16 '08 14:10

None


2 Answers

A lambda (or closure) encapsulates both the function pointer and variables. This is why, in C#, you can do:

int lessThan = 100; Func<int, bool> lessThanTest = delegate(int i) {    return i < lessThan; }; 

I used an anonymous delegate there as a closure (it's syntax is a little clearer and closer to C than the lambda equivalent), which captured lessThan (a stack variable) into the closure. When the closure is evaluated, lessThan (whose stack frame may have been destroyed) will continue to be referenced. If I change lessThan, then I change the comparison:

int lessThan = 100; Func<int, bool> lessThanTest = delegate(int i) {    return i < lessThan; };  lessThanTest(99); // returns true lessThan = 10; lessThanTest(99); // returns false 

In C, this would be illegal:

BOOL (*lessThanTest)(int); int lessThan = 100;  lessThanTest = &LessThan;  BOOL LessThan(int i) {    return i < lessThan; // compile error - lessThan is not in scope } 

though I could define a function pointer that takes 2 arguments:

int lessThan = 100; BOOL (*lessThanTest)(int, int);  lessThanTest = &LessThan; lessThanTest(99, lessThan); // returns true lessThan = 10; lessThanTest(100, lessThan); // returns false  BOOL LessThan(int i, int lessThan) {    return i < lessThan; } 

But, now I have to pass the 2 arguments when I evaluate it. If I wished to pass this function pointer to another function where lessThan was not in scope, I would either have to manually keep it alive by passing it to each function in the chain, or by promoting it to a global.

Though most mainstream languages that support closures use anonymous functions, there is no requirement for that. You can have closures without anonymous functions, and anonymous functions without closures.

Summary: a closure is a combination of function pointer + captured variables.

like image 137
Mark Brackett Avatar answered Nov 13 '22 20:11

Mark Brackett


As someone who has written compilers for languages both with and without 'real' closures, I respectfully disagree with some of the answers above. A Lisp, Scheme, ML, or Haskell closure does not create a new function dynamically. Instead it reuses an existing function but does so with new free variables. The collection of free variables is often called the environment, at least by programming-language theorists.

A closure is just an aggregate containing a function and an environment. In the Standard ML of New Jersey compiler, we represented one as a record; one field contained a pointer to the code, and the other fields contained the values of the free variables. The compiler created a new closure (not function) dynamically by allocating a new record containing a pointer to the same code, but with different values for the free variables.

You can simulate all this in C, but it is a pain in the ass. Two techniques are popular:

  1. Pass a pointer to the function (the code) and a separate pointer to the free variables, so that the closure is split across two C variables.

  2. Pass a pointer to a struct, where the struct contains the values of the free variables and also a pointer to the code.

Technique #1 is ideal when you are trying to simulate some kind of polymorphism in C and you don't want to reveal the type of the environment---you use a void* pointer to represent the environment. For examples, look at Dave Hanson's C Interfaces and Implementations. Technique #2, which more closely resembles what happens in native-code compilers for functional languages, also resembles another familiar technique... C++ objects with virtual member functions. The implementations are almost identical.

This observation led to a wisecrack from Henry Baker:

People in the Algol/Fortran world complained for years that they didn't understand what possible use function closures would have in efficient programming of the future. Then the `object oriented programming' revolution happened, and now everyone programs using function closures, except that they still refuse to to call them that.

like image 32
Norman Ramsey Avatar answered Nov 13 '22 20:11

Norman Ramsey