Remove the Magic with Functional PHP - Part II

This is part two of a three part post based on a presentation I plan to give at some meetups or conferences this year. The slides for this presentation can be seen here: http://slides.com/davidcorona/remove-magic-functional-php/live

Part I: http://www.lovesmesomecode.com/remove-the-magic-with-functional-php-part-i


In Part I, we talked about the things that can make our code more complex without us realizing it. To continue this series of posts, I am going to introduce some basic functional concepts which will set us up for the final post in the series. With these basic functional concepts, we enable ourselves to think about and write our code in a different way from how we may be used to writing them with PHP.

Functional Programming

Functional programming is all about functions. Everything is a function that returns a value. In functional code, a function’s output is only dependent on the value of its inputs, without side effects. What does that mean? Let’s see.

First class functions

Functions as data. Probably the single most important concept of functional programming. Functions are treated the same as any other value in an application. You can pass them into and return them from other functions as if they were any other piece of data. PHP has anonymous functions for us to take advantage of. They are also called lambdas if you want to sound fancy when talking about them. I always do. Here is a simple anonymous function used as a callback for some random asynchronous operation.

function getSomeAsyncData(‘foo’, function($data) {
    // do something with async data here.
});

In PHP, you write an anonymous function just like any other function, except it does not have a name. Hints the name anonymous function. You can see here that we have a function that is receiving another function as one of it's arguments. We can also call this function a Higher order function, because it receives a function as an argument. Higher order functions are functions that can receive other functions, or return new functions.

function getAddFunction($a) {
    return function($b) use ($a) {
        return $a + $b;
    };
};

$add5 = getAddFunction(5);

In this example, we have a function called getAddFunction, and it's job is to return a new function that just takes a single number. So, we use this function to create an add5 function. Which we can then call like this.

$sum = $add5(10);

This example also shows off a concept called partial application. In which we partially apply the number 5 to an add function. So, we create a new add function which only needs a single number instead of 2. We partially applied the number 5.

Pure functions

Pure functions are functions without side effects. A function receives input, runs a computation on that input and returns a result. They have no concept of global state. There is a concept called referential transparency that says that if you can replace a function with it’s value and it will have no effect on your program, it has referential transparency. When I call a function with a given value, I should always receive the same result. Every time. Period. Nothing else should happen.

function add($a, $b) {
    return $a + $b;
}

This function does one thing. Add two numbers together. It does nothing else so it is considered a pure function. I can call this function 100 times with the numbers 1 and 2, and I will always get back the number 3.

This means that anywhere I call this add function, I could just completely replace that call with the result of the function.

$sum = add(2, 3);

$sum = 5;

This add function is referentially transparent because I can replace it with the number 5 and nothing about my program would change.

A lot of times, you may have a function without a return value. This means that it has some side effect and is probably mutating global state or touching some external resource. This is bad. We don’t want to do this, because we don’t know what these functions are doing. They could be changing all sorts of unexpected things. We would have to actually go read the function to see what it is doing.

However, this can be rather limiting. What if we need to do things like file IO, printing messages, or sending html to a browser? Those are all side effects. Well, we can still do these things, but the functions that do them are known as impure functions. They are referentially opaque, which you guessed it, is the opposite of transparent. They are dirty and tainted. We want to limit the amount of impure functions in our code. They should be isolated to the part of the application that deals with IO, and nowhere else.

Immutable data

This is the concept that once a variable has been set, it cannot be changed. That's it. You can’t change it. Don’t even try it because you can’t. Say we have an array of numbers. How would you normally add 5 to each number in the array?

$numbers = [1, 2, 3, 4, 5];

for ($x = 0; $x < count($numbers); $x++) {
    $numbers[$x] = $numbers[$x] + 5;
}

Sweet. Looks good. Works great! Nothing can go wrong here right? Well what if this array was passed in from another function that was relying on those original values that you just changed? Well, that code is probably going to break.

Unfortunately, PHP only has immutable data in the form of const or with some fancy __get and __set magic. So how do we work around this limitation in PHP? Const all the things? Not quite. We will need to think of our data as immutable and treat it that way. Of course we still need to be able to change our data, but we don’t have to actually change it, we can return new data. Let’s change this to a map and return a new array instead.

$biggerNumbers = array_map(function($num) {
    return $num + 5;
}, $numbers);

Much better, and easier to read IMO. We know what array_map does. It runs a function over an array and returns a new array from the results. This means that we can understand what this does quicker than the first method. We have a problem here though. We wrote this glorious add 5 function, but no one else can use it because it’s an anonymous function. Let’s give it it’s own function.

Function composition

function add5($a) {
    return $a + 5;
}

Nice! Now everyone else can reap the benefits our sweet add5 function. Let’s use it to re-write our array_map.

$biggerNumbers = array_map('add5', $numbers);

Oh yea. I don’t even need to look at the add5 function, because it says add5 right there and I know what it will do to my array.

But, we still have a problem. We already wrote an awesome add function earlier. Now we just wrote an add 5 function, but they basically do the same thing. They Add numbers together. We can take advantage of our add function and ADD to it. (get it?)

// Our add function from earlier.
function add($a, $b) {
    return $a + $b;
}

function add5($a) {
    return add($a, 5);
}

Looks familiar from above, right? It's very similar, but we are returning the result of calling the add function instead of returning a new function. Again, our add5 function is just a partially applied add function. Composing functions like this is very common in functional programming. We reuse functions we already wrote to make new interesting functions.


That's it for now. There are a lot more functional concepts that you can explore on your own. These are only the ones we will be specifically using in the next part of this series.