6 min read

Functional Programing with PHP7

Many developers like to talk about functional programming, but most have never written a working implementation, nor understand the concepts practically without putting in the practice itself. The reason is quite simple: we are taught to think in an imperative manner when we first start learning to program and any developer or engineer who thinks highly of themselves refuse to accept they need to actually forget what they think they know and take the time to challenge themselves and re-learn everything again from scratch.

I'm proud to know the developers I've hired are open-minded, humble, and thirsty for new challenges! And I expect nothing less of myself.

So what is Functional Programming?

Other than being the method in which a regular human brain computes, functional programming streamlines the applications logic around abstractions such as higher order functions which are inherently stateless and allow you to be as DRY as possible while remaining readable and easy to follow with your logic.

In short; Functional Programming allows for complexities to be trivial to follow.

Because your abstractions are stateless it is up to your main application logic to maintain a certain clarity over what you would normally expect as a 'state', because essentially Functional Programming is stateless too. Instead of maintaining a central state for each lifecycle of your processing you would instead pass the state along the chain of processing in an ever forward momentum of executions.

Although Functional Programming itself is only the main application logic PHP however is Imperative fundamentally so it is our job to create the abstractions that allow us to be as functional as possible with our main application logic.

Recursion = PHP without imperative programming

We'll also see a rise in lambda or anonymous function usage (i.e. closures; to be covered in detail further on) as we will be expecting all of our functions to take a function as a parameter, return a function, or both.

This will lead you to rely less on OOP in general and rather focus abstractions on more functional friendly collections, factories, and yes singletons too.

If you aren't already familiar with the concepts of general OOP design patterns, in particular collections and factories, now is the time to learn them as they will make your abstractions possible and therefore provide what is needed to create a truly functional application.

Recursion

Functional PHP would not include any loops, foreach, for, while, do while, are all taken away from you and replaced with the concept of recursion.

Suppose we want to write a function to find the sum of all elements in an array (forget that array_sum exists for the time being). In a functional style, we would write;

function sum($array) {
  if (empty($array)) {
    return 0;
  } else {
    return $array[0] + sum(array_slice($array, 1));
  }
}

$total = sum([1, 2, 3]); // 6

An empty list will return 0, which is our base condition. For an array with more than one value, it will return the results of adding the first element with the recursive sum of all other elements.

Fun fact: I included this in the Punters.com.au coding challenge and to date no candidate has demonstrated functional programming

Functional building blocks

There are tools you must first provide PHP with before you can get started employing Functional Programming.

I've put the entire project on GitHub for you, It is production ready.

chrisdlangton/php-functional-programming

php-functional-programming - The Building Blocks for PHP to be able to provide true Functional Programming

We'll start with a higher order function called array_each which abstracts looping and allows you to perform your usually looped actions when a collection or return value isn't required and explain how it is different from the array_map function that does return a value.

function array_each(array $items, $func): void {
  foreach ($items as $key => $item) {
    $func($item, $key);
  }
}

$users = [
  [
    "id" => "1",
    "username" => "John",
    "email" => "john@domain.tld"
  ],[
    "id" => "2",
    "username" => "Jane",
    "email" => "jane@domain.tld"
  ]
];

array_each($users, function($user) {
  emailTo($user['email'], 'Welcome to my blog')
});

You can see clearly that the array_each function provides no return value as it iterates, it's purpose is to consume the data and do something with each item it finds - brilliant!

If you intend to extract certain information for later use as a collection, perhaps a list of username's, you would instead need to utilise array_map like so;

function array_map(array $items, $func): array {
  $results = [];
  foreach ($items as $key => $item) {
    $results[] = $func($item, $key);
  }
  return $results;
}

$users = [
  [
    "id" => "1",
    "username" => "John",
    "email" => "john@domain.tld"
  ],[
    "id" => "2",
    "username" => "Jane",
    "email" => "jane@domain.tld"
  ]
];

$usernames = array_map($users, function($user){
  return $user['username'];
});

print( implode(', ', $usernames) ); // John, Jane

