Composing Functions - Functional PHP (2017)

Functional PHP (2017)

Chapter 4. Composing Functions

In previous chapters, we talked a lot about building blocks and small pure functions. But so far, we haven't even hinted at how those can be used to build something bigger. What good is a building block if you cannot use it? The answer partly lies in function composition.

Although this chapter completes the previous one, this technique is such an integral and important part of any functional program that it warranted its own chapter.

In this chapter, we will cover the following topics:

· Function composition

· Partial application

· Currying

· Parameter order importance

· Real-life application of those concepts

Composing functions

As is often the case in functional programming, the concept of function composition is borrowed from mathematics. If you have two functions, f and g, you can create a third function by composing them. The usual notation in mathematics is (f g)(x), which is equivalent to calling them one after the other as f(g(x)).

You can compose any two given functions very easily with PHP, using a wrapper function. Say you want to display a title in all caps and only safe HTML characters:

<?php

function safe_title(string $s)

{

$safe = htmlspecialchars($s);

return strtoupper($safe);

}

You can also avoid the temporary variable altogether:

<?php

function safe_title2(string $s)

{

return strtoupper(htmlspecialchars($s));

}

This works well when you want to compose only a few functions. But creating a lot of those wrapper functions can become really cumbersome. What if you could simply use $safe_title = strtoupper htmlspecialchars line of code? Sadly, this operator does not exist in PHP, but the functional-php library we presented earlier contains a compose function which does exactly that:

<?php

require_once __DIR__.'/vendor/autoload.php';

use function Functional\compose;

$safe_title2 = compose('htmlspecialchars', 'strtoupper');

The gain may not seem that important, but let's compare using such an approach in a bit more context:

<?php

