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';