First Class Functions in PHP 7
Introduction
I've been using React to build a new website and holy hell - I haven't had this much fun programming since I was in highschool wrote my first PERL programs to download stuff from websites! Javascript with the React/Redux patterns has been a blast and makes development SO fun. I'm on windows, so having things "just work" with NPM and webpack-dev-server for instant refresh has been a pleasant surprise. This tutorial (Redux Tutorial #1 - React js tutorial - How Redux Works) is an awesome intro to React-Redux, but doesn't go in-depth enough, so you really need to watch Part 1 (Getting Started with Redux) and Part 2 (Building React Applications with Idiomatic Redux) of the real tutorial to really understand how to use things properly. ANYWAYS...
Motivation
It's been a blast using Javascript and some functional programming, but I use PHP on the server backend (and that ain't gonna change anytime soon - ❤ PHP 7), so I wanted to figure out if I could use similar programming patterns with PHP. This is what I figured out after a bit of messing around.
Note: I know these demos aren't mind-blowing, but it was fun to see them working naturally in PHP without much effort
To start, we have some test data:
$one = [1];
$twoThreeFour = [2, 3, 4];
$oneTwoThreeFour = [1, 2, 3, 4];
This is a standard "regular" array_reduce call just to get you warmed up.
// Simplest Reduce Demo: A typical array_reduce call by calling a regular external function
$twoThreeFour = [2, 3, 4];
function add_SimpleReduce($a, $b)
{
return $a + $b;
}
echo '2+3+4=';
echo array_reduce($twoThreeFour, function ($a, $b) {
return add_SimpleReduce($a, $b);
});
OUTPUT:
array_reduce($twoThreeFour, function ($a, $b) { return add_SimpleReduce($a, $b); })
2+3+4=9
Here we have a function $add_FunctionByReference
declared as a variable and able to be passed as an argument
(or even called like $add_FunctionByReference(1, 2)
)
// Another Simple Reduce Demo: Except this time we're passing a function that was declared as a variable.
// We now have a reference to that function as $add_FunctionByReference.
$oneTwoThreeFour = [1, 2, 3, 4];
$add_FunctionByReference = function ($a, $b) {
return $a + $b;
};
echo '1+2+3+4=';
echo array_reduce($oneTwoThreeFour, $add_FunctionByReference);
OUTPUT:
array_reduce($oneTwoThreeFour, $add_FunctionByReference)
1+2+3+4=10
In this next example you can see how a "variadic function" with the splat/spread operator "...
" can be used to pass an arbitrary number of arguments. The splat/spread operator spreads the array as individual arguments to the function.
I've taken the array_reduce
function from before and put it in the addAll_VariadicFunction
function.
// Variadic Function Demo: Taking an arbitrary number of arguments and passing them to the reduce function as an array
$oneTwoThreeFour = [1, 2, 3, 4];
function addAll_VariadicFunction(...$rest)
{
return array_reduce($rest, function ($a, $b) {
return $a + $b;
});
}
echo '1+2+3+4=';
echo addAll_VariadicFunction(...$one, ...$twoThreeFour) . "<br><br>\n\n";
OUTPUT:
addAll_VariadicFunction(...$one, ...$twoThreeFour)
1+2+3+4=10
$volume_FunctionByReference
is a similar function to $add_FunctionByReference
but now we're calculating area/volume of (potentially-multi-dimensional) shapes instead of adding numbers. We're supplying an initial value of 1
to the array_reduce function since we're dealing with multiplication.
// Here is a calculation of volume, similar to the addAll_VariadicFunction above
// but we're also supplying an initialValue to the array_reduce function since it defaults to 0 and needs to be 1 for multiplication
// (or else the return value will always be 0)
$volume_FunctionByReference = function (...$lengthWidthHeight) {
return array_reduce($lengthWidthHeight, function ($a, $b) {
return $a * $b;
}, 1);
};
echo '$volume_FunctionByReference(9, 9) : ' . $volume_FunctionByReference(9, 9);
echo '$volume_FunctionByReference(2, 2, 2) : ' . $volume_FunctionByReference(2, 2, 2);
OUTPUT:
$volume_FunctionByReference(9, 9) : 81
$volume_FunctionByReference(2, 2, 2) : 8
Here is the same function as above, but demonstrating passing the function $volume_FunctionByReference
by reference to another function.
function testPassByReference($func, ...$args)
{
return $func(...$args);
}
$volume_FunctionByReference = function (...$lengthWidthHeight) {
return array_reduce($lengthWidthHeight, function ($a, $b) {
return $a * $b;
}, 1);
};
echo 'testPassByReference($volume, 2, 3, 4) : ' .
testPassByReference($volume_FunctionByReference, 2, 3, 4);
OUTPUT:
testPassByReference($volume, 2, 3, 4) : 24
And now for the grande finale. We define a function template opAll
that takes a reducer function and an initial value.
Then we define a new function addAll
and multiplyAll
which we can provide values to later.
// Demo showing composition of functions and passing of functions as arguments to other functions
// Notice the "use" operator in opAll (this is a closure to keep the
// $func and $initialValue variables in-scope within the function)
$add = function ($a, $b) {
return $a + $b;
};
$multiply = function ($a, $b) {
return $a * $b;
};
function opAll($func, $initialValue)
{
return function (...$rest) use ($func, $initialValue) {
return array_reduce($rest, $func, $initialValue);
};
}
$addAll = opAll($add, 0); // This is the money shot
$multiplyAll = opAll($multiply, 1); // This is the money shot
echo '$addAll(1,2,3,4)';
echo '1+2+3+4=' . $addAll(1, 2, 3, 4);
echo '$multiplyAll(1,2,3,4)';
echo '1*2*3*4=' . $multiplyAll(1, 2, 3, 4);
OUTPUT:
$addAll(1,2,3,4)
1+2+3+4=10
$multiplyAll(1,2,3,4)
1*2*3*4=24
To run it yourself, download the entire raw file and run it on your web server: https://gist.githubusercontent.com/nanch/25d96f616a5cc1950b0561fb8b848955/raw/f8d23b90d2f5821c03d373bf9e9642fd85e98701/spread_test.php
Sincerely,
@davidnanch