In your implementation you'd have these higher order functions referenced by your script and therefore you'd only be writing the 3 lines of usage, clearly functional is easier to read #amiright

More advanced examples

Looking deep at these implementations you might already see that we have much more expressive power than has been demonstrated. For example, you can use array_each and array_map on multidimensional arrays as well as make use of PHP's use on closures and of course pass into your closure scope another variable by reference if need be - to demonstrate;

$data = [
  "foo" = "bar",
  "baz" = true,
  "users" => [
    [
      "id" => "1",
      "username" => "John",
      "email" => "john@domain.tld"
    ],[
      "id" => "2",
      "username" => "Jane",
      "email" => "jane@domain.tld"
    ]
  ]
];

$usernames = [];
array_each($data, function($item, $key) use (&$usernames) {
  if ($key === 'users') {
    $usernames = array_map($item['users'], function($user) {
      return $user['username'];
    });
  }
});

print( implode(', ', $usernames) ); // John, Jane

In PHP functions can be passed as arguments to other functions and a function can return other functions (a feature called higher-order functions). Both user-defined and built-in functions can be referenced by a variable and invoked dynamically.

On higher-order functions

The most common usage of higher-order functions is when implementing a strategy pattern.

$input = [1, 2, 3, 4, 5, 6];

$filter_even = function($item) {
    return ($item % 2) == 0;
};

$output = array_filter($input, $filter_even);

print_r( implode(', ', $output) ); // 2, 4, 6

I've mentioned closures a few times and I'll take the time now to explain them so you can see for yourself their usefulness, actually, their importance to implement functional programming.
A closure is an anonymous function that can access variables imported from the outside scope without using any global variables. Basicly, a closure is a function with some arguments fixed (closed) by the environment when it is defined. Closures can work around variable scope restrictions in a clean way readable way that the human can understand just by glancing at the code, a core point of employing functional programming.

In the next example we use closures to define a higher-order function returning a single filtering function, to be used by PHP's array_filter;

$greater_than = function($min) {
    return function($item) use ($min) {
        return $item > $min;
    };
};

$input = [1, 2, 3, 4, 5, 6];
$output = array_filter($input, $greater_than(3));

print_r( implode(', ', $output) ); // 4, 5, 6

Early binding is used by default for importing $min variable into the created function. For true closures with late binding one should use a reference when importing. Imagine a templating or input validation library, where closure is defined to capture variables in scope and access them later when the anonymous function is evaluated.

The fun stuff; Immutability

Immutability

Not so fun in PHP actually.
Immutability is the behavior that a value of a variable cannot be changed once it is defined. Different languages have different ways of achieving this; in PHP, for example, the only way to make a variable immutable is to define it as a constant.

An immutable object cannot be modified by accident

This also has an extra benefit of making testing easier because complex objects do not need to be rebuilt. Because all objects are safe from modification by default, and all changes are explicit, a significant amount of cognitive load can be reduced by not having to follow complex method chains to see where changes occur.

PHP is fairly unique among popular programming languages in that the array type can be used in so many ways. All of the following are arrays in PHP;

$dictionary = [
    'rabbit' => 'fun pet',
    'szechuan' => 'spicy deliciousness',
    'php7' => 'not php as you know it'
];
$orderedList = ['amazing', 'functional', 'functional', 'programming'];
$unorderedList = [0, 8, 2, 32, 4];
$set = ['total war', 'sins of a solar empire', '0 AD'];

Data type structures:

  • Dictionary, an associative array that consists of a key and a value.
  • OrderedList, a list that contains any number of values in a defined order.
  • UnorderedList, a list that contains any number of values in an undefined order.
  • Set, a set of values that contains no duplicates.

While the PHP approach provides a great amount of flexibility it also makes it more difficult to validate data that is being passed through an application. By using more strict data structures, we can avoid these problems and have more testable code. Anyone can assign a value in an unexpected way with new functionality given an array is representative of these 4 structures.

In my repo you'll find these data type structures and several building blocks needed to kick start your dive into Functional Programming with PHP;