I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

Reuse your closures with functors

12.28.2010
| 8093 views |
  • submit to reddit
I like PHP closures and their superset, anomyous functions, as they implement the Command pattern very well when used correctly. However I feel that sometimes they are:
  • difficult to reuse: if you want to reuse a closure or anonymous function in two different places, you'll have to share the creation code by putting it in a shared class or method. We have no separation between the creation code (which for an object would be a new operator and the code of the closure itself. It's a bit like eval().
  • Difficult to force contracts on. You can't type hint a closure: even if a closure takes five arguments and another no arguments at all, you can't differentiate between them as parameters in a method signature.
For the latter case, you can just use the undocumented Closure type hint, but it will just catch if an object is passed to the method in question, not if the wrong closure is.
function takesAsInputAnotherFunction(Closure $closure)

This hack is explicitly stated as an implementation detail which should not be relied upon in the PHP manual

__invoke() to the rescue

What if we wanted instead, a closure which we can instance even more than one time (maybe with different variables), and that we could type hint?
In this case, we need to return to the object-oriented paradigm by defining a class, and then use __invoke() to keep the syntactic sugar of being able to call it very simply:

$object($argument1, $argument2);$object($argument1, $argument2);

This kind of object is called a functor.

The definition of a class is longer than the creation code of a closure, so there is a trade-off: we have better specified code but we lose conciseness. Type hints are not only a defensive programming construct: they serve also as documentation as the next programmer reading a method's signature would learn what he can pass in just from the parameters hints instead of going reading the tests or grepping for the method in his working copy:

grep -r '->methodName(' .

becomes just a quick look at the definition

public function methodName(AdderCallback $callback) { // ...

However the expressing power of an object implementing __invoke() and of a closure are equivalent, and when you find yourself rewriting the same closure over and over, you may want to keep it in only one place. You'll have to create a class to hold the creation code anyway, so why not making it a class of its own?

For the PHP interpreter, closure and functors are really the same thing, and are even more swappable than callbacks built with array($object, 'methodName'). is_callable() returns true for both of them, and you can write $closure() as well as $functor().

Example

Here is some code that you can hack if you want to play with closures and functors.

<?php
$square = function($value) {
return $value * $value;
};

var_dump($square(3));

/**
* a little more noisy, but equivalent in usage and functionality-
*/
class SquareCallback
{
public function __invoke($value)
{
return $value * $value;
}
}

$squareObject = new SquareCallback;
var_dump($squareObject(3));

// using PHP utilities which works on callback: total equivalency
var_dump(is_callable($squareObject));

$array = array(0, 1, 2);
var_dump(array_map($square, $array));
var_dump(array_map($squareObject, $array));

// currying vs collaborators: total equivalency
$toAdd = 5;
$adder = function($value) use ($toAdd) {
return $value + $toAdd;
};
var_dump($adder(3));

/**
* A LOT more noisy
*/
class AdderCallback
{
private $numberToAdd;

public function __construct($numberToAdd)
{
$this->numberToAdd = $numberToAdd;
}

public function __invoke($value)
{
return $value + $this->numberToAdd;
}
};
$adder = new AdderCallback(5);
var_dump($adder(3));

// but only classes can do this
function someMethodOrFunction(AdderCallback $adder)
{
// ...
}
Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Matthew Weier O... replied on Wed, 2010/12/29 - 3:13pm

Functors are problematic when it comes to interfaces and/or implementations. Because __invoke() is a magic method, concrete classes can always re-define the number of arguments that may be accepted (__construct() is the same in this regard). As a result, one implementation may require a single argument, another 3 -- leading to variations in behavior when you call with fewer arguments.

That said, the abilities to re-use, type-hint, and check against is_callable() are invaluable, and often make them much more worthwhile than callbacks.

Wil Moore replied on Thu, 2010/12/30 - 7:37pm

Nice article...and for those that are interested, Functors help make the strategy and state patterns more manageable as well.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.