Why does my function composition implemented by reduction return a closure?

I want to get a composition function for n-functions from reduce / fold, but it does not work properly:

$id = function ($x) { return $x; }; $comp = function ($f) { return function ($g) use ($f) { return function ($x) use ($f, $g) { return $f($g($x)); }; }; }; $fold = function ($f, $acc) { return function ($xs) use ($f, &$acc) { return array_reduce($xs, $f, $acc); }; }; $compn = function($fs) {/* apply $fold here */}; $inc = function ($n) { return $n + 1; }; $fold($comp, $id) ([$inc, $inc, $inc]) (0); // yields a closure instead of 3 

I have the same function implemented in Javascript and it works. I am using PHP 7.0.8 cli. I don't know much about PHP, so I'm probably missing something.

+7
php functional-programming function-composition reduce
source share
2 answers

Your $comp curried , and, of course, you find that PHP native array_reduce expects the function to take several parameters - quickly the uncurry application takes away some of your pain, but you need to read it if you want to see how it can be improved in general. ..


in PHP a humble opinion ...

Using uncurry does the trick, but you will probably end up disliking your program if all functions are defined as $ -name variables - I anticipate a lot of small problems using this style.

PHP has a callable "type" that makes things a little more specific to PHP functions - 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 variable interface compn working 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 also works with anonymous functions.

 $double = function ($x) { return $x + $x; }; echo compn ($double, $double, 'inc') (2); // 12 

functional code, modular program

With declared functions 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 throw 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, named functions will provide more useful stack trace messages as well


relevant but inconsequential

PHP has other reasons for adding a callable type, but I'm sure it doesn't concern you, since they are OOP related - for example,

class method call

 // MyClass::myFunc (1); call_user_func (['MyClass', 'myFunc'], 1); 

object method call

 // $me->myfunc (1); call_user_func ([$me, 'myfunc'], 1); 
+2
source share

I realized: array_reduce calls $f as a function with several arguments. Therefore, I have to introduce another anonymous function:

 $id = function ($x) { return $x; }; $comp = function ($f) { return function ($g) use ($f) { return function ($x) use ($f, $g) { return $f($g($x)); }; }; }; $fold = function ($f, $acc) { return function ($xs) use ($f, &$acc) { return array_reduce($xs, function ($acc_, $x) use ($f) { // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ return $f($acc_) ($x); }, $acc); }; }; $compn = function($fs) {/* apply $fold here */}; $inc = function ($n) { return $n + 1; }; echo $fold($comp, $id) ([$inc, $inc, $inc]) (0); // yields 3 

And here is the shrink / fold wrapper to get a more convenient API:

 $compn = function (...$fs) use ($fold, $comp, $id) { return $fold($comp, $id) ($fs); }; $compn($inc, $inc, $inc) (0); 
+2
source share

All Articles