What are anonymous functions?
Many programming languages in use today support the notion of anonymous functions out of the box, sometimes also referred to as lambda functions. PHP is amongst this group of languages.
So what is an anonymous function?
Well, speaking in terms of programming:
That's precisely and solely what an anonymous function is.
The idea of lambda functions
As stated earlier, anonymous functions sometimes go by the name lambda functions.
If you're curious about the origin of the term 'lambda' here, the idea of lambda functions in programming was actually borrowed from a similar idea in mathematics, particularly in lambda calculus.
Now you might be thinking why would anyone ever need a function without a name? Well, there might be some cases where this applies.
Recall the array_map()
function from the PHP Arrays — Functions chapter. Let's say you want to map an array of integers to an array of the squares of those integers.
One way is to obviously use a named function as we did in that chapter:
<?php
function to_square($num) {
return $num ** 2;
}
$nums = [10, 12, -5, -3, 0];
$squares = array_map('to_square', $nums);
print_r($squares);
However, if you think about it for a while, creating a named function for this task is more-or-less an overkill. We ideally want a quick and easy way to specify the mapping rule of squaring an element, without having to worry much about the name of the function defining this rule.
What if the rule becomes an extremely complex one? What will we name the function then?
Moreoever, we don't even need to pollute the global namespace with a function that's only meant to be called by array_map()
, and possibly only once.
What we really want here is an anonymous function.
Let's now see how to create anonymous functions in PHP.
Creating anonymous functions
We've already seen how to create an anonymous function in the PHP Functions — Basics chapter.
It's really simple. Everything is the same as in defining a named function in PHP except for one thing: just omit the name of the function. And now what you have behind is an anonymous function.
Here's the syntax of an anonymous function:
function($param_1, $param_2, ..., $param_n) {
...
}
One important thing to note here is that, unlike named function definitions in PHP (which otherwise denote statements), anonymous function definitions denote expressions.
Hence, wherever PHP expects an expression, an anonymous function can be provided there.
In fact, this is a feature of anonymous functions: to act as expressions so that it's super convenient for us to directly provide them to functions, as callbacks, and return them from functions (the most common uses of anonymous functions).
Let's now define some anonymous functions.
Below, we rewrite the array_map()
code above, utilizing an anonymous function to define the mapping rule instead of this being done by a named function:
<?php
$nums = [10, 12, -5, -3, 0];
$squares = array_map(function($num) { return $num ** 2; }, $nums);
print_r($squares);
Notice how much simplified this code is compared to the previous one. There is no need to first create the function and then pass its name to array_map()
, and neither is there a name in the global context that's not supposed to be there.
Over to example number two.
In the code below, we have a function get_greeting_fn()
that takes in an argument specifying whether we are greeting a programmer or a teacher and then returns back an appropriate function to make the actual greeting:
<?php
function get_greeting_fn($type = null) {
if ($type === 'programmer') {
return function($name) {
echo "Hello programmer $name.\n";
};
}
elseif ($type === 'teacher') {
return function($name) {
echo "Hello teacher $name.\n";
};
}
else {
return function($name) {
echo "Hello $name.\n";
};
}
}
The returned function itself takes the name of the person to greet and, when called, outputs the greeting message to the terminal.
Let's get some greeting functions and call them to make a couple of greetings:
<?php
function get_greeting_fn($type = null) {
/* ... */
}
$greet_programmer = get_greeting_fn('programmer');
$greet_programmer('Alice');
$greet_programmer('Bob');
echo "\n";
$greet_teacher = get_greeting_fn('teacher');
$greet_teacher('Alice');
$greet_teacher('Bob');
echo "\n";
$greet = get_greeting_fn();
$greet('Alice');
$greet('Bob');
$greet_programmer
holds a function meant to greet programmers, $greet_teacher
holds a function meant to greet teachers, and $greet
holds a function meant to greet just about anyone. We call each of these functions twice, with a given set of names, in order to greet given people.
The point to note here is that by virtue of returning a function from get_greeting_fn()
, we are able to customize how the greeting essentially happens.
While this same thing could've been accomplished by using a single generic greet_person()
function having two parameters: the first one representing the type of person to greet and the second one representing that person's name, using a function that returns a concrete greeting function has the benefit that we don't have to provide the type of the person again and again.
For example, imagine we have a list of 100 teachers to greet. We'd be better off at using a function that knows that we are greeting teachers (like $greet_teacher()
above) than using a generic function where we ought to provide this information with each invocation.
Even though this example, and the one shown above, may not be that practical, it does illustrate a common pattern used in complex PHP programs, i.e. functions returning other functions that respect some of the configurations we pass on while calling the outer functions.
Higher-order functions
A function that accepts in a function as argument or returns a function, or does both of them, is referred to as a higher-order function.
So in the code above, get_greeting_fn()
is a higher-order function as it returns a function. However, the anonymous function returned by get_greeting_fn()
is not a higher-order function since it neither accepts a function as argument, nor does it return a function.
Moving on, if you notice one thing in the get_greeting_fn()
function above, there is an unnecessary amount of repetition involved in it. In each conditional statement, we return more-or-less the same kind of function: it takes in a name and prints a greeting message.
We could largely simplify this code, removing all the conditional statements and reducing down to just one single return
. But this could only be done once we understand the concept of closures in PHP.
What are closures?
Anonymous functions are functions without a name. Period. This doesn't, however, tell us anything about how the functions interact with their surrounding environment, i.e. are they able to access variables from the surrounding environment.
That aspect essentially boils down to how the programming language implements anonymous functions and what bells and whistles it gives to them.
Talking about PHP, it implements anonymous functions as closure functions, or simply closures.
A closure is basically a function that is able to remember variables from its enclosing environment.
Quite often, people talk about the 'remembrance' factor of closures as follows: a function f()
has a local variable $x
and returns another function. If that returned function is a closure, then it'll be able to read and write to the variable $x
from its outer environment, that is, the local environment of f()
.
In PHP, all anonymous functions are closures and, conversely, all closures are anonymous functions. Therefore, the two terms are interchangeable in PHP parlance.
If we were to be really precise, anonymous functions in PHP are instances of the built-in Closure
class.
To check if an arbitrary callable value is a closure (or identically, an anonymous function), we can just check if it's an instance of the Closure
class.
An example is illustrated as follows:
<?php
$value = function() {};
var_dump($value instanceof Closure);
This example uses ideas in PHP that are tied to working with objects and classes — in particular, the instanceof
operator. We'll learn more about them in the PHP OOP unit.
A closure really isn't a closure until it accesses a variable from its enclosing environment. After all, this is the real essence of closures — to keep track of their enclosing environments (a thing which couldn't be done by named functions in PHP).
But the problem here is that a closure function in PHP can't access anything until we explicitly allow it to access that thing. This is done using the use
keyword.
Consider the following code:
<?php
function f() {
$x = 10;
return function() {
echo $x;
};
}
$func = f();
$func();
We have a function f()
returning a closure that tries to access the local variable $x
of f()
and return it. The function f()
is called and then the function it returns.
Surprsingly enough, when we run this code, we get the following warning:
The variable $x
in the statement echo $x
, in the anonymous function, doesn't seem to exist.
But why is this so?
Well, by design, and by default, PHP does NOT allow a closure access to any outer variable. That's why when we access $x
from within the closure above, PHP complains about a non-existent variable.
If we want to give the closure access to a given outer variable, or a set of outer variables, we ought to do so explicitly, using the use
keyword.
Here's the syntax of use
when used with a closure:
function(...) use ($var_1, $var_2, ..., $var_n) {
...
}
The use
keyword comes after the parameter list in the anonymous function's definition, followed by a list of variables to access from the outer scope. Simple.
Let's solve the issue in the code above by allowing the closure access to the outer variable $x
:
<?php
function f() {
$x = 10;
return function() use ($x) {
echo $x;
};
}
$func = f();
$func();
Time to test the code:
And there you have it. The closure can now access the local variable $x
of f()
, all thanks to use
. Amazing.
Let's now consider a more involved example.
Recall the get_greeting_fn()
function from the section above, returning a different anonymous function based on the type of person provided to it. Also recall how much repetition was going on in its definition above.
Now, it's time to improve the implementation of get_greeting_fn()
using the power of closures.
Here's the new definition of get_greeting_fn()
:
<?php
function get_greeting_fn($type = null) {
return function($name) use ($type) {
echo "Hello $type $name.\n";
};
}
As promised earlier, by leveraging the fact that the returned function here is a closure, we've effectively cleaned up the get_greeting_fn()
to a great extent. The conditionals are gone and we now have just one return
.
It can't get any better!
Now, as always, let's test the function:
<?php
function get_greeting_fn($type = null) {
/* ... */
}
$greet_programmer = get_greeting_fn('programmer');
$greet_programmer('Alice');
$greet_programmer('Bob');
echo "\n";
$greet_teacher = get_greeting_fn('teacher');
$greet_teacher('Alice');
$greet_teacher('Bob');
echo "\n";
$greet = get_greeting_fn();
$greet('Alice');
$greet('Bob');
Yes, we indeed get the desired output, however the last set of outputs has an odd spacing. This is because of the way $type
is interpolated in the given template string.
In the code that follows we solve this issue using a simple trick:
<?php
function get_greeting_fn($type = null) {
$type = $type ? $type . ' ' : '';
return function($name) use ($type) {
echo "Hello $type$name.\n";
};
}
/* ... */
We add a space at the end of $type
if it is a string, otherwise make it an empty string. And then, in the string, we interpolate $type
right before the $name
variable.
Perfect!
More about use
The use
keyword's semantics when used with anonymous functions are not really that difficult to understand but they sure have many aspects to them which we didn't cover in the section above for brevity. We'll now do so in this very section.
Variables are use
d by-value, by default
To start off, note that whatever variables are passed on to an anonymous function via use
are by value, not by reference, unless we explicitly do so.
Hence, if we have a variable assignment statement in an anonymous function, accessing an outer variable, it won't actually end up modifying that outer variable. It's quite easy to verify this behavior.
Below, we create a function f()
with a static local variable $x
and return a closure from it, which, when called, would modify this variable $x
:
<?php
function f() {
static $x = 10;
echo "\$x: $x\n";
return function() use ($x) {
$x = 50;
};
}
Let's run f()
and then the returned function, followed by calling f()
another time in order to see the value of its local variable $x
:
<?php
function f() {
static $x = 10;
echo "\$x: $x\n";
return function() use ($x) {
$x = 50;
};
}
$func = f();
$func();
// Call f() again to see the value of $x.
f();
As can be seen in the output, the static local variable's value remains the same across both the calls to the function f()
.
So one thing that's clear is that passing a variable normally using use
and then modifying it inside the anonymous function has absolutely no effect on the original variable.
It's worthwhile mentioning here that the variable assignment statement $x = 50
inside the closure above (since it obviously doesn't apply to the outer $x
variable) is merely creating a local variable $x
, initialized to 50
.
Variables are captured upon the closure's definition
There is an aspect of closure functions which can be easily reasoned by the assumption that closure functions create and maintain internal slots, storing the values of variables at the site of their definition.
That aspect is detailed as follows:
<?php
function f() {
$x = 10;
$func = function() use $x {
echo $x;
};
$x = 50;
return $func;
}
We have the same setup as before except for two things. The anonymous function is not returned right away; instead, it's created and then the value of $x
mutated in f()
before finally getting to the return of the closure.
Now, before we get to execute this code, we encourage you to think about what would $x
be in the closure. 10
or 50
?
Follow your natural intuition to answer this question. Once done, continue on reading below.
If you answered 50
, congratulations! You indeed thought the way anyone, be it a newbie or someone experienced with closures beforehand from another language, would have thought about this code. Unfortunately, it's the wrong answer!
Let's see how and why.
use
d variables right at the site of its definition and stores them in internal slots.If we thereafter modify those variables outside the closure, the closure won't bother to update its internal slots. This is because it does not store the variables by reference, by default, even though we can do so (more on that later below).
Saving variables at site of definition does make some sense!
The fact that a closure in PHP captures all the variables given by use
right at the instant of its definition seems to be a fair behavior since a closure in PHP is an instance of the Closure
class.
An instance, when it's being instantiated, has to be given all the necessary pieces of data for it to work; and for an instance of Closure
, this data is the set of variables to save from the outer environment.
The moment a closure is defined, it's actually equivalent to the Closure
class being instantiated. During this process, the closure instance stores the current values of all the use
d variables in it. Thereafter, if we change any of those variables in the outer environment directly, the closure won't pick up the changes because its internal slots have already been finalized with the respective values.
Closure
class.Hence, in the code above, $x
inside the closure would be 10
. Now, as you would probably be thinking about this and saying that 'this is counter-intuitive,' well, to some extent, yes it sure is so.
But if we think about the whole design philosophy of PHP and that functions are only meant to have local variables, it does seem to align with that design philosophy. There's an interesting discussion of this aspect, amongst many others, of closures at RFC: Lambda functions and closures — wiki.php.net
Variables can be use
d by-reference
Anyways, coming back to the use
keyword, if it's desired to be able to modify outer variables from a closure, what we need is...you guessed it: references.
Just like we can precede parameters in a parameter list in a function's definition to get them by reference, we can do so in the list of variables following the use
keyword.
Syntactically, it looks similar to the following:
function(...) use (&$param_1, ...) {}
Let's consider a quick example.
In the following code, we rewrite the static
local variable example above, leveraging a pass-by-reference use
d variable in order to be able to mutate the static variable from within the closure:
<?php
function f() {
static $x = 10;
echo "\$x: $x\n";
return function() use (&$x) {
$x = 50;
};
}
$func = f();
$func();
// Call f() again to see the value of $x.
f();
As you can see, the second call of f()
outputs 50
, thereby confirming that the statement $x = 50
in the closure did indeed have an effect on the outer variable $x
, the local variable of f()
.
The rule is pretty simple: if you want to mutate an outer variable inside a closure, pass it by reference when capturing it in the closure via use
.
Normal parameters can be used along with use
While we pass on variables from the enclosing environment to an anonymous using use
, it's still completely alright (and rightly so) to pass normal parameters to the function.
Consider the code below:
<?php
function f() {
static $x = 10;
echo "\$x: $x\n";
return function($a) use (&$x) {
$x += $a;
};
}
f()(100);
f();
The anonymous function returned by f()
takes in a parameter $a
and adds it to the outer variable $x
, mutating that outer variable with the result of this computation.
Also notice the way in which we call this anonymous function here — instead of saving the return value of f()
, which is the anonymous function, inside a variable $func
and then calling the variable as $func()
, we directly call the anonymous function via f()()
.
Here's the output produced:
The first call of f()
echoes '$x: 10' as the static variable gets initialized with 10
with this first invocation of the function f()
. Then, the returned function is called with the argument 100
. This has the effect of mutating the local variable $x
of f()
by adding 100
to it.
Finally, calling f()
again tells us about the current value of the static variable $x
of f()
, which turns out to be 110
(100
+ 10
).