Friday, December 3, 2010

Revision to php TCO trampoline

github.com/shaunxcode/php-trampoline

Sometimes I wake up at 6am with the frightful knowledge that there is an edge case to something I wrote somewhere which is incorrect. The only thing worse than knowing someone on the internet is wrong (http://xkcd.com/386/) is knowing you were wrong on the internet.

I realized in a dream that my trampoline would not work with higher order functions as it currently stands because the while loop is based on is_callable, obviously if your function is meant to return a function you would not want it to be executed immediately (particularly because it probably expects arguments etc.). All of a sudden the whole throwing an exception approach made sense - however thanks to php5.3s __invoke method I can handle this cleanly by wrapping each tail call in an instance of TrampolineBounce and then check "$return instanceof TrampolineBounce".

I figured if I am going to need a Trampoline class I may as well leverage __callStatic to allow for easier invocation of trampolined functions. With out further ado:

namespace Trampoline;

class Bounce {
private $func;
public function __construct($func) {
$this->func = $func;
}

function __invoke() {
return call_user_func_array($this->func, array());
}
}

class Trampoline {
public static function Bounce($func) {
return new Bounce($func);
}

public static function __callStatic($func, $args) {
$return = call_user_func_array($func, $args);
while($return instanceof Bounce) {
$return = $return();
}

return $return;
}
}

function even($n) {
return $n == 0 ? true : Trampoline::Bounce(function() use($n) { return odd($n - 1);});
}

function odd($n) {
return $n == 0 ? false : Trampoline::Bounce(function() use($n) { return even($n - 1);});
}


echo Trampoline::even(1500) ? 'Yep' : 'Nope';

0 comments:

Blog Archive