DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Full-Stack Observability Essentials: Explore the fundamentals of system-wide observability and key components of the OpenTelemetry standard.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • What Is Applicative? Basic Theory for Java Developers
  • What Is a Functor? Basic Theory for Java Developers

Trending

  • Demystifying Enterprise Integration Patterns: Bridging the Gap Between Systems
  • Automated Testing Lifecycle
  • Automated Testing: The Missing Piece of Your CI/CD Puzzle
  • Getting Started With Prometheus Workshop: Instrumenting Applications

Reuse your closures with functors

Giorgio Sironi user avatar by
Giorgio Sironi
·
Dec. 28, 10 · Interview
Like (0)
Save
Tweet
Share
11.35K Views

Join the DZone community and get the full member experience.

Join For Free
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)
{
// ...
}
Functor

Opinions expressed by DZone contributors are their own.

Related

  • What Is Applicative? Basic Theory for Java Developers
  • What Is a Functor? Basic Theory for Java Developers

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: