If you examine several frameworks and large-scale applications, you’ll certainly see a higher-order function at some point. Many languages support the idea of higher-order functions, including JavaScript, Java, .NET, Python and even PHP, to name a few.
But what is a higher-order function and why would we want to use one? What advantages does it give us and can we use it to simplify our code? In this article, we’ll talk about higher-order functions in PHP specifically, but will show how they’ve been used in other languages for comparison.
First-Class Functions
Table of Contents
Before we can get into what a higher-order function is, we must first understand that we have to have a language that can support a feature called first-class functions (aka first-order functions). This means that the language treats functions as first-class citizens. In other words, the language treats functions like it does variables. You can store a function in a variable, pass it to other functions as a variable, return them from functions like variables and even store them in data structures like arrays or object properties like you can with variables. Most modern languages these days have this feature by default. All you really need to know is that a function can be passed around and used much like variables are.
For our purposes, we’ll be focusing mostly on passing functions as arguments and returning functions as results, and briefly touching on the concept of non-local variables and closures. (You can read more about these concepts in sections 1.1, 1.4 and 1.3 of the Wikipedia article that was linked to in the previous paragraph.)
What Are Higher-order Functions?
There are two main characteristics that identify a higher-order function. A higher-order function can implement just one or both of the following ideas: a function that takes one or more functions as an input or returns a function as an output. In PHP there’s a keyword that’s a clear giveaway that a function is higher-order: the keyword callable
. While this keyword doesn’t have to be present, the keyword makes it easy to identify them. If you see a function or method that has a callable parameter, it means that it takes a function as input. Another easy sign is if you see a function return a function using its return
statement. The return statement might be just the name of the function, or could even be an anonymous/in-line function. Below are some examples of each type.
// Simple user-defined function we'll pass to our higher-order function function echoHelloWorld() { echo "Hello World!"; } // Higher-order function that takes a function as an input and calls it function higherOrderFunction(callable $func) { $func(); } // Call our higher-order function and give it our echoHelloWorld() function. // Notice we pass just the name as a string and no parenthesis. // This echos "Hello World!" higherOrderFunction('echoHelloWorld');
Here are some simple examples of higher-order functions that return a function:
// Returns an existing function in PHP called trim(). Notice it's a simple string with no parentheses. function trimMessage1() { return 'trim'; } // Here's an example using an anonymous inline function function trimMessage2() { return function($text) { return trim($text); }; } // Returns the 'trim' function $trim1 = trimMessage1(); // Call it with our string to trim echo $trim1(' hello world '); // Returns the anonymous function that internally calls 'trim' $trim2 = trimMessage1(); // Call it again with our string to trim echo $trim2(' hello world ');
As you can imagine, you can take in a function and also use that to generate another function that’s returned. Pretty neat trick, right? But why would you want to do any of this? It sounds like something that would simply make things more complicated.
Why Would You Use or Create a Higher-order Function?
There are several reasons you might want to create higher-order functions in your code. Everything from code flexibility, code reuse, code extension or to imitate a code solution you saw in another program. While the reasons are numerous, we’ll cover a few of them here.
Adding code flexibility
Higher-order functions add a ton of flexibility. Based on the previous examples, you probably can see a few uses for something like this. You can write a single function that takes in a whole suite of different functions and uses them without having to write code to execute them individually.
Maybe the higher-order function itself doesn’t know what type of function it will receive. Who said that the function has to know anything about the function it’s taking in? One minute the input function could be an add()
function, and in the next it could be a divide()
function. In either case it just works.
Easily expand your code
This functionality also makes it easier to add additional input functions later. Let’s say you have add()
and divide()
, but down the road you need to add a sum()
function. You can write the sum()
function and pipe it through the higher-order function without ever having to change it. In a later example, we’ll demonstrate how we can use this functionality to create our own calc()
function.
Imitate features of another language
Another reason you might want to use a higher-order function in PHP is to simulate the behavior of something like a decorator in Python. You “wrap” a function inside another function to modify how that function behaves. For instance, you could write a function that times other functions. You write a higher-order function that takes in a function, starts a timer, calls the function, then ends the timer to see how much time has elapsed.
Examples of Existing Higher-Order Functions in PHP
It’s really simple to find examples of higher-order functions in PHP. Look at the PHP documentation and find a function/method that takes a callable
input parameter and you’ve found one. Below are a few examples of commonly used higher-order functions. You might have even used them without ever knowing they were higher-order functions.
The higher-order dynamic duo: array_map
and array_filter
$arrayOfNums = [1,2,3,4,5]; // array_map: example of taking an inline anonymous function $doubledNums = array_map(function($num) { return $num * 2; }, $arrayOfNums); var_dump($doubledNums); // Prints out array containing [2, 4, 6, 8, 10];
Let’s see how to use another one, this time with a filter function we’ve defined separately:
$arrayOfNums = [1,2,3,4,5]; // Example of creating a function and giving it to a higher-order function array_filter() function isEven($num) { return ($num % 2) === 0; } // array_filter is a higher-order function $evenNums = array_filter($arrayOfNums, 'isEven'); var_dump($evenNums); // Prints out array containing [2, 4]
array_filter
and array_map
are two very popular higher-order functions that you’ll find in plenty of code projects. Another one you might use is call_user_func
, which takes in a function and a list of arguments and calls on that function. This is essentially a customized higher-order function. Its whole purpose is to call other functions that the user has defined:
function printCustomMessage($message) { echo "$message"; } call_user_func('printCustomMessage', 'Called custom message through the use of call_user_func!');
Continue reading Functional Programming in PHP: Higher-order Functions on SitePoint.