saturday, 19 august 2006

posted at 14:18

Back in November 2005, I was learning alot about Javascript. I'd used closures (essentialy anonymous functions) in Perl for years without thinking about it, but the way Javascript scopes variables makes them not work quite the same, so I was suddenly forced to think about closures in more detail.

I had this on the old website and would probably have been content to let it die there, but for two things Daniel did this week:

  • He pointed the Rails IRC channel at it, and I just can't ignore that kind of exposure.
  • He dared me to make function currying work in C, and the two are related enough that they should be nearby.

So consider this use of closures in Perl:

@c = ();
for($i = 0; $i < 10; $i++) {
    my $x = $i;
    push @c, sub { print "x is $x\n" };
}
$_->() for @c;

Or the equivalent in Javascript:

c = [];
for(i = 0; i < 10; i++) {
    var x = i;
    c.push(function () { print("x is " + x) });
}
for(f in c)
    c[f]();

The C version would look like this:

#include <stdio.h>
#include "closure.h"

int main(int argc, char **argv) {
    closure c[10];
    int i, x;

    for(i = 0; i < 10; i++) {
        CLOSURE_INIT(c[i]);
    }

    for(i = 0; i < 10; i++) {
        CLOSURE_START(c[i]);
            int x = i;
            printf("x is %d\n", x);
        CLOSURE_END(c[i]);
    }

    for(i = 0; i < 10; i++) {
        CLOSURE_CALL(c[i]);
    }

Compile and run:

% gcc -Wall -ggdb -o closure closure.c
% ./closure 
x is 0
x is 1
x is 2
x is 3
x is 4
x is 5
x is 6
x is 7
x is 8
x is 9

closure.h provides the magic:

#ifndef __CLOSURE_H
#define __CLOSURE_H 1

#include <ucontext.h>

typedef struct closure {
    ucontext_t  enter;
    ucontext_t  exit;
    int         inside;
} closure;

#define CLOSURE_INIT(c) \
    c.inside = 0;

#define CLOSURE_START(c) \
    getcontext(&c.enter); \
    if(c.inside) { \
        ucontext_t __closure_end;

#define CLOSURE_END(c) \
        swapcontext(&__closure_end, &c.exit); \
    }

#define CLOSURE_CALL(c) \
    c.inside = 1; \
    swapcontext(&c.exit, &c.enter); \
    c.inside = 0;

#endif

I hope to be able to have an implementation of currying/partial application soon - got some great ideas already :)