Functional Programming and PHP
Many programmers like to talk about functional programming, but if you ask them if they’ve ever done it, most of their replies will be “No”. The reason is quite simple: we are taught to think in an imperative manner when we first start learning to program, in terms of flow charts and steps to be followed in the program. So in this article I’ll explain some important concepts to functional programming and how to write functional code in PHP.
Important Concepts of Functional Programming
Starting with the jargon, Wikipedia defines functional programming as “a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data.” In functional programming, functions are treated as first-class citizens, whereas in imperative programming we are mostly concerned with the data and the steps to alter it to reach the desired result.
When we say functions are first-class, it means we can use functions just as we use values in imperative programming. They can be passed as an argument to a function, defined inside another function, and can even be returned as a result. In other words, “functions are values”.
We’ll come back to this again later, but there are many other important concepts of functional programming. To mention a few:
Immutability
Immutability is the behavior that a value of a variable cannot be changed once it is defined. Different languages have different ways of achieving this; in PHP, for instance, the only way to make a variable immutable is to define it as a constant.
Recursion
Recursion is also very prominent in functional programming. In imperative programming, we can use looping constructs like for
and foreach
when we need to manipulate collections or arrays, walking through each element and keeping a temporary variable to hold the current value. But because of immutability, this approach is not possible in functional programming. Recurssion is the answer because such book keeping is done implicitly with the call stack.
Suppose we want to write a function to find the sum of all elements in an array (forget that array_sum()
exists for the time being). In a functional style, we would write:
<?php
function sum($array) {
if (empty($array))
return 0;
else
return $array[0] + sum(array_slice($array, 1));
}
$total = sum(array(1, 2, 3)); // 6
An empty list will return 0, which is our base condition. For an array with more than one value, it will return the results of adding the first element with the recursive sum of all other elements.
Pure Functions and Referential Transparency
A function is said to be free from side effects if it does not change the value of an object outside itself, such as a global or static variable, and does not have any I/O effects like writing into file, database, and so on. Such functions are otherwise called pure functions.
The output of a pure function will always be the same for a given set of arguments, which leads to another property called referential transparency. When a function is referentially transparent, we can replace that function with its value without affecting the behavior of the program. All mathematical functions are pure functions, whereas date functions, rand()
, etc. are impure.
Higher Order Functions
The concepts above can be achieved in almost any programming language, but first class functions and higher order functions are the two most distinguishing features of functional programming. I explained that first class functions means that functions can be treated as values. Higher order functions are functions that can take functions as arguments and can return a function as their result. Two important features were added relatively recently which enabled us to write higher order functions in PHP: lambdas and closures.
Lambda Functions
A lambda function (also known as anonymous function) is nothing but a function that has no name. When we define an anonymous function, a reference to the function is returned which is stored in a variable for later use. We use this variable to call the function whenever it’s needed.
This concept has been adopted in many different languages. In fact, you’re probably using lambda functions in your day-to-day JavaScript programming, passing them as callback functions for different user interactions and Ajax calls.
$("#myButton").click(function () {
// do something
});
This piece of code is so simple and easy to understand, which might make us forget its functional aspect.
PHP introduced this awesome feature in version 5.3, which lets us write PHP code in a similar fashion:
<?php
$square = function ($arg) {
return $arg * $arg;
};
$value = $square(2); // 4
When talking about functions, and anonymous functions in particular, its important to understand how variable scope is handled. JavaScript, for example, allows you to access a variable from an outer scope inside the lambda, whereas PHP does not. Inside the lambda is its own scope, just as with regular PHP functions.
Closures
Sometimes you’ll want to reference a variable from the parent scope inside your function. Closures are similar to lambda functions with the minor difference that you can access variables from an outer scope. We can use “reach out” and bind an outer variable using PHP’s use
keyword, also introduced in PHP 5.3.
<?php
$rate = .12;
$findInterest = function ($value) use ($rate) {
return $value * $rate;
};
$interest = $findInterest(100);
In this case we don’t pass the interest rate each time we call the function. Instead, we’ve defined it outside and made it available inside the function with the use
keyword.
Partial Functions and Currying
A partial function, simply put, is a function created from an existing function by partially applying its arguments. You only need to pass the remaining arguments when you call the created function.
We can create partial functions in PHP with the use of closures. Here’s an example to find the volume of a box given its length, width and height. All arguments are optional; if you don’t supply all of the arguments, the function will return another function to accept the necessary values that remain.
<?php
$volume = function ($length = 0, $width = 0, $height = 0) use (&$volume) {
$args = func_get_args();
$numArgs = func_num_args();
if ($numArgs == 3) {
return $length * $width * $height;
}
else if ($numArgs < 3) {
return function() use(&$volume, $args) {
$newArgs = array_merge($args, func_get_args());
return call_user_func_array($volume, $newArgs);
};
}
else {
throw new BadFunctionCallException("Too many arguments");
}
};
All of the parameters are optional. First a check is made to determine whether the caller passed all arguments. In that case, we can directly return the volume by multiplying length, width and height. If the number of arguments is less than the parameters, a new function is returned to find the volume “pre-seeded” with the given arguments.
Now suppose that most of the time we’re finding the volume of box with a fixed length, say 10. This can be easily done by passing 10 as the first argument, or we could create the partial function by fixing 10 as the first argument and then only ask for the remaining values.
<?php
$standardVolume = $volume(10);
$vol = $standardVolume(5, 5); // 250
Currying is a special case of partial functions where you convert a function that takes multiple arguments into to multiple functions that will each take a single argument. For example, something like f(x,y,z) to f(x)(y)(z) (although PHP syntax doesn’t allow nesting of function calls like this). Timothy Boronczyk has written an excellent article on currying with a practical example if you’re interested in seeing more.
Advantages and Disadvantages
There are many practical uses of functional programming features in PHP. For instance, lambda functions are widely used when working with callbacks. Using the Slim framework, for example, you can define a route like this:
<?php
$app = new SlimSlim();
$app->get("/home", function () {
// show home page
});
Slim invokes the callback function when the request URL matches this route. Vance Lucas wrote about some other interesting use cases of Lambda functions a while back.
Safe programming is encouraged by avoiding state and mutable data. In functional programming you should write functions that do exactly one thing each and do not cause any side effects. The paradigm’s emphasis on modularity and conciseness of functions can make its easier to reason about your program in terms of different, small sub programs as well.
Functional programming can also help you write code that focuses what you want to achieve instead of explicitly managing the incidentals in the process (compare recursion with having to manage loop counter variables).
But keep in mind however that some of the advantages traditionally associated with functional programming are not applicable to PHP as it’s not designed to be a functional programming language. For example, side-effect-free functions lend well to parallel processing, but PHP scripts are not run in this manner.
It’s not always easy to calculate the cost of recursion and lazy functions, either, and there can be significant performance issues due to the internal overhead. Sometimes it makes more sense to write programs in terms of mutability for better efficiency.
Perhaps the biggest disadvantage of functional programming is its steep learning curve for those trained in an imperative manner. But over all, functional programming is interesting, and learning it will give you the tools to think about old problems in a new light, helping you to grow as a programmer. It’s not a one-size fits all solution, but it can be applied as appropriate for cleaner, more eloquent PHP code.
Summary
Functional programming is more than just a programming paradigm; it’s a way of thinking and reasoning about your program. If you can think functionally, you can do functional programming in almost any language.
In this article we discussed the basics of functional programming, leveraging features of PHP to write provide their examples. Although, the example given in this article may not be practically useful to you, you’ll find many situations where the functional style can significantly improve the quality of code you are writing. Try to find such cases, think functionally, and have fun!
Image via Fotolia