Introduction to Programming Languages/Closures

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Closures[edit | edit source]

A closure can be implemented as a pair (f, t) of pointers. The first element, f, points to the implementation of a function. The second element, t, points to a table containing the free variables used in the body of the function.

A closure is an implementation of a function, plus a table that binds values to the free variables that appear in the body of the function. A variable v is free in a function body f if v is used inside f, but it is not declared in f. Closures give the developer a way to pass functions around, together with some information about the context where these functions were created. Pragmatically speaking, closures allow the developer to write factories of functions. For instance, below we have a Python function that produces unary sums:

def unarySumFactory(a):
  def unarySum(b): return a + b
  return unarySum  
                 
inc = unarySumFactory(1)
  
print inc(2)
  
sum2 = unarySumFactory(2)
  
print sum2(2)

In the above example we notice that variable a is free in the body of function unarySum. This variable has been declared in the scope of the function unarySumFactory, and the fact that it can be referenced inside unarySum poses to language designers an implementation problem. Normally once a function returns its value, the space reserved for its activation record is deallocated. However, if this space is deallocated, variable a in our example would no longer have a storage location once unarySumFactory had returned a value. To circumvent this difficulty, closures are implemented as pairs (f, t), where f is a pointer to the implementation of the function, and t is a pointer to a table with all the free variables used in f associated with values.

Because the closure might outlive the function that has created it, normally the pair (f, t), and the contents of table t are allocated in the heap. The code below, written in C, implements the call unarySumFactory seen before. C does not have syntactic support for closures. Nevertheless, we can implement closures by combining high-order function calls with dynamic heap allocation.

#include <stdio.h>
#include <stdlib.h>
 
typedef struct struct_free_variables_unarySum {
  int x;
} FREE_VARIABLES_unarySum;
 
typedef struct {
  FREE_VARIABLES_unarySum* t;
  int (*f)(FREE_VARIABLES_unarySum* t, int x);
} CLOSURE_unarySum;
 
int unarySum(FREE_VARIABLES_unarySum* t, int y) {
  return y + t->x;
};
 
void* unarySumFactory(int x) {
  CLOSURE_unarySum* c = (CLOSURE_unarySum*) malloc (sizeof(CLOSURE_unarySum));
  c->t = (FREE_VARIABLES_unarySum*) malloc (sizeof(FREE_VARIABLES_unarySum));
  c->t->x = x;
  c->f = &unarySum;
  return c;
}
 
int test(int n) {
  CLOSURE_unarySum* c = unarySumFactory(2);
  int retVal = c->f(c->t, n);
  free(c->t);
  free(c);
  return retVal;
}
 
int main(int argc, char** argv) {
  printf("%d\n", test(argc));
  return 0;
}

Definition and Examples · Partial Application