RSS Feed

Peter Goodman's blog about PHP, Parsing Theory, C++, Functional Programming, Applications,

PHP Closures (and Y Combinator in PHP)

I've updated the closure script and changed the syntax of the inner-lambdas. See this post for the update.

One thing I've always wanted in PHP are closures. I know someone else has already tried to add them in without making a PHP extension, but I decided that I wanted to stay away from PHP's eval. I like the way my solution has turned out except that it requires that get_defined_vars()--or a pseudo args array--be passed in to the first function call instead of automagically figuring that stuff out. Here are some examples how the closures are used...

class Foo
{
    public $bar = 'hi';
}

$foo = new Foo;

$anon = lambda(get_defined_vars(), '', '
    $foo->bar = "hello";
');

$anon->call();

var_dump($foo);

// gives: object(Foo)#1 (1) { ["bar"]=>  string(5) "hello" }

In the previous example, the thing to notice is that object references are preserved throughout the closure. Now here comes a beast... A fixed-point combinator! I modeled this after a javascript example that I found on Douglas Crockford's website. Please realize that for the nested functions I needed to escape the nested single-quotes.

// fixed-point combinator
function Y(Lambda &$le) {
    return lambda(get_defined_vars(), 'Lambda $f', '
        return $f->call($f);
    ')->call(lambda(get_defined_vars(), 'Lambda $f', '
        return $le->call(lambda(get_defined_vars(), '$x', '
            return $f->call($f)->call($x);
        '));
    '));
}

// anonymous factorial function
$factorial =& Y(lambda(array(), 'Lambda $fac', '
    return lambda(get_defined_vars(),'$n', '
        return $n <= 2 ? $n : $n * $fac->call($n - 1);
    ');
'));

echo $factorial->call(5);

// output: 120

As a comparison, it was modeled after:

function Y(le) {
    return function (f) {
        return f(f);
    }(function (f) {
        return le(function (x) {
            return f(f)(x);
        });
    });
}

var factorial = Y(function (fac) {
    return function (n) {
        return n <= 2 ? n : n * fac(n - 1);
    };
});

To use this, you need to take the file and also create a folder that the closures can compile to... Yes! They compile! I do not suggest using this script in production code.. mainly because of its heavy use of a singleton as a stack, but then if anyone wants to benchmark this under a heavy load, I'd be very interested to see the results!

Also, I tried to find a hack around having to pass get_defined_vars() as an argument to the "lambda" (Closure) function, but I obviously did not succeed. Anyway, check out the full source of my php closure script at http://ioreader.com/code/php/Closure/closure.phps

As a side note, you might be wondering why I simply didn't use PHP's create_function() to create slightly more lambda-style functions. Check out this comment for more info.

EDIT: I renamed the "C" function to "lambda" and removed the ->scope(...) call so that everything is contained within lambda.


Comments

There are no comments, but you look like you have something on your mind.


Comment