$titles = ['Firefly', 'Buffy the Vampire Slayer', 'Stargate Atlantis', 'Tom & Jerry', 'Dawson's Creek'];

$titles2 = array_map(function(string $s) {

return strtoupper(htmlspecialchars($s));

}, $titles);

$titles3 = array_map(compose('htmlspecialchars', 'strtoupper'), $titles);

Personally, I find the second approach a lot easier to read and understand. And it gets better, as you can pass more than two functions to the compose function:

<?php

$titles4 = array_map(compose('htmlspecialchars', 'strtoupper', 'trim'), $titles);

One thing that can be misleading is the order of application of the functions. The mathematical notation f ∘ g first applies g and then the result is passed to f. However, the compose function from functional-php library applies the functions in the order they are passed in the compose('first', 'second', 'third') parameters.

This might be easier to understand depending on your personal preferences, but beware when you use another library, as the order of application might be reversed. Always make sure you've read the documentation carefully.

Partial application

You might want to set some parameters of a function but leave some of them unassigned for later. For example, we might want to create a function that returns an excerpt of a blog post.

The dedicated term for setting such a value is bind a parameter or bind an argument. The process itself is called partial application and the new function is set to be partially applied.

The naive way to do this is by wrapping the function in a new one:

<?php

function excerpt(string $s)

{

return substr($s, 0, 5);

}

echo excerpt('Lorem ipsum dolor si amet.');

// Lorem

As with composition, always creating new functions can quickly become cumbersome. But once again, the functional-php library has us covered. You can decide to bind parameters either from the left, the right, or in any particular location in the function signature, using respectively the partial_left, partial_right, or partial_any function.

Why three functions? Mostly for performance reasons, as the left and right versions will perform a lot faster because the parameters will be replaced once and for all, whereas the last one will use placeholders evaluated upon each call to the new function.

In the last example, the placeholder is defined using the function ... which is the ellipsis unicode character. If you don't have an easy way to type it on your computer, you can also use the placeholder function from the Functional namespace which is an alias.

Currying

Currying is often used as a synonym for partial application. Although both concepts allow us to bind some parameters of a function, the core ideas are a bit different.

The idea behind currying is to transform a function, taking multiple arguments into a sequence of functions taking one argument. As this might be a bit hard to grasp, let's try to curry substr function. The result is called a curryied function:

<?php

function substr_curryied(string $s)

{

return function(int $start) use($s) {

return function(int $length) use($s, $start) {

return substr($s, $start, $length);

};

};

}

$f = substr_curryied('Lorem ipsum dolor sit amet.');

$g = $f(0);

echo $g(5);

// Lorem

As you can see, each call returns a new function that takes the next parameter. This illustrates the principal difference with partial application. When you call a partially applied function, you will obtain a result. But, when you call a curryied function, you will get a new function until you pass the last parameter. Also, you can only bind the arguments in order starting from the left.

If the call chain seems overly lengthy, you can greatly simplify it starting from PHP 7. This is because the RFC Uniform variable syntax was implemented (see https://wiki.php.net/rfc/uniform_variable_syntax for details):

<?php

echo substr_curryied('Lorem ipsum dolor sit amet.')(0)(5);

// Lorem

The advantages of currying might not seem evident when presented like this. But, as soon as you start working with higher-order functions such as map or reduce function, the idea becomes really powerful.

You might remember the pluck function from the functional-php library. The idea is to retrieve a given property from a collection of objects. If the pluck function was implemented as a curryied function, it could be used in a variety of ways:

<?php

function pluck(string $property)

{

return function($o) use($property) {

if (is_object($o) && isset($o->{$propertyName})) {

return $o->{$property};

} elseif ((is_array($o) || $o instanceof ArrayAccess) && isset($o[$property])) {

return $o[$property];

}

return false;

};

}

We could get a value from any kind of object or array easily:

<?php

$user = ['name' => 'Gilles', 'country' => 'Switzerland', 'member' => true];

pluck('name')($user);

We could extract a property from a collection of objects, as does the version from the functional-php library:

<?php

$users = [

['name' => 'Gilles', 'country' => 'Switzerland', 'member' => true],

['name' => 'Léon', 'country' => 'Canada', 'member' => false],

['name' => 'Olive', 'country' => 'England', 'member' => true],

];

pluck('country')($users);

As our implementation returns false when nothing is found, we could use it to filter arrays that contain a certain value:

<?php

array_filter($users, pluck('member'));

We could combine multiple use cases to get the names of all the members:

<?php

pluck('name', array_filter($users, pluck('member')));

Without currying, we would have needed to either write a wrapper around a more traditional pluck function, or create three specialized functions.

Let's go a step further and combine multiple curryied functions. First, we will need to create a wrapper function around the array_map and preg_replace functions:

<?php

function map(callable $callback)

{

return function(array $array) use($callback) {

return array_map($callback, $array);

};

}

function replace($regex)

{

return function(string $replacement) use($regex) {

return function(string $subject) use($regex, $replacement)

{

return preg_replace($regex, $replacement, $subject);

};

};

}

Now we can use those to create multiple new functions, for example, a function that replaces all spaces in a string with underscores or all vowels with a star:

<?php function map(callable $callback)

{

return function(array $array) use($callback) {

return array_map($callback, $array);

};

}

function replace($regex)

{

return function(string $replacement) use($regex) {

return function(string $subject) use($regex, $replacement)

{

return preg_replace($regex, $replacement, $subject);

};

};

}

Currying functions in PHP

I hope you are now convinced of the power of currying. If not, I hope the examples to follow will do it for you. In the meantime, you are probably thinking it is really cumbersome to write a new utility function around existing ones to create a new curryied version, and you would be right.

In languages such as Haskell, all functions are curryied by default. Sadly, this is not the case in PHP, but the process is easy and repetitive enough that we can write a helper function.

Due to the possibility of having optional parameters in PHP, we will first create a function curry_n that takes the number of parameters you want to curry. This way, you will also be able to decide if you want to curry all parameters, or only some of them. It can also be used for functions that have a variable number of parameters:

<?php

function curry_n(int $count, callable $function): callable

{

$accumulator = function(array $arguments) use($count, $function, &$accumulator) {

return function() use($count, $function, $arguments, $accumulator) {

$arguments = array_merge($arguments, func_get_args());

if($count <= count($arguments)) {

return call_user_func_array($function, $arguments);

}

return $accumulator($arguments);

};

};

return $accumulator([]);

}

The idea is to use an inner helper function, taking the already passed values as parameters, and then creating a closure with those. When called, the closure will decide, based on the actual number of values, whether we can call the original function, or whether we need to create a new function using our helper.

Be aware that if you give a parameter count higher than the real count, all extraneous parameters will be passed along to the original function but will probably be ignored. Also, giving a smaller count will result in the last step expecting more than just one parameter to correctly complete.

We can now create our second function that will determine the number of parameters using reflection variable:

<?php

function curry(callable $function, bool $required = true): callable

{

if(is_string($function) && strpos($function, '::', 1) !== false) {

$reflection = new \ReflectionMethod($f);

}

else if(is_array($function) && count($function) == 2)

{

$reflection = new \ReflectionMethod($function[0], $function[1]);

}

else if(is_object($function) && method_exists($function, '__invoke'))

{

$reflection = new \ReflectionMethod($function, '__invoke');

}

else

{

$reflection = new \ReflectionFunction($function);

}

$count = $required ?

$reflection->getNumberOfRequiredParameters() :

$reflection->getNumberOfParameters();

return curry_n($count, $function);

}

As you can see, there is no easy way to determine the number of parameters a function is expecting. We also had to add a parameter to determine whether we should consider all parameters, including those with a default value, or only the mandatory ones.

You might have noticed that we don't create functions that take strictly one parameter; instead, we used the func_get_args function to get all passed parameters. This allows the use of currying functions more naturally and is also on a par with what is done in functional languages. Our definition of currying is now more along the lines of A function that returns a new function until it receives all its arguments.

The examples in the remainder of the book will assume that this curry function is available and ready to use.

At the time of writing, there is an open pull request on the functional-php library to incorporate this function.

Parameter order matters a lot!

As you might remember from the first chapter, array_map and array_filter functions have their parameters in different orders. For sure, it makes them a bit more difficult to use, as you are more prone to getting it wrong, but it is not the only issue this poses. To illustrate why parameter order matters, let's create curryied versions of both of them:

<?php

$map = curry_n(2, 'array_map');

$filter = curry_n(2, 'array_filter');

We are using curry_n functions for two different reasons here:

· The array_map function accepts a variable number of arrays, so we enforce the value to 2 to be on the safe side

· The array_filter function has a third parameter named $flag for which the optional value is fine

Remember the order parameters of our newly curryied functions? The $map parameter will take the callback first, and the $filters parameter expects the collection first. Let's try to create a new useful function knowing this:

<?php

$trim = $map('trim');

$hash = $map('sha1');

$oddNumbers = $filter([1, 3, 5, 7]);

$vowels = $filter(['a', 'e', 'i', 'o', 'u']);

Our mapping examples are really basic but serve some purpose, whereas our filtering examples are just static data. I bet you can find some way to use $trim and $hash parameters, but what are the chances you will need a list of odd numbers or vowels ready to be filtered?

Another example can be taken from a bit earlier in this chapter-remember our curryied example of substr function?

<?php

function substr_curryied(string $s)

{

return function(int $start) use($s) {

return function(int $length) use($s, $start) {

return substr($s, $start, $length);

};

};

}

$f = substr_curryied('Lorem ipsum dolor sit amet.');

$g = $f(0);

echo $g(5);

// Lorem

I can guarantee you it would be a lot more useful if we could first define the start and length to create. For example, a $take5fromStart function; instead of having this awkward $substrOnLoremIpsum parameters, we simply called the $f parameter in the example.

The important thing here is that the data you want to work upon, your "subject", must come last because it greatly increases reuse of your curryied functions and lets you use them as parameters to other higher-order functions.

As in the last example, let's say we want to create a function that takes the first two letters of all elements of a collection. We will try to do it with a set of two functions, where the arguments are in different orders.

The implementation of the function is left as an exercise, as it does not really matter to drive the point home.

In example one, the subject is the first argument:

<?php

$map = curry(function(array $array, callable $cb) {});

$take = curry(function(string $string, int $count) {});

$firstTwo = function(array $array) {

return $map($array, function(string $s) {

return $take($s, 2);

});

}

The parameter order forces us to create wrapper functions. In fact, it doesn't even matter that the functions are curryied because we cannot use this fact.

In example two, the subject is at the end:

<?php

$map = curry(function(callable $cb, array $array) {});

$take = curry(function(int $count, string $string) {});

$firstTwo = $map($take(2));

As a matter of fact, a well-chosen order also helps a lot with function composition, as we will see in the following section.

As a final note on the subject and to be completely fair, the version using functions with backward parameters could have been written using the partial_right function from the functional-php library, and you could use the partial_any function for functions with more parameters in strange orders. But even so, the solution is not as simple as the one with the arguments in the right order:

<?php

use function Functional\partial_right;

$firstTwo = partial_right($map, partial_right($take, 2));

Using composition to solve real issues

As an example, let's say that your boss comes in and wants you to produce a new report with the phone numbers of all users that have registered in the last 30 days. We assume that we have the following class representing our users. Obviously, a real class will store and return real data, but let us just define our API:

<?php

class User {

public function phone(): string

{

return '';

}

public function registration_date(): DateTime

{

return new DateTime();

}

}

$users = [new User(), new User(), new User()]; // etc.

Without any knowledge of functional programming, you might write something like this:

<?php

class User {

public function phone(): string

{

return '';

}

public function registration_date(): DateTime

{

return new DateTime();

}

}

$users = [new User(), new User(), new User()]; // etc.

A first look at our function tells us that it is not pure, as the limit is computed inside the function, thus subsequent calls could result in a different user list. We could also leverage the map and filter functions:

<?php

function getUserPhonesFromDate($limit, $users)

{

return array_map(function(User $u) {

return $u->phone();

}, array_filter($users, function(User $u) use($limit) {

return $u->registration_date()->getTimestamp() > $limit;

}));

}

Depending on your preferences, the code might now be a bit easier to read, or not at all, but at least we have a pure function and our concerns are a bit more separated. We can, however, do better. Firstly, the functional-php library has a function that allows us to create a helper calling a method on an object:

<?php

use function Functional\map;

use function Functional\filter;

use function Functional\partial_method;

function getUserPhonesFromDate2($limit, $users)

{

return map(

filter(function(User $u) use($limit) {

return $u->registration_date()->getTimestamp() >$limit;

}, $users),

partial_method('phone')

);

}

It is a bit better, but if we accept having to create some new helper functions, we can improve the solution even more. Also, those helper functions are new building blocks we will be able to reuse:

<?php

function greater($limit) {

return function($a) {

return $a > $limit;

};

}

function getUserPhonesFromDate3($limit, $users)

{

return map(

filter(compose(

partial_method('registration_date'),

partial_method('getTimestamp'),

greater($limit)

),

$users),

partial_method('phone')

);

}

If we have curryied versions of filter and map functions, we can even create a function that only takes a date and returns a new function that can be further composed and reused:

<?php

use function Functional\partial_right;

$filter = curry('filter');

$map = function($cb) {

return function($data) use($cb) {

return map($data, $cb);

};

};

function getPhonesFromDate($limit)

{

return function($data) use($limit) {

$function = compose(

$filter(compose(

partial_method('getTimestamp'),

partial_method('registration_date'),

greater($limit)

)),

$map(partial_method('phone'))

);

return $function($data);

};

}

As a good reminder about the necessity of having a good parameter order, since the map function from the functional-php library has the same signature as the original one from PHP, we had to curry it manually.

Our resulting function is a tad longer than the original imperative one, but, in my opinion, it is easier to read. You can easily follow what is happening:

1. Filter the data using:

1. The registration date

2. From this, you get the timestamp.

3. Check whether it's greater than the given limit.

2. Map the phone method on the result.

I totally agree if you find that the name partial_method is not ideal and that the presence of the calls to the compose function somehow makes it a bit difficult on the eyes. As a matter of fact, in a hypothetical language with a compose operator, auto-currying, and some syntactic sugar to defer a call to a method, this could look like this:

getFromDate($limit) = filter(

(->registration_date) >>

(->getTimestamp) >>

(> $limit)

) >> map(->phone)

Now that we have our function, your boss walks right back in your office with new requirements. In fact, he only wants the three most recent registrations from the last 30 days. Easy, let's just compose our new function with some more building blocks:

<?php

use function Functional\sort;

use function Functional\compare_on;

function take(int $count) {

return function($array) use($count) {

return array_slice($array, 0, $count);

};

};

function compare($a, $b) {

return $a == $b ? 0 : $a < $b ? -1 : 1;

}

function getAtMostThreeFromDate($limit)

{

return function($data) use($limit) {

$function = compose(

partial_right(

'sort',

compare_on('compare', partial_method('registration_date'))

),

take(3),

getPhonesFromDate($limit)

);

return $function($data);

};

}

In order to take a certain number of items from the beginning of an array, we need to create a take function around array_slice function. We also need a function to compare values, which we can do simply because DateTime function overloads the comparison operators.

Again, the functional-php library gets argument order wrong for sort function, so we need to partially apply instead of curry. And the compare_on function creates a comparator given a comparison function and a "reducer" which is called on each item being compared. In our case, we want to compare the registration date, so we reuse our different method application.

We need to perform the sorting operation before filtering because our getPhonesFromDate method returns only the phone numbers as the name suggests. Our resulting function is itself a curryied composition of other functions, thus allowing easy reuse.

I hope this small example has finished convincing you of the power of using small functions as building blocks and composing them to solve issues. If that is not the case, maybe one of the more advanced techniques we'll see in the following chapters will do it for you.

As a final note, as you perhaps gathered from the examples, PHP is sadly missing a lot of utility functions to make the life of a functional programmer easy. Also, even the functional-php library, which is widely used, gets some parameter orders wrong and is missing some important pieces of code, such as currying.

By combining multiple libraries, we could have a better coverage of the required features, but it would also add a lot of useless code and some mismatched function names that wouldn't really make your life easier.

What I can recommend is to keep a file with all the little gems you create along the way and soon you will have your own compilation of helpers that really suits your need and coding style. This advice might go against best practices concerning reusable packages with a large community around them, but until someone creates the right library, it helps a lot. And who knows, you might be the one who has enough energy to create the missing pearl in the functional PHP ecosystem.

Summary

This chapter revolved around function composition, which is a really powerful idea once you get used to it. By using small building blocks, you can create complex processes while keeping the readability and maintainability offered by short functions.

We also talked about the partial application and the most powerful concept of currying, which allow us to easily create more specialized versions of existing functions and rewrite our code to be more readable.

We tackled argument order, a topic which is often brushed off but is really important as soon as you want to use higher-order functions. The combination of currying and a correct parameter succession allowed us to reduce the need for boilerplate code and wrapper functions, a process sometimes referred to as eta-reduction.

Finally, with all the aforementioned tools, we tried to demonstrate solutions to some issues and problems that you could encounter in your day-to-day programming to help you write better code.