Lambdas And Closures

June 25, 2014 —John Koster

When building our applications in Laravel, we often make use of the routing system. The most basic example of this is by using the Route facade, and then appending a HTTP verb, the URI and some weird function looking thing:

1<?php
2 
3Route::get('my/url', function()
4 {
5 return 'This is my url!';
6 });

This is not something new, or something that we are not used to seeing. But what exactly is going on? Some intermediate to veteran developers will recognize the anonymous function, or lambda function (a function without a name) that we are passing to the get method. However, this may seem confusing to new developers.

#What Are They?

As stated earlier lambda's are anonymous functions. That is, a function without a name. PHP allows you to create these lambda functions and assign them to a variable, or even pass them as arguments to a function (this is what we are doing with Laravel's routing).

1<?php
2 
3// Create an anonymous function and assign
4// it to a variable.
5$myFunction = function() {
6 return 'This is a lambda function';
7};
8 
9// Calling our anonymous function.
10echo $myFunction();

In the above code sample, we are creating an anonymous function and assigning it to a variable named $myFunction. After this, call our function. This looks similar to what we are used to, but we add the $ sign in front since we referencing our variable.

The above code example would print output this to the browser:

1This is a lambda function

We could even define function parameters, just like with normal functions:

1<?php
2 
3// A simple 'hello' function
4$hello = function($name) {
5 return 'Hello '.$name;
6};
7 
8echo $hello('Jill');

The code above would output something like this (depending on the name passed in):

1Hello Jill

#But Why?

These anonymous functions are useful when we as developers need to do something right then and there, but do not necessarily want, or need to create a dedicated function for the task. Like when we define routes in Laravel, it would be cumbersome to have to create a new function in the global scope every time we wanted to define a new route.

Another use of anonymous functions is to pass them as function arguments. For example, let's say we are a developer writing a function that will filter an array. We know that we need to operate on an array, and we need to perform some sort of filtering algorithm, or function on each element of the array. So we start writing our filtering function, and we get pretty far. Then a few days later we need to filter another array in a different way. So we think "All right, I'll just write a new filtering function". This continues for a while, and your code contains functions that look like this:

1<?php
2 
3// Filtering madness.
4filterOnlyNewRegisters($array);
5filterHavingMoreThanThreeOrders($array);
6filterContainsLetterS($array);
7filterContainsLetterT($array);
8filterContainsLetterI($array);
9filterContainsNumber3($array);
10// ... and so on

That is no fun. In fact, that is a nightmare, especially if you are only using the function once! Lambdas to the rescue!

#Lambdas as Function Arguments

Thinking back on our previous hypothetical scenario, we can be thankful that there is a built in PHP function named array_filter. This function filters elements of an array using a callback function. That callback function will be the lambda function that you pass in as an argument.

However, we can create our own function that utilizes anonymous functions as an acceptable argument. The first thing we have to do is define what our function is going to do.

Our function will be a simple utility function called if_null_then. It will check if a value is null, if it is, then it returns a default value $then, or does something the developer wants. If the value is not null, it is simply returned as the value of the function. Make sense? So we could use the function we are going to build like this:

1<?php
2 
3function iHateNulls()
4{
5 $checkValue = null;
6 
7 // The value of $checkValue would be `I hate nulls` after this line:
8 $checkValue = is_null_then($checkValue, 'I hate nulls');
9 
10 $checkValue = 'candy';
11 
12 // The value of $checkValue would be 'candy' after this line:
13 $checkValue = is_null_then($checkValue, 'I hate nulls');
14 
15}

But what about actually doing something when the value is null, instead of just returning a value? What would that look like?

1<?php
2 
3function iStillHateNulls()
4{
5 $checkValue = null;
6 
7 $checkValue = is_null_then($checkValue, function()
8 {
9 // The value was null, so let's just exit the script.
10 exit;
11 });
12}

Okay, that's pretty cool. But what would it look like to actually implement this function? Well, here is the completed code for this function (taken from my in-progress open-source library):

1<?php
2 
3if (!function_exists('if_null_then'))
4{
5 /**
6 * Returns a value based on whether another value is null.
7 *
8 * If you need to check if a value exists in an array, such as a checkbox
9 * input, consider using the `array_key_exists_then_or` for a simple inline
10 * approach.
11 *
12 * @param mixed $check The value to check for null.
13 * @param \Closure|mixed $then The value to return if $check is null.
14 * @return mixed
15 */
16 function if_null_then($check, $then)
17 {
18 if ($check === null)
19 {
20 if (is_object($then) and $then instanceof Closure) { return $then(); }
21 
22 return $then;
23 }
24 
25 return $check;
26 }
27}

The important bits for this post are the lines of code that live inside the is_null_then() body. First we are just checking if $check is actually null. If it is not, we return $check. Simple enough.

When $check is null however, we first check to see if the value of $then is an instance of the Closure class. If it is we return from the function by calling the closure, which is done by treating the variable as a function. Notice the { return $then(); } at then of line 20? Lastly, if the value of $then is not an instance of a Closure, we simply return it.

That's it! We now know how to make a simple function that can either accept a regular-old-vanilla value or a really cool inline function. Hope everyone else has had fun.

Some absolutely amazing
people

The following amazing people help support this site and my open source projects ♥️
If you're interesting in supporting my work and want to show up on this list, check out my GitHub Sponsors Profile.