I/O Reader

RSS Feed

Peter Goodman's blog about PHP, Javascript, Functional Programming, Applications, Actionscript,

PHP Closures (and Y Combinator in PHP)

posted on May 3, 2007 at 1:36pm with one comment and tagged with PHP, Functional Programming

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



write down what you see in the box below (all uppercase)