Functional PHP (2017)

Chapter 3. Functional Basis in PHP

After covering functions in PHP in the first chapter, followed by theoretical aspects of functional programming in the second, we will finally start to write real code. We will start with the available functions in PHP that allow us to write functional code. Once the basic techniques are well understood, we will move on to various libraries that will help us throughout the book.

In this chapter, we will cover the following topics:

· Mapping, folding, reducing, and zipping

· Recursion

· Why exceptions break referential transparency

· A better way of handling errors using the Maybe and Either types

· Functional libraries available for PHP

General advice

In the previous chapters, we described the important properties a functional application must have. However, we never really discussed how it can be achieved. Besides the various techniques we will learn about later on, there are a few simple pieces of advice that could really help you right away.

Making all inputs explicit

We discussed purity and hidden inputs, or side causes, a lot in the previous chapter. By now, it should be pretty clear that all the dependencies of your functions should be passed on as parameters. This advice, however, goes a bit further.

Avoid passing objects or complex data structure to your functions. Try to limit your input to what is necessary only. Doing so will make the scope of your function easier to understand and it will ease determining how the function operates. It also has the following benefits:

· It will be easier to call

· Testing it will require stubbing less data

Avoiding temporary variables

As you may have gotten to understand, state is evil-particularly global state. However, local variables are a kind of local state. As soon as you start peppering your code with them, you are slowly opening the can of worms. This is especially true in a language such as PHP, where all variables are mutable. What happens if the value changes along the way?

Each time you declare a variable, you have to keep its value in mind if you are to understand how the remainder of the code works. This greatly increases the cognitive burden. Also, as PHP is dynamically typed, a variable can be reused with totally different data.

When using a temporary variable, there is always the risk that it gets modified somehow or reused without it being evident, leading to bugs that are difficult to debug.

In nearly all cases, using a function is better than a temporary variable. Functions allow for the same benefits:

· Improving readability by naming intermediate results

· Avoiding repeating yourself

· Caching the result of a lengthy operation (this requires the use of memoization, which we will discuss in Chapter 8, Performance Efficiency)

The added cost of calling a function is usually minimal enough to not tip the balance. Also, using functions instead of temporary variables means that you can then reuse those functions in other places. They could also make future refactoring easier and they improve the separation of concerns.

As it can be expected with best practices, there are, however, some times when it's easier to use temporary variables. For example, if you need to store a return value that will be used just after in a short function so that you can keep the line length comfortable, don't hesitate to do so. The only thing that should be strictly forbidden is to use the same temporary variables to store various different bits of information.

Smaller functions

We already mentioned that functions are like building blocks. Usually, you want your building blocks to be versatile and sturdy. Both those properties are better enforced if you write small functions that only focus on doing one thing well.

If your function does too much, it is difficult to reuse. We will look at composing functions in the next chapter and how you can leverage all your small utility functions to create new ones with bigger reaches.

Also, it is easier to read smaller pieces of code and reason about them. The implications are simpler to understand and there are usually fewer edge cases, making the function easier to test.

Parameter order matters

Choosing the order of parameters of your functions does not seems like much, but in fact it matters a lot. Higher-order functions are a core feature of functional programming; this means that you will be passing a lot of functions around.

Those functions could be anonymous, in which case you might want to avoid having a function declaration as the middle parameter, for readability reasons. Optional parameters are also constrained to the end of the signature in PHP. As we will see, some functional constructs take functions that can have default values.

We will also dwell on this topic further in Chapter 4, Compositing Functions. When you chain multiple functions together, the first parameter of each is the return value of the previous one. This means you will have to take special care when choosing which parameters go first.

The map function

The map, or array_map method in PHP, is a higher-order function that applies a given callback to all elements of a collection. The return value is a collection in the same order. A simple example is:

<?php

function square(int $x): int

{

return $x * $x;

}

$squared = array_map('square', [1, 2, 3, 4]);

// $squared contains [1, 4, 9, 16]

We create a function that computes the square of the given integer and then use the array_map function to compute all the square values of a given array. The first parameter of the array_map function is any form of callable and the second parameter has to be a real array. You cannot pass an Iterator or an instance of Traversable.

You can also pass multiple arrays. Your callback will receive a value from each array:

<?php

$numbers = [1, 2, 3, 4];

$english = ['one', 'two', 'three', 'four'];

$french = ['un', 'deux', 'trois', 'quatre'];

function translate(int $n, string $e, string $f): string

{

return "$n is $e, or $f in French.";

}

print_r(array_map('translate', $numbers, $english, $french));

This code will display:

Array

(

[0] => 1 is one, or un in French.

[1] => 2 is two, or deux in French.

[2] => 3 is three, or trois in French.

[3] => 4 is four, or quatre in French.

)

The longest array will determine the length of the result. Shorter arrays will be expanded with the null value so that they all have matching lengths.

If you pass null as a function, PHP will merge the arrays:

<?php

print_r(array_map(null, [1, 2], ['one', 'two'], ['un', 'deux']));

And the result:

Array

(

[0] => Array

(

[0] => 1

[1] => one

[2] => un

)

[1] => Array

(

[0] => 2

[1] => two

[2] => deux

)

)

If you pass only one array, the keys will be preserved; but if you pass multiple arrays, they will be lost:

<?php

function add(int $a, int $b = 10): int

{

return $a + $b;

}

print_r(array_map('add', ['one' => 1, 'two' => 2]));

print_r(array_map('add', [1, 2], [20, 30]));

And the result:

Array

(

[one] => 11

[two] => 12

)

Array

(

[0] => 21

[1] => 32

)

As a final note, it is sadly impossible to access the key of each item easily. Your callable can, however, be a closure so you are able to use any variable accessible from your context. Using this, you can map over the keys of your array and use a closure to retrieve the values like that:

$data = ['one' => 1, 'two' => 2];

