March 25, 2012

Partial Function Application in PHP

Starting with PHP 5.3.0, using partial application in your app is a viable alternative to some existing design patterns. This post will explore some ways in which partial function application can be used in your code.

What is partial application?

Partial function application is the process of creating a new function from an existing function with some of arguments already specified, or applied. In this contrived example, imagine having a commonly used function that takes 4 or 5 arguments, the first couple always being the same. You could write a helper function:

function irritating_fn($a, $b, $c, $d, $e) {
    // do some useful work behind an annoying interface
}

function less_irritating($c, $d, $e) {
    $a = 'foo';
    $b = 'bar';
    return irritating_fn($a, $b, $c, $d, $e);
}

That works well enough, but is not a general solution. Rather than writing a helper function for each case, why not generalize?

A Class-based Approach

This approach relies on the __invoke magic method introduced in PHP 5.3, which enables instances of classes to be called like functions. The PartialCallable constructor accepts a callable as the first argument, and an array of values to be applied.

// equivalent to irritating_fn('foo', 'bar', 'c', 'd', 'e');
$less_irritating = new PartialCallable('irritating_fn', array('foo', 'bar'));
$less_irritating('c', 'd', 'e');

When an instance of PartialCallable is called like a function it accepts a variable number of arguments, and merges them with the array provided in the constructor before applying them all to the callable.

A Function-based Approach

This solution builds on another new feature introduced in PHP 5.3: anonymous functions. In this approach, the PHP function partial_function takes a callable as a first argument, and the arguments to be applied to the callable.

// equivalent to irritating_fn('foo', 'bar', 'c', 'd', 'e');
$less_irritating = partial_function('irritating_fn', 'foo', 'bar');
$less_irritating('c', 'd', 'e');

The new function $less_irritating will behave just like the irritating_fn, except its first two arguments are already applied.

Partial Application In Action

Partial application is good for things other than trimming down unwieldy argument signatures. It's also good for managing scope and avoiding writing out anonymous functions. In this slightly less contrived example, we have an array of names and a search corpus. We want the start position of each name within the corpus. Here it is solved with partial application:

$names = array(...); // array of names to search for
$corpus = "..."; // some large fixed search body
$callback = partial_function('strpos', $corpus);
$positions = array_map($callback, $names);

The alternative is slightly bigger, and PHP's closure syntax makes it a bit awkward:

$names = array(...); // array of names to search for
$corpus = "..."; // some large fixed search body
$callback = function($name) use($corpus) {
    return strpos($corpus, $name);
};
$positions = array_map($callback, $names);

Wrapping Up

Hopefully you've gotten a good grasp on what partial application is and what it can be useful for. If you have any other methods for using partial application in PHP, or any examples of it being used in the wild, fork the gist and let me know.