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



Please submit feedback or corrections to: nanch at nanch.com