PHP Lambdas and Closures

Functional programming is another trend that has been growing over the past 5 years.

Look, here is a graph from Google to prove it:

But the concepts of functional programming predate computers altogether, literally dating back thousands of years. It is very easy to get absorbed into the theory and mathematics of it all to the point that it can be overwhelming.

Although there are many programming languages that are designed to be "functional", one area that I have recently been interested in, is using some of the concepts in my every day PHP programming.

I recently read Programming in PHP by Simon Holywell and saw a talk by Zsolt Szende at PHP London meetup on this same topic.

Although both of these sources went into some detail, today, I am only going to cover how to implement and us lambda functions and closures.

Defining Functional Programming

Functional programming is quite simply "programming with functions". This might seem like an odd thing to say because programmers have slightly changed the definition of "function" to mean a defined block of code.

But in mathematics, functions are basically mappings from an input to an output this is is the core principle of functional programming: that a function will always produce exactly the same output for a given input and have no side effects outside of this.

This is an incredibly powerful thing. It means that code is more testable as we can easily specify expected outputs and more maintainable as there are no unexpected behaviours outside of functions that can get you.

You can hopefully see from this, that you can make what is known as a "pure function" in any programming language, functional or not, and PHP is no exception.

Lambda Functions

Given that a function has the same output for a given input, this means it can be treated like a variable and be used in place of a variable. These are known as lambda functions. They are functions that have no name at the time of initialisation but can be assigned to a variable.

A here is an example:

$square = function($x) {
    return $x * $x;
};

var_dump($square(2));

This would print out int(4). And that is it. It is that simple. Because I know that $square will always return an integer, I can place it any time an integer is expected.

Right now, I am able to pass letters into this function. In this function, this returns int(0), which is still an integer which is good, but this may not always be the case for more complex functions.

So to make the function even more robust, I can add a type hint to the parameter and set a return type to ensure that this function will always return an integer, and if there is a case where it doesn't, there will be a language level error:

$square = function(int $x) : int {
    return $x * $x;
};

This now explicitly only accepts integers and tells me that the return type is integer. In this case, its impossible a non-integer to be returned if the input is valid, but if the code was more complex, it could happen.

var_dump($square($square(2)));

This would print int(16). It works because $square takes an integer as a parameter and $square itself is an integer.

You can go one step further (well you can actually go many step further, but this post will only go one) and use something called a closure to make your function a little more generic.

Lets make our function so that not only will it have no name, but it can also be "instantiated" with a value that specifies how many times it should multiply $x by itself.

$exponent = 2;
$square = function(int $x) use ($exponent): int { 
    return $x ** $exponent; 
};

The use is able to take any variable within scope and make it available to within the function itself. This is done only once at the moment of assignment, so even if I change the value of $exponent afterwards, it will not effect $square and I would have to re-assign it to change it.

The benefit of this if you need to call a function with the same arguments every time, you can set those up once and then forget about them.

We can make this easier by doing something like this:

function getFunction(int $exponent) : callable
{
    return function(int $x) use ($exponent): int { 
        return $x ** $exponent; 
    };
}

This is a function that has a name and returns another function (a callable). You can see it accepts a parameter that is used within the closure. It can then be used like so:

$square = getFunction(2);
$cube = getFunction(3);

var_dump($square(10));
var_dump($cube(10));

This would print out int(100) and int(1000). What we have done here is created instances of functions with hardwired inputs that can be used anywhere else in the code in substitution of a value.

Also, since getFunction itself is a pure function that only returns a callable, I can use it also any time a callable is expected. For example, array_map applies a callable to every element in an array, so:

 array_map(getFunction(2), [1,2,3,4,5]);

Would give:

 Array
 (
     [0] => 1
     [1] => 4
     [2] => 9
     [3] => 16
     [4] => 25
)

Summary

The topic of functional programming is vast. What we have done today is the tip of the iceberg. But, I hope it has illustrated how simple it is to use PHP to right cleaner code using pure functions. You may already have been doing so without even realising!


© 2012-2017