array_map(function to_string($key) use($data) {

return (str) $data[$key];

},

array_keys($data);

The filter function

The filter, or array_filter method in PHP, is a higher-order function that keeps only certain elements of a collection, based on a Boolean predicate. The return value is a collection that will only contain elements returning true for the predicate function. A simple example is:

<?php

function odd(int $a): bool

{

return $a % 2 === 1;

}

$filtered = array_filter([1, 2, 3, 4, 5, 6], 'odd');

/* $filtered contains [1, 3, 5] */

We first create a function that takes a value and returns a Boolean. This function will be our predicate. In our case, we check whether an integer is an odd number. As with the array_map method, the predicate can be anything that is a callable and the collection must be an array. Be aware, however, that the parameter order is reversed; the collection comes first.

The callback is optional; if you don't give one, all elements which PHP will evaluate to false, like empty strings and arrays for example, will be filtered out:

<?php

$filtered = array_filter(["one", "two", "", "three", ""]);

/* $filtered contains ["one", "two", "three"] */

$filtered = array_filter([0, 1, null, 2, [], 3, 0.0]);

/* $filtered contains [1, 2, 3] */

You can also pass a third parameter that acts as a flag to determine whether you want to receive the key instead of the value, or both:

<?php

$data = [];

function key_only($key) {

// [...]

}

$filtered = array_filter($data, 'key_only', ARRAY_FILTER_USE_KEY);

function both($value, $key) {

// [...]

}

$filtered = array_filter($data, 'both', ARRAY_FILTER_USE_BOTH);

The fold or reduce function

Folding refers to a process where you reduce a collection to a return value using a combining function. Depending on the language, this operation can have multiple names like fold, reduce, accumulate, aggregate, or compress. As with other functions related to arrays, the PHP version is the array_reduce function.

You may be familiar with the array_sum function, which calculates the sum of all the values in an array. This is, in fact, a fold and can be easily written using the array_reduce function:

<?php

function sum(int $carry, int $i): int

{

return $carry + $i;

}

$summed = array_reduce([1, 2, 3, 4], 'sum', 0);

/* $summed contains 10 */

Like the array_filter method, the collection comes first; you then pass a callback and finally an optional initial value. In our case, we were forced to pass the initial value 0 because the default null is an invalid type for our function signature of int type.

The callback function has two parameters. The first one is the current reduced value based on all previous items, sometimes called carry or accumulator. The second one is the array element currently being processed. On the first iteration, the carry is equal to the initial value.

You don't necessarily need to use the elements themselves to produce a value. You could, for example, implement a naive replacement for in_array using fold:

<?php

function in_array2(string $needle, array $haystack): bool

{

$search = function(bool $contains, string $item) use ($needle):bool

{

return $needle == $item ? true : $contains;

};

return array_reduce($haystack, $search, false);

}

var_dump(in_array2('two', ['one', 'two', 'three']));

// bool(true)

The reduce operation starts with the initial value false because we assume that the array does not contain our needle. This also allows us to nicely manage the case where we have an empty array.

Upon each item, if the item is the one we are searching for, we return true, which will be the new value passed around. If it does not match, we simply return the current value of the accumulator, which will be either true if we found the item earlier, or false if we did not.

Our implementation will probably be a tad slower than the official one because, no matter what, we have to iterate over the entire array before returning a result instead of being able to exit the function as soon as we encounter the searched item.

We could, however, implement an alternative to the max function where performances should be on par, because any implementation will have to iterate over all values:

<?php

function max2(array $data): int

{

return array_reduce($data, function(int $max, int $i) : int

{

return $i > $max ? $i : $max;

}, 0);

}

echo max2([5, 10, 23, 1, 0]);

// 23

The idea is the same as before, although using numbers instead of a Boolean value. We start with the initial 0, our current maximum. If we encounter a bigger value, we return it so that it gets passed around. Otherwise, we keep returning our current accumulator, already containing the biggest value encountered so far.

As the max PHP functions works on both arrays and numbers, we could reuse it for our reducing. This would, however, bring nothing, as the original function can already operate directly on arrays:

<?php

function max3(array $data): int

{

return array_reduce($data, 'max', 0);

}

Just to be clear, I don't recommend using those in production. The functions already in the language are better. Those are just for educational purposes to demonstrate the various possibilities of folding.

Also, I totally understand if those short examples do not seem better than a foreach loop, or any other more imperative approach, to implement those two functions. They have, however, a few advantages:

· If you are using PHP 7 scalar type hinting, the types are enforced for each item, making your software more robust. You can verify that by putting a string in the array used for the max2 method.

· You can unit test the function that you are passing to the array_reduce method, or the array_map and array_filter functions for that matter, to ensure its correctness.

· You could distribute the reducing of a big array between multiple threads or network nodes if you have such an architecture. This would be a lot harder with a foreach loop.

· As shown with the max3 function, this approach allows you to reuse existing methods instead of writing custom loops to manipulate data.

The map and filter functions using fold

For now, our fold only returned simple scalar values. But nothing prevents us from building more complex data structures. For example, we can implement the map and filter functions using fold:

<?php

function map(array $data, callable $cb): array

{

return array_reduce($data, function(array $acc, $i) use ($cb) {

$acc[] = $cb($i);

return $acc;

}, []);

}

function filter(array $data, callable $predicate): array

{

return array_reduce($data, function(array $acc, $i) use($predicate) {

if($predicate($i)) {

$acc[] = $i;

}

return $acc;

}, []);

}

Again, those are mostly for the purposes of demonstrating that it is possible to return arrays with folding. The native functions are enough if you don't need to manipulate more complex collections.

As an exercise for the reader, try to implement the map_filter or the filter_map function if you prefer, and the array_reverse function. You can also try writing head and tail methods, which return the first, respectively last, element of an array and are often found in functional languages.

As you can see, folding is really powerful and the idea behind it is central to a lot of functional techniques. It is why I largely prefer to talk about fold rather than reduce, which I find a bit reductive, pun intended.

Before going further, make sure you understand how fold works, as it will make everything else much easier.

Folding left and right

Functional languages often implement two versions of fold, foldl and foldr. The difference is that the first folds from the left and the second from the right.

For example, if you have the array [1, 2, 3, 4, 5] and you want to compute its sum, you can have either (((1 + 2) + 3) + 4) + 5 or (((5 + 4) + 3) + 2) + 1. If you have an initial value, it will always be the first value used in the computation.

If the operation you are applying to the values is commutative, both the left and right variants will produce the same results. The notion of commutative operation comes from mathematics and is explained in Chapter 7, Functional Techniques and Topics.

For languages allowing infinite lists, such as Haskell, depending on how the list is generated, one of the two folds could be able to compute a value and stop. Also, if the language implements tail call elimination, a topic that we will discuss in Chapter 7, Functional Techniques and Topics, choosing the right side to start the fold might avoid a stack overflow and allow the operation to finish.

As neither infinite list or tail call elimination is performed by PHP, there is, in my opinion, no reason to bother with the distinction. If you are interested, the array_reduce function folds from the left and implementing a function that does the same from the right should not be too complicated.

The MapReduce model

You may have already heard the name the MapReduce programming model. At first, it referred to a proprietary technology developed by Google but nowadays there are multiple implementations in a variety of languages.

Although the ideas behind MapReduce are inspired by the map and reduce functions we just discussed, the concept is broader. It describes a whole model to process large datasets using parallel and distributed algorithms on a cluster.

Every technique you learn in this book could help you when implementing a MapReduce to analyze data. However, the topic is out of scope, so if you want to learn more, you can start with the Wikipedia page by visiting https://en.wikipedia.org/wiki/MapReduce.

Convolution or zip

Convolution, or more often zip is the process of combining each nth element of all given arrays. In fact, this is exactly what we did by passing null value to the array_map function before:

<?php

print_r(array_map(null, [1, 2], ['one', 'two'], ['un', 'deux']));

And the output:

Array

(

[0] => Array

(

[0] => 1

[1] => one

[2] => un

)

[1] => Array

(

[0] => 2

[1] => two

[2] => deux

)

)

It is important to note that if the arrays are of different lengths, PHP will use null as the padding value:

<?php

$numerals = [1, 2, 3, 4];

$english = ['one', 'two'];

$french = ['un', 'deux', 'trois'];

print_r(array_map(null, $numerals, $english, $french));

Array

(

[0] => Array

(

[0] => 1

[1] => one

[2] => un

)

[1] => Array

(

[0] => 2

[1] => two

[2] => deux

)

[2] => Array

(

[0] => 3

[1] =>

[2] => trois

)

[3] => Array

(

[0] => 4

[1] =>

[2] =>

)

)

Be aware that in most programming languages, including Haskell, Scala, and Python, the zip operation will, however, stop at the shortest array without padding any values. You can try to implement a similar function in PHP using, for example, the array_slice function to reduce all arrays to the same size before calling the array_merge function.

We can also perform the inverse operation by creating multiple arrays from an array of arrays. This process is sometimes called unzip. Here is a naive implementation which is missing a lot of checks to make it robust enough for production use:

<?php

function unzip(array $data): array

{

$return = [];

$data = array_values($data);

$size = count($data[0]);

foreach($data as $child) {

$child = array_values($child);

for($i = 0; $i < $size; ++$i) {

if(isset($child[$i]) && $child[$i] !== null) {

$return[$i][] = $child[$i];

}

}

}

return $return;

}

You could use it like this:

$zipped = array_map(null, $numerals, $english, $french);

list($numerals2, $english2, $french2) = unzip($zipped);

var_dump($numerals == $numerals2);

// bool(true)

var_dump($english == $english2);

// bool(true)

var_dump($french == $french2);

// bool(true)

Recursion

In the academic sense, recursion is the idea of dividing a problem into smaller instances of the same problem. For example, if you need to scan a directory recursively, you first scan the starting directory and then scan its children and the children's children. Most programming languages support recursion by allowing a function to call itself. This idea is often what is described as recursion.

Let's see how we can scan a directory by using recursion:

<?php

function searchDirectory($dir, $accumulator = []) {

foreach (scandir($dir) as $path) {

// Ignore hidden files, current directory and parent directory

if(strpos($path, '.') === 0) {

continue;

}

$fullPath = $dir.DIRECTORY_SEPARATOR.$path;

if(is_dir($fullPath)) {

$accumulator = searchDirectory($path, $accumulator);

} else {

$accumulator[] = $fullPath;

}

}

return $accumulator;

}

We start by using the scandir function to obtain all files and directories. Then, if we encounter a child directory, we call the function on it again. Otherwise, we simply add the file to the accumulator. This function is recursive because it calls itself.

You could write this using control structures, but as you don't know in advance what the depth of your folder hierarchy is, the code will probably be a lot messier and harder to understand.

Some books and tutorials use the Fibonacci sequence, or computing a factorial as recursion examples but, to be fair, those are quite poor, as they are better implemented using a traditional for loop for the second, and compute terms in advance for the first.

Instead, let's wrap our heads around a more interesting challenge, the Hanoi Towers. For those unaware of this game, the traditional version features three rods with discs of different sizes stacked in top of one another, the smallest on the top. At the beginning of the game, all discs are on the leftmost rod and the goal is to bring them to the rightmost one. The game obeys the following rules:

· Only one disc can move at a time

· Only the topmost disc of a rod can be moved

· A disc cannot be placed on top of a smaller disc

The setup for this game looks like the following:

Recursion

If we want to solve the game, the larger disc must be placed first on the last rod. In order to do that, we need to move all other discs to the middle rod first. Following this line of reasoning, we can draw three big steps that we must achieve:

1. Move all discs but the bigger one to the middle.

2. Move the large disc to the right.

3. Move all discs on top of the large one.

Steps 1 and 3 are smaller versions of the initial problem. Each of those steps can, in turn, be reduced to a smaller version until we have only one disc to move-the perfect situation for a recursive function. Let's try implementing that.

To avoid cluttering our function with variables related to the rods and discs, we will assume the computer will give orders to someone making the moves. In our code, we will also assume the largest disc is number 1, smaller discs having larger numbers:

<?php

function hanoi(int $disc, string $source, string $destination, string $via)

{

if ($disc === 1) {

echo("Move a disc from the $source rod to the $destination rod\n");

} else {

// step 1 : move all discs but the first to the "via" rod hanoi($disc - 1, $source, $via, $destination);

// step 2 : move the last disc to the destination

hanoi(1, $source, $destination, $via);

// step 3 : move the discs from the "via" rod to the destination

hanoi($disc - 1, $via, $destination, $source);

}

}

On using the hanoi(3, 'left', 'right', 'middle') input for the three discs, we get the following output:

Move a disc from the left rod to the right rod

Move a disc from the left rod to the middle rod

Move a disc from the right rod to the middle rod

Move a disc from the left rod to the right rod

Move a disc from the middle rod to the left rod

Move a disc from the middle rod to the right rod

Move a disc from the left rod to the right rod

It takes a while to think in terms of recursion instead of using a more traditional loop, and obviously recursion is not a silver bullet that is better for all problems you are trying to solve.

Some functional languages have no loop structures at all, forcing you to use recursion. This is not the case with PHP, so let's use the right tool for the job. If you can think of the problem as a combination of smaller similar issues, usually it will be easy to use recursion. For example, trying to find an iterative solution to the Towers of Hanoi requires a lot of careful thinking. Or you could try to rewrite the directory scanning function using only loops to convince yourself.

Some other areas where recursion is useful are:

· Generating the data structure for a menu with multiple levels

· Traversing an XML document

· Rendering a series of CMS components that could contain child components

A good rule of thumb is to try recursion when your data has a tree-like structure with a root node and children.

Although often easier to read, once you have gotten to grip with it, recursion comes with a memory cost. In most applications, you should not encounter any difficulties, but we will discuss the topic further in Chapter 10, PHP Frameworks and FP, and present some methods to avoid those issues.

Recursion and loops

Some functional languages, such as Haskell, do not have any loop structure. This means the only way to iterate over a data structure is to use recursion. Although it is discouraged in the functional world to use a for loop due to all issues that arise when you can modify the loop index, there are no real dangers to using a foreach loop, for example.

For the sake of completeness, here are some ways you can replace a loop with a recursive call if you want to try it or need to understand code written in another language without a loop construct.

Replace a while loop:

<?php

function while_iterative()

{

$result = 1;

while($result < 50) {

$result = $result * 2;

}

return $result;

}

function while_recursive($result = 1, $continue = true)

{

if($continue === false) {

return $result;

}

return while_recursive($result * 2, $result < 50);

}

Or a for loop:

<?php

function for_iterative()

{

$result = 5;

for($i = 1; $i < 10; ++$i) {

$result = $result * $i;

}

return $result;

}

function for_recursive($result = 5, $i = 1)

{

if($i >= 10) {

return $result;

}

return for_recursive($result * $i, $i + 1);

}

As you can see, the trick is to use function parameters to pass the current state of the loop to the next recursion. In the case of a while loop, you pass the result of the condition and when you emulate a for loop, you pass the loop counter. Obviously, the current state of computation must also always be passed around.

Usually, the recursion itself is done in a helper function to avoid cluttering the signature with optional parameters used to perform the loop. In order to keep the global namespace clean, this helper is declared inside the original function. Here is an example:

<?php

function for_with_helper()

{

$helper = function($result = 5, $i = 1) use(&$helper) {

if($i >= 10) {

return $result;

}

return $helper($result * $i, $i + 1);

};

return $helper();

}

Notice how you need to pass the variable containing the function by reference with the use keyword. This is due to a fact we already discussed. The variable passed to the closure is bound at the declaration time, but when the function is declared, the assignment has not happened yet and the variable is empty. However, if we pass the variable by reference, it will be updated once the assignment is complete and we will be able to use it as a callback inside the anonymous function.

Exceptions

Error management is one of the toughest problems you face when writing software. It is often difficult to decide which piece of code should treat the error. Do it in the low-level function and you might not have access to the facilities to display an error message or enough context to decide the best course of action. Do it higher up and this might cause havoc in your data or put the application into an unrecoverable state.

The usual way to manage errors in OOP codebases is to use exceptions. You throw an exception in your library or utility code and you catch it whenever you are ready to manage it as you want.

Whether exception throwing and catching can be considered side effects or side causes is a matter for debate even among academics. There's a variety of points of view. I don't want to bore you with rhetorical arguments, so let's stick to some points nearly everyone agrees upon:

· An exception thrown by any external source (database access, filesystem errors, unavailable external resource, invalid user input, and so on) is inherently impure because accessing those sources is already a side cause.

· An exception thrown due to a logical error (index out of bound, invalid types or data, and so on) is, usually, considered pure as it can be considered a valid return value for the function. The exception must, however, be clearly documented as a possible outcome.

· Catching an exception breaks referential transparency and thus makes any function with a catch block impure.

The first two statements should be fairly easy to understand, but what about the third one? Let us start our demonstration with a short piece of code:

<?php

function throw_exception()

{

throw new Exception('Message');

}

function some_function($x)

{

$y = throw_exception();

try {

$z = $x + $y;

} catch(Exception $e) {

$z = 42;

}

return $z;

}

echo some_function(42);

// PHP Warning: Uncaught Exception: Message

It's easy to see that our call to the some_function function will result in an uncaught exception because the call to the throw_exception function is outside the try ... catch block. Now, if we apply the principles of referential transparency, we should be able to replace the $y parameter in the addition by its value. Let's try that:

<?php

try {

$z = $x + throw_exception();

} catch(Exception $e) {

$z = 42;

}

What is the value of the $z parameter now and what will our function return? Contrary to before, we will now have a return value of 42, clearly changing the outcome of calling our function. By simply trying to apply equation reasoning, we just proved that catching an exception can break referential transparency.

What good are exceptions if you cannot catch them? Not much; this is why we will refrain from using them throughout the book. You could, however, consider them as a side effect and then apply the techniques we will see in Chapter 6, Real-Life Monads, to manage them. Haskell, for example, allows throwing exceptions as long as they are caught using the IO Monad.

Another issue is cognitive burden. As soon as you use them, you cannot know for sure when they will be caught; they might even be displayed directly to the end user. This breaks the ability to reason about a piece of code on its own as you now have to think of what will happen higher up.

This issue is usually why you hear advice such as Use exceptions for errors only, not flow control. This way, you can at least be sure that your exception will be used to display some kind of error instead of wondering in which state you put the application.

PHP 7 and exceptions

Even if we are discussing exceptions mostly in a negative light, let me take this opportunity to present the improvements that have been made in the new PHP version concerning the topic.

Before, some type of errors would generate fatal errors or errors which would stop the execution of the script and display an error message. You were able to use the set_error_handler exception to define a custom handler for non-fatal errors and eventually continue execution.

PHP 7.0 introduces a Throwable interface, which is a new parent for the exception. The Throwable class also a new child called the Error class, which you can use to catch most of the errors that you weren't able to manage before. There are still some errors, such as parsing errors, which you can obviously not catch, as it means your whole PHP file is somehow invalid.

Let's demonstrate this with a piece of code that tries to call a non-existing method on an object:

<?php

class A {}

$a = new A();

$a->invalid_method();

// PHP Warning: Uncaught Error: Call to undefined method A::invalid_method()

If you are using PHP 5.6 or lower, the message will say something along the lines of:

Fatal error: Call to undefined method A::invalid_method()

Using PHP 7.0, however, the message will be (emphasis is mine):

Fatal error: Uncaught Error: Call to undefined method A::invalid_method()

The difference being that PHP informs you that this is an uncaught error. This means you can now catch it using the usual try ... catch syntax. You can catch the Error class directly, or if you want to be broader and catch any possible exception, you can use the Throwable interface. However, I discourage this as you will lose the information about which error you have exactly:

<?php class B {}

$a = new B();

try {

$a->invalid_method();

} catch(Error $e) {

echo "An error occured : ".$e->getMessage();

}

// An error occured : Call to undefined method B::invalid_method()

Also interesting for us, the TypeError parameter is a child of the Error class which is raised when a function is called with parameters of the wrong type or the return type is wrong:

<?php

function add(int $a, int $b): int

{

return $a + $b;

}

try {

add(10, 'foo');

} catch(TypeError $e) {

echo "An error occured : ".$e->getMessage();

}

// An error occured : Argument 2 passed to add() must be of the type integer, string given

For those wondering why a new interface was created alongside the new Error class, it is mostly for two reasons:

· To clearly separate the Exception interface from what were internal engine errors before

· To avoid breaking existing code catching the Exception interface, letting the developer choose whether they want to also start catching errors or not

Alternatives to exceptions

As we just saw, we cannot use exceptions if we want to keep our code pure. What are our options for making sure we can signify an error to the caller of our function? We want our solution to have the following features:

· Enforce error management so that no errors can bubble up to the end user

· Avoid boilerplate or complex code structure

· Advertised in the signature of our function

· Avoid any risk of mistaking the error for a correct result

Before presenting a solution possessing all those benefits in the next section of the chapter, let's have a look at various ways error management is done in imperative languages.

In order to test the various ways, we will try to implement the max function we already used a bit earlier:

<?php

function max2(array $data): int

{

return array_reduce($data, function(int $max, int $i) : int {

return $i > $max ? $i : $max;

}, 0);

}

Because we chose the initial value 0, if we call the function with an empty array, we will get the result 0. Is 0 really the maximal value of an empty array? What happens if we call the version bundled with PHP, the max([]) method?

Warning: max(): Array must contain at least one element

Also, the value false is returned. Our version uses the value 0 as a default value, and we could consider false to be an error code. The PHP version also greets you with a warning.

Now that we have a function we can improve, let us try the various options we have at our disposal. We will go from the worst to the best one.

Logging/displaying error message

As we just saw, PHP can display a warning message. We could also go with a message of level notice or error. This is probably the worst you could do because there is no way for the caller of your function to know something went wrong. Messages will only be displayed in the logs or on the screen once your application is run.

Also, in some cases, an error is something you can recuperate from. Since you have no idea something happened, you cannot do that in this case.

To make matters worse, PHP allows you to configure which error level gets displayed. In most cases, notices are just hidden, so no one will ever see that an error happened somewhere in the application.

To be fair to PHP, there is a way to catch those warnings and notices at runtime using a custom error handler declared with the set_error_handler parameter. However, in order to manage errors correctly, you will have to find a way to determine inside the handler which is the function that generated the error and act accordingly.

If you have multiple functions using these kinds of messages to signal errors, you will soon have either a really big error handler, or a multitude of smaller ones, making the whole process error prone and really cumbersome.

Error codes

Error codes are a heritage from the C language, which does not have any concept of exception. The idea is that a function always returns a code to signify the status of the computation and some other way is found to pass the return value around. Usually, the code 0 means that all went well, and anything else is an error.

When it comes to numerical error codes, PHP has no function using them as return value as far as I can tell. The language has, however, a lot of functions returning the false value when an error occurred instead of the expected value. Only having one potential value to denote failure can lead to difficulties in transmitting information about what happened. For example, the documentation of the move_uploaded_filestates that:

Returns TRUE on success.

If filename is not a valid upload file, then no action will occur, and move_uploaded_file() will return False.

If filename is a valid upload file, but cannot be moved for some reason, no action will occur, and move_uploaded_file() will return False. Additionally, a warning will be issued.

This means you will be informed when you have an error, but you are unable to know which category of error it is without resorting to reading the error message. And even then, you will lack important information, such as why the uploaded file is invalid, for example.

If we wanted to better mimic the max function of PHP, we could do it like this:

<?php

function max3(array $data)

{

if(empty($data)) {

trigger_error('max3(): Array must contain at least one element', E_USER_WARNING);

return false;

}

return array_reduce($data, function(int $max, int $i) : int {

return $i > $max ? $i : $max;

}, 0);

}

Since now our function needs to return the false value in case of error, we've had to remove the type hint for the return value, thus making our signature a bit less self-documenting.

Other functions, usually those that wrap an external library, also return the false value, in case of error but have companion functions in the form X_errno and X_error that return more information about the error of the last function that was executed. A few examples would be the curl_exec, curl_errno, and curl_error functions.

Such helpers allow for more fine-grained error handling but come with the cognitive cost that you must think about them. Error management is not enforced. To further my point, let us note that even the example for the curl_exec function in the official documentation does not set the best practice of checking the return value:

<?php

/* create a new cURL resource */

$ch = curl_init();

/* set URL and other appropriate options */

curl_setopt($ch, CURLOPT_URL, "http://www.example.com/");

curl_setopt($ch, CURLOPT_HEADER, 0);

/* grab URL and pass it to the browser */

curl_exec($ch);

/* close cURL resource, and free up system resources */

curl_close($ch);

Using the false value as a marker for failure also has another consequence in a language performing loose type casting like PHP. As stated in the aforementioned documentation, if you don't perform strict equality comparison, you risk considering a valid return value that evaluates as false as an error:

Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to False. Please read the section on Boolean values for more information. Use the === operator for testing the return value of this function.

PHP uses the false error code only in the case of errors but does not return true or 0 as is usually the case in C. You don't have to find a way to transmit the return value to the user.

But if you want to implement your own function using a numerical error code to have the possibility of categorizing the error, you have to find a way to return both the code and the value. Usually, you can use one of two options:

· Using a parameter passed by reference which will hold the result; the preg_match parameter does that, for example, even if it is for different reasons. This is not strictly against function purity as long as the parameter is clearly identified as a return value.

· Returning an array or some other data structure that can hold two or more values. This idea is the beginning of what we will present as our functional solution in the next section.

Default value/null

A tad better than error codes when it comes to cognitive burden is the default value. If your function has only a reduced set of input that could result in an error, or if the error reason is not important, you could imagine returning a default value instead of specifying the error reason via an error code.

This will, however, open a new can of worms. It is not always easy to determine what a good default value is and, in some cases, your default value will also be a valid value, making it impossible to determine whether there was an error or not. For example, if you get 0 as a result when calling our max2 function, you cannot know whether the array is empty or contains only the value 0 and negative numbers.

The default value could also depend on the context, in which case you will have to add a parameter to your function so that you can also specify the default value when calling it. Besides making the function signature bigger, this also defeats some performance optimization we will learn later on and, although being totally pure and referentially transparent, increases the cognitive burden.

Let's add a default value parameter to our max function:

<?php

function max4(array $data, int $default = 0): int

{

return empty($data) ? $default :

array_reduce($data, function(int $max, int $i) : int

{

return $i > $max ? $i : $max;

}, 0);

}

As we enforce the type of the default, we are able to restore the type hint for the return value. If you would like to pass anything as a default value, you will also have to remove the type hint.

To avoid some of the discussed issues, the value null is sometimes used as a default return value. Although not really a value, null is not categorized in the error code category as it is a perfectly valid value in some cases. Say you are searching for an item in a collection, what do you return if you find nothing?

Using the null value as a possible return value has, however, two issues:

· You cannot use a return type hint as null will not be considered of the correct type. Also, if you plan to use the value as a parameter, it also cannot be type hinted or it must be optional with the value null as a default value. This forces you to either remove your type hints or make your parameters optional.

· If your function usually returns objects, you will have to check the value for null, otherwise you risk what Tony Hoare called The Billion Dollar Mistake, a null pointer reference. Or, as it is reported in PHP, Call to a member function XXX() on null.

For the anecdote, Tony Hoare is the one that introduced null value to the world back in 1965, because it was so easy to implement. Later on, he strongly regretted this decision and decided it was his billion dollar mistake. If you want to learn more about the reasons, I invite you to watch this talk he gave at https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare.

Error handler

The last method is used a lot in the JavaScript world, where callbacks are everywhere. The idea is to pass an error callback each time the function is called. It can be even more powerful if you allow the caller to pass multiple callbacks, one for each kind of error that can arise.

Although it alleviates some of the issues that the default value has, such as the possibility to mix up a valid value with the default one, you still need to pass different callbacks depending on the context, making this solution only marginally better.

How will this approach look for our function? Consider the following implementation:

<?php

function max5(array $data, callable $onError): int

{

return empty($data) ? $onError() :

array_reduce($data, function(int $max, int $i) : int {

return $i > $max ? $i : $max;

}, 0);

}

max5([], function(): int {

// You are free to do anything you want here.

// Not really useful in such a simple case but

// when creating complex objects it can prove invaluable.

return 42;

});

Again, we kept the return type hint because the contract we have with our caller is to return an integer value. As stated in the comment, in this particular case, the default value as parameter will probably suffice, but in more complex situations, this approach provides more power.

We could also imagine passing the initial parameters to the callback along with information about the failure so that the error handler can act accordingly. In a way, this approach is a bit like the combination of everything we've seen earlier, as it allows you to:

· Specify a default return value of your choice

· Display or log any kind of error message you want

· Return a more complex data structure with an error code if you so wish

The Option/Maybe and Either types

As hinted before, our solution is to use a return type that contains the wanted value or something else in case of error. Those kinds of data structures are called union types. A union can contain values of different types, but only one at a time.

Let's start with the easiest of both union types we will see in this chapter. As always, naming is a difficult thing in computer science and people came up with different names to designate mostly the same structure:

· Haskell calls it the Maybe type, as does Idris

· Scala calls it the Option type, as does OCaml, Rust, and ML

· Since version 8, Java has an Optional type, as does Swift and the next specification of C++

Personally, I prefer the denomination Maybe as I consider an option to be something else. The remainder of the book will thus use this, except when a specific library has a type called Option.

The Maybe type is special in the sense that it can either hold a value of a particular type or the equivalent of nothing, or if you prefer, the null value. In Haskell, those two possible values are called Just and Nothing. In Scala, it is Some and None because Nothing is already used to designate the type equivalent of the value null.

Libraries implementing only a Maybe or Option type exist for PHP, and some of the libraries presented later in this chapter also ship with such types. But for the sake of correctly understanding how they work and their power, we will implement our own.

Let us reiterate our goals first:

· Enforce error management so that no errors can bubble up to the end user

· Avoid boilerplate or complex code structure

· Advertised in the signature of our function

· Avoid any risk of mistaking the error for a correct result

If you type hint your function return value using the type that we will create in a few moments, you are taking care of our third goal. The presence of two distinct possibilities, the Just and Nothing values, ensure that you cannot mistake a valid result for an error. To make sure we don't end up with an erroneous value somewhere along the line, we must ensure that we cannot get a value from our new type without specifying a default if it is the Nothing value. And, concerning our second goal, we will see if we can write something nice:

<?php

abstract class Maybe

{

public static function just($value): Just

{

return new Just($value);

}

public static function nothing(): Nothing

{

return Nothing::get();

}

abstract public function isJust(): bool;

abstract public function isNothing(): bool;

abstract public function getOrElse($default);

}

Our class has two static helper methods to create two instances of our soon-to-come child classes representing our two possible states. The Nothing value will be implemented as a singleton for performance reasons; since it will never hold any values, it is safe to do it this way.

The most important part of our class is an abstract getOrElse function, which will force anyone wanting to get a value to also pass a default that will get returned if we have none. This way, we can enforce that a valid value will be returned even in the case of error. Obviously, you could pass the value null as the default, since PHP has no mechanism to enforce something else, but this would be akin to shooting yourself in the foot:

<?php

final class Just extends Maybe

{

private $value;

public function __construct($value)

{

$this->value = $value;

}

public function isJust(): bool

{

return true;

}

public function isNothing(): bool

{

return false;

}

public function getOrElse($default)

{

return $this->value;

}

}

Our Just class is pretty simple; a constructor and a getter:

<?php

final class Nothing extends Maybe

{

private static $instance = null;

public static function get()

{

if(is_null(self::$instance)) {

self::$instance = new static();

}

return self::$instance;

}

public function isJust(): bool

{

return false;

}

public function isNothing(): bool

{

return true;

}

public function getOrElse($default)

{

return $default;

}

}

If you don't take the part about being a singleton into account, the Nothing class is even simpler because the getOrElse function will always return the default value no matter what. For those wondering, it is a deliberate choice to keep the constructor public. It has absolutely no consequences if someone wants to create a Nothing instance directly, so why bother?

Let's test our new Maybe type:

<?php

$hello = Maybe::just("Hello World !");

$nothing = Maybe::nothing();

echo $hello->getOrElse("Nothing to see...");

// Hello World !

var_dump($hello->isJust());

// bool(true)

var_dump($hello->isNothing());

// bool(false)

echo $nothing->getOrElse("Nothing to see...");

// Nothing to see...

var_dump($nothing->isJust());

// bool(false)

var_dump($nothing->isNothing());

// bool(true)

Everything seems to be working great. The need for boilerplate can be improved though. At this point, every time you want to instantiate a new Maybe type, you need to check the value you have and choose between the Some and Nothing values.

Also, it might happen that you need to apply some functions to the value before passing it further without knowing at this point what default value is best. As it would be cumbersome to get the value with some temporary default before creating a new Maybe type right behind, let's try to fix this aspect as well:

<?php

abstract class Maybe

{

// [...]

public static function fromValue($value, $nullValue = null)

{

return $value === $nullValue ?

self::nothing() :

self::just($value);

}

abstract public function map(callable $f): Maybe;

}

final class Just extends Maybe

{

// [...]

public function map(callable $f): Maybe

{

return new self($f($this->value));

}

}

final class Nothing extends Maybe

{

// [...]

public function map(callable $f): Maybe

{

return $this;

}

}

In order to have a somewhat coherent naming for utility methods, we use the same name as for functions working with collections. In a way, you can consider a Maybe type like a list with either one or no value. Let's add some other utility methods based on the same assumption:

<?php abstract class Maybe

{

// [...]

abstract public function orElse(Maybe $m): Maybe;

abstract public function flatMap(callable $f): Maybe;

abstract public function filter(callable $f): Maybe;

}

final class Just extends Maybe

{

// [...]

public function orElse(Maybe $m): Maybe

{

return $this;

}

public function flatMap(callable $f): Maybe

{

return $f($this->value);

}

public function filter(callable $f): Maybe

{

return $f($this->value) ? $this : Maybe::nothing();

}

}

final class Nothing extends Maybe

{

// [...]

public function orElse(Maybe $m): Maybe

{

return $m;

}

public function flatMap(callable $f): Maybe

{

return $this;

}

public function filter(callable $f): Maybe

{

return $this;

}

}

We have added three new methods to our implementation:

· The orElse method returns the current value if there is one, or the given value if it was Nothing. This allows us to easily get data from multiple possible sources.

· The flatMap method applies a callable to our value but does not wrap it inside a Maybe class. It is the responsibility of the callable to return a Maybe class itself.

· The filter method applies the given predicate to the value. If the predicate returns true value, we keep the value; otherwise, we return the value Nothing.

Now that we have implemented a working Maybe type, let's see how we can use it to get rid of error and null management easily. Imagine we want to display information about the connected user in the upper-right corner of our application. Without a Maybe type, you do something like the following:

<?php

$user = getCurrentUser();

$name = $user == null ? 'Guest' : $user->name;

echo sprintf("Welcome %s", $name);

// Welcome John

Here, we only use the name, so we can limit ourselves to one null check. If we need more information from the user, the usual method is to use a pattern that is sometimes called the Null object pattern. In our case, our Null object will be an instance of AnonymousUser method:

<?php

$user = getCurrentUser();

if($user == null) {

$user = new AnonymousUser();

}

echo sprintf("Welcome %s", $user->name);

// Welcome John

Now let's try to do the same with our Maybe type:

<?php

$user = Maybe::fromValue(getCurrentUser());

$name = $user->map(function(User $u) {

return $u->name;

})->getOrElse('Guest');

echo sprintf("Welcome %s", $name);

// Welcome John

echo sprintf("Welcome %s", $user->getOrElse(new AnonymousUser())->name);

// Welcome John

The first version might not be much better, as we have had to create a new function to extract the name. But let's keep in mind that you could do any number of treatments on your object before needing to extract a final value. Also, most functional libraries we present later provide helper methods to get value from objects in a simpler way.

You can also easily call a chain of methods until one of them returns a value. Say you want to display a dashboard, but those can be redefined on a per-group and per-level basis. Let's compare how our two methods fare.

First, the null value check approach:

<?php

$dashboard = getUserDashboard();

if($dashboard == null) {

$dashboard = getGroupDashboard();

}

if($dashboard == null) {

$dashboard = getDashboard();

}

And now, using Maybe type:

<?php

/* We assume the dashboards method now return Maybe instances */

$dashboard = getUserDashboard()

->orElse(getGroupDashboard())

->orElse(getDashboard());

I think the more readable one is easier to determine!

Finally, let us demonstrate a little example on how we could chain multiple calls on a Maybe instance without having to check whether we currently have a value or not. The chosen example is probably a bit silly, but it shows what is possible:

<?php

$num = Maybe::fromValue(42);

$val = $num->map(function($n) { return $n * 2; })

->filter(function($n) { return $n < 80; })

->map(function($n) { return $n + 10; })

->orElse(Maybe::fromValue(99))

->map(function($n) { return $n / 3; })

->getOrElse(0);

echo $val;

// 33

The power of our Maybe type is that we have never had to consider whether the instance contained a value. We were just able to apply functions to it until finally, extracting the final value with the getOrElsemethod.

Lifting functions

We have seen the power of our new Maybe type. But the fact is, you either don't have time to rewrite all your existing functions to support it or you simply cannot because those are in an external third party.

Fortunately, you can lift a function to create a new function that takes a Maybe type as a parameter, applies to the original function to its value, and returns the modified Maybe type.

For this, we will need a new helper function. In order to keep things more or less simple, we will also assume that, if any of the parameters of the lifted function evaluate to the value Nothing, we will simply return nothing:

<?php

function lift(callable $f)

{

return function() use ($f)

{

if(array_reduce(func_get_args(), function(bool $status, Maybe $m) {

return $m->isNothing() ? false : $status;

}, true)) {

$args = array_map(function(Maybe $m) {

// it is safe to do so because the fold above checked

// that all arguments are of type Some

return $m->getOrElse(null);

}, func_get_args());

return Maybe::just(call_user_func_array($f, $args));

}

return Maybe::nothing();

};

}

Let's try it:

<?php

function add(int $a, int $b)

{

return $a + $b;

}

$add2 = lift('add');

echo $add2(Maybe::just(1), Maybe::just(5))->getOrElse('nothing');

// 6

echo $add2(Maybe::just(1), Maybe::nothing())- >getOrElse('nothing');

// nothing

You can now lift any function so that it can accept our new Maybe type. The only consideration to have is that it will not work if you want to rely on any optional parameter of your function.

We could use reflection or other means to determine whether the function has an optional value or pass some default to the lifted functions, but this will only complicate things and make our function slower. If you need to use a function with optional parameters and Maybe types, you can rewrite it or make a custom wrapper for it.

As a closing note, the process of lifting is not reserved to Maybe types. You can lift any function to accept any kind of container type. A better name for our helper will probably be liftMaybe or we could add it as a static method on our Maybe class to make things clearer.

The Either type

The Either type is a generalization of our Maybe type. Instead of having a value and nothing, you have a left and right value. As it is also a union type, only one of these two possible values can be set at any given time.

The Maybe type works well when there is only a few sources of errors or when the error in itself does not matter. With the Either type, we can provide any kind of information we want in case of error through the left value. The right value is used for success because of the obvious wordplay.

Here is a simple implementation of Either type. Since the code in itself is pretty boring, only the base class is presented in the book. You can access both child classes on the Packt website:

<?php

abstract class Either

{

protected $value;

public function __construct($value)

{

$this->value = $value;

}

public static function right($value): Right

{

return new Right($value);

}

public static function left($value): Left

{

return new Left($value);

}

abstract public function isRight(): bool;

abstract public function isLeft(): bool;

abstract public function getRight();

abstract public function getLeft();

abstract public function getOrElse($default);

abstract public function orElse(Either $e): Either;

abstract public function map(callable $f): Either;

abstract public function flatMap(callable $f): Either;

abstract public function filter(callable $f, $error): Either;

}

The implementation proposes the same API as the one we have for Maybe class, assuming that the right value is the valid one. You should be able to use the Either class instead of the Maybe class, everywhere without having to change your logic. The only difference is the methods to check which case we are in, and change the method to the new getRight or getLeft method.

It is also possible to write lift for our new type:

<?php

function liftEither(callable $f, $error = "An error occured")

{

return function() use ($f)

{

if(array_reduce(func_get_args(), function(bool $status, Either $e) {

return $e->isLeft() ? false : $status;

}, true)) {

$args = array_map(function(Either $e) {

// it is safe to do so because the fold above checked

// that all arguments are of type Some

return $e->getRight(null);

}, func_get_args());

return Either::right(call_user_func_array($f, $args));

}

return Either::left($error);

};

}

This function is, however, a bit less useful than a custom wrapper because you cannot specify an error message that is specific to the possible errors.

Libraries

Now that we covered the basics of the functional techniques with the various functions already available in PHP, it's time to have a look at the various libraries that will allow us to concentrate on our business code, instead of writing helpers and utility functions, as we did with our new Maybe and Either types.

The functional-php library

The functional-php library is probably one of the oldest libraries related to functional programming for PHP, as its first release dates back to June 2011. It evolved nicely with the newest PHP version and even switched to Composer last year for distribution.

The code is available on GitHub at https://github.com/lstrojny/functional-php. It should be fairly easy to install if you are accustomed to using Composer by writing the following command:

composer require lstrojny/functional-php.

The library used to be implemented both in PHP and as part of a C extension for performance reasons. But recent improvements of the PHP core regarding speed and the burden of maintaining two codebases made the extension obsolete.

A lot of helper functions are implemented-we won't have enough space to go into the details of each of them right now. If you are interested, you can have a look at the documentation. We will, however, quickly present the important ones and the rest of the book will contain examples using more of them.

Also, we haven't yet discussed some concepts covered by the library-related functions will be presented as we tackle those topics.

How to use the functions

As already discussed in Chapter 1, Functions as First-Class Citizens in PHP, since PHP 5.6, you can import a function from a namespace. This is the easiest way to use the library. You can also import the whole namespace and prefix all functions when calling them:

<?php

require_once __DIR__.'/vendor/autoload.php';

use function Functional\map;

map(range(0, 4), function($v) { return $v * 2; });

use Functional as F;

F\map(range(0, 4), function($v) { return $v * 2; });

It is also important to note that most functions accept arrays and anything implementing the Traversable interface, such as iterators.

General helpers

Those functions can help you in a variety of contexts, not only functional ones:

· The true and false functions check whether all elements in a collection are either strictly True or strictly False.

· The truthy and falsy functions are same as before but the comparison is not strict.

· The const_function function returns a new function that will always return the given value. This could be used to simulate immutable data.

Extending PHP functions

PHP functions have a tendency to work only on real arrays. The following functions extend their behavior to anything that can be iterated over using a foreach loop. The order of parameters is also kept the same across all functions:

· The contains method checks whether the value is contained in the given collection. The third parameter controls whether the comparison should be strict or not.

· The sort method sorts a collection but returns a new array instead of sorting by reference. You can decide to preserve the keys or not.

· The map method extends the array_map method behavior to all collections.

· The sum, maximum, and minimum methods perform the same job as their PHP counterparts but on any type of collection. Besides those, the library also contains product, ratio, difference, and average.

· The zip method performs the same work as the array_map method when you don't pass it a function. You can, however, also pass a callback to determine how the various items should be merged.

· The reduce_left and reduce_right methods fold collections either from the left or the right.

Working with predicates

When working with collections, you often want to check whether some, all, or no elements verify a certain condition and act accordingly. In order to do so, you can use the following functions:

· The every function returns the true value if all elements of a collection are valid for the predicate

· The some function returns the value true if at least one element is valid for the predicate

· The none function returns the value true if no elements at all are valid for the predicate

Those functions won't modify the collections. They are only to check whether the elements match a certain condition or not. If you need to filter some elements, you can use the following helpers:

· The select or filter functions return only the elements that are valid for the predicate.

· The reject function returns only the elements that are invalid for the predicate.

· The first or head function return the first element that is valid for the predicate.

· The last function returns the last element that is valid for the predicate.

· The drop_first function removes elements from the beginning of the collection until the given callback is true. As soon as the callback returns false, stop removing elements.

· The drop_last function is the same as the previous function, but starts at the end.

All those functions return a new array, leaving the original collection untouched.

Invoking functions

It is cumbersome to declare an anonymous function as soon as you want to invoke a function in a callback. Those helpers will do exactly that for you with a simpler syntax:

· The invoke helper invokes a method on all objects in a collection and returns a new collection with the result

· The invoke_first and invoke_last helpers invoke a method on the first and last object of a collection, respectively

· The invoke_if helper invokes the given method on the first parameter if it is a valid object. You can pass the method parameters and a default value.

· The invoker helper returns a new callable that invokes the given method with the given parameters to its parameter.

You might also want to call a function until you obtain a value or some threshold is reached. The library's got you covered:

· The retry library calls a function until it stops returning an exception or the number of tries is reached

· The poll library calls the function until it returns the truthy value or a given timeout is reached

Manipulating data

The previous functions group was about invoking functions with helpers; this one is about getting and manipulating data without having to resort to an anonymous function each time:

· The pluck function fetches a property from all objects in a given collection and returns a new collection with the values.

· The pick function selects an element from an array based on the given key. You can provide a default value if the element does not exist.

· The first_index_of and last_index_of functions return the first, respectively last, index of an element matching the given value.

· The indexes_of function returns all indexes matching the given value.

· The flatten function reduces the depth of nested collection to a single flat collection.

Sometimes, you also want to separate a collection into multiple parts, either given a predicate or some grouping value:

· The partition method accepts a list of predicates-each item of the collection is put in a given group based on the first predicate for which it is valid

· The group method creates multiple groups based on each different value returned by the callback for each element

Wrapping up

As you can see, the functional-php library offers a lot of different helpers and utility functions. It might not be obvious how you can get the most of all of them right now, but I hope the remainder of the book will give you a glimpse of what you can achieve.

Also, do not forget that we didn't present all functions as some of them need a bit of theoretical explanation first. All in due time.

The php-option library

We created our own version of the Maybe type earlier. This library proposes a more complete implementation. The naming used by Scala was chosen, however. The source code is on GitHub at https://github.com/schmittjoh/php-option. The easiest way to install is to by writing the following command using Composer:

composer require phpoption/phpoption

An interesting addition is the LazyOption method, which takes a callback instead of a value. The callback will be executed only when a value is needed. This is particularly interesting when you use the orElsemethod to give alternatives in case the previous one is an invalid value. By using the LazyOption method in this case, you avoid doing unnecessary computation as soon as one value is valid.

You also have various helpers to help you call methods only if the value is valid, for example, and there are multiple instantiation possibilities offered. The library also provides an API even more akin to the one you are accustomed to for a collection.

Laravel collections

As already mentioned in the first chapter, Laravel offers a great library to manage collections. It declares a class called Collection which is used internally by their ORM, Eloquent, and most of the other parts relying on collections.

Internally, a simple array is used, but it is wrapped in a way that promotes immutability of the data and a functional approach manipulating data. To achieve this goal, between 60 and 70 methods are proposed to the developer.

If you are already using Laravel, you are probably already familiar with the possibilities offered by this support class. If you are using any other framework, you can still benefit from it by getting the extracted part from https://github.com/tightenco/collect.

The documentation is available on Laravel's official website at https://laravel.com/docs/collections. We won't describe each method in detail as there are a lot of them. If you are using Laravel and want to learn more about all the possibilities offered by its collections, you can head over to https://adamwathan.me/refactoring-to-collections/.

Working with Laravel's Collections

The first step is to transform your array or Traversable interface to an instance of the Collection class using the collect utility function. You will then have access to all the various methods the class provides. Let's make a quick list of those that we have already encountered in another form so far:

· The map method applies a function to all elements and returns the new value

· The filter method filters the collection using a predicate

· The reduce method folds the collection using the given callback

· The pluck gets a given property from all elements

· The groupBy method partitions the collection using the given value from each element

All those methods return a new instance of the Collection class, preserving the values of your original instance.

Once you are done manipulating, you can get the current values as an array using all method.

The immutable-php library

This library proposing an immutable data structure is born due to various gripes with the SplFixedArray method from the Standard PHP Library, mostly with its hard-to-use API. At its core, the immutable-php library uses the aforementioned data structure, but with a nice set of methods to wrap it.

The SplFixedArray method is a specific implementation of an array with a fixed size and which allows only numerical indexes. Those constraints allow for a really fast array structure.

You can have a look on the GitHub project page at https://github.com/jkoudys/immutable.php or install it by writing the following command using Composer:

composer require qaribou/immutable.php.

Using immutable.php

Creating a new instance is really easy using the dedicated static helpers fromArray or fromItems for any instance of the Traversable class. Your newly created ImmArray instance can be accessed like any array, iterated over using foreach loop, and counted using the count method. However, if you try to set a value, you will be rewarded with an exception.

Once you have your immutable array, you can have various methods to apply the transformations you should now be accustomed to:

· The map method to apply a function to all items and return the new value

· The filter method to create a new array with only items valid for the predicate

· The reduce method to fold items using a callback

You also have other helpers:

· The join method concatenates a collection of strings

· The sort method returns a collection sorted using the given callback

Your data can also easily be retrieved as a traditional array or encoded to a JSON format.

All in all, this library provides fewer methods than Laravel's Collection but you will have better performance and a much lower memory footprint.

PHP Functional

This library revolves mostly around the concept of Monads we will see in Chapter 5, Real-life Monads. The acknowledged inspiration is Haskell, from which the library implements:

· State Monad

· IO Monad

· Collection Monad

· Either Monad

· Maybe Monad

Through the Collection Monad, the library offers the various methods we expect the map, reduce, and filter methods.

As it is inspired by Haskell, you might find it a bit more difficult to use in the beginning. However, it should prove more powerful in the end. You can find the code on GitHub at https://github.com/widmogrod/php-functional.

Functional

Originally created as a learning playground, this library has grown into something that could prove useful if you are looking for something relatively small. The main idea is to provide a framework so that you can remove all loops in your code.

The most interesting feature is that all functions can be partially applied without doing anything special. Partial application is really important for function composition. We will discover both topics in Chapter 4, Compositing functions.

The library also has all the traditional contenders such as mapping and reducing. The code and documentation are available on GitHub at https://github.com/sergiors/functional.

PHP functional programming Utils

This library tries to walk the same path as the functional-php library, which we presented in the previous pages. As far as I can tell, it has, however, slightly fewer features as of now. It can be an interesting library for people wanting something a tad smaller and maybe easier to learn. The code is on GitHub at https://github.com/daveross/functional-programming-utils.

Non-standard PHP library

This library is not strictly a functional one. The idea is more to extend the standard library with various helpers and utility functions to make working with the collections easier.

It contains useful features such as helpers to validate function parameters with ease, either with already defined constraints or custom ones. It also extends existing PHP functions so that they can work on anything that is Traversable interface and not just arrays.

The library was created in 2014 but was nearly dead until work started going strong again at the end of 2015. Now it is a possible replacement for any of the libraries we presented previously. If you are interested, grab the code on GitHub at https://github.com/ihor/Nspl.

Summary

In this long chapter, we presented all the practical building blocks that we will use throughout the book. I hope the few examples didn't seem too dry. There was a lot to cover and only a limited set of pages. The following chapters will build on what we have learned with better examples.

You first read some general advice about programming in general that is especially important for a functional codebase. We then discovered basic functional techniques such as mapping, folding, filtering, and zipping, all of which are available directly within PHP.

The next part was a brief introduction to recursion, both a technique to solve a particular set of problems and to avoid using loops. In a book about a functional language, the topic might have deserved a whole chapter, but since PHP has various loop structures, it's a bit less important. Also, we will see more recursion examples in the following chapters.

We also discussed exceptions and why they pose issues in a functional codebase, and we wrote implementations for the Maybe and Either types as a better way to manage errors after discussing the pros and cons of other methods.

Finally, we presented some libraries that provide functional constructs and helpers so that we don't have to write our own.