Your $comp is curried, and of course you discovered PHP's native array_reduce expects the function to accept multiple parameters – a quick application of uncurry takes some of your pain away, but you'll need to read on if you want to see how this can be improved overall ...
in PHP's humble opinion...
Using uncurry does the trick, but you'll probably end up disliking your program if all the functions are defined as $-named variables – I foresee lots of little problems using that style.
PHP has a callable "type" which makes things a little more PHP-ish – user-defined functions (including higher-order functions) should be called using call_user_func and call_user_func_array
namespace my\module;
function identity ($x) {
return $x;
}
function comp ($f) {
return function ($g) use ($f) {
return function ($x) use ($f, $g) {
return call_user_func ($f, call_user_func ($g, $x));
};
};
}
function uncurry ($f) {
return function ($x, $y) use ($f) {
return call_user_func (call_user_func ($f, $x), $y);
};
}
function fold ($f, $acc) {
return function ($xs) use ($f, $acc) {
return array_reduce ($xs, uncurry ($f), $acc);
};
}
Now your compn with variadic interface works as expected
function compn (...$fs) {
return fold ('comp', 'identity') ($fs);
}
function inc ($x) {
return $x + 1;
}
echo compn ('inc', 'inc', 'inc') (0); // 3
But it works with anonymous functions too
$double = function ($x) { return $x + $x; };
echo compn ($double, $double, 'inc') (2); // 12
functional code, modular program
With your functions declared using function syntax, you can import them into other areas of your program
// single import
use function my\module\fold;
// aliased import
use function my\module\fold as myfold;
// multiple imports
use function my\module\{identity, comp, compn, fold};
And now you don't have to litter you code with use-blocks every time you want to use one of your functions
// before
$compn = function (...$fs) use ($fold, $comp, $id) {
return $fold($comp, $id) ($fs);
};
// after
function compn (...$fs) {
return fold ('comp', 'id') ($fs);
}
When it comes to debugging, undoubtedly the named functions will provide more helpful stack trace messages as well
relevant but unimportant
PHP has other reasons for adding the callable type, but ones I'm sure that don't concern you as they're OOP-related – eg,
class method calls
// MyClass::myFunc (1);
call_user_func (['MyClass', 'myFunc'], 1);
object method calls
// $me->myfunc (1);
call_user_func ([$me, 'myfunc'], 1);