Functional Programming in PHP

The new hype in programming is all about functional programming paradigms. Functional languages are used more and more in greater and better applications. Scala, Haskel, etc. are thriving and other, more conservative languages like Java started to adopt some of the functional programming paradigms (see closures in Java7 and lazy eval for lists in Java8). However, what only few people know is that PHP is quite versatile when it comes to functional programming. All the main functional programming concepts can be expressed in PHP. So, if you are new to functional programming, be prepared to have your mind blown, and if you are already familiar with functional programming, be prepared to have great fun with this tutorial.


Programming Paradigms

Without any programming paradigms we could do whatever we want in whatever way we want. While this would lead to extreme flexibility, it would also lead to impossible architectures and very bloated code. Thus, programming paradigms were invented to help us, programmers, to think in a specific way about a specific program and in this way, limit our ability to express our solution.

Each programming paradigm takes away a freedom from us:

  • Modular Programming takes away unlimited program size.
  • Structured and Procedural Programming are taking away the "go-to" and limits the programmer to sequence, selection and iteration.
  • Object Oriented Programming takes away pointers to functions.
  • Functional Programming takes away assignment and mutable state.

Functional Programming Principles

In functional programming, you have no data represented by variables.

In functional programming everything is a function. And I mean everything. For example a set, as in mathematics, can be represented as several functions. An array or list is also a function or a group of functions.

In object oriented programming everything is an object. And an object is a collection of data and methods that do actions on those data. Objects have a state, a volatile, mutable state.

In functional programming, you have no data represented by variables. There are no data containers. Data is not assigned to a variable. Some values may be defined and assigned. However, in most cases they are functions assigned to "variables". I've put "variables" between quotes because in functional programming they are immutable. Even though most functional programming languages do not enforce immutability, in the same way most object oriented languages do not enforce objects, if you change the value after an assignment you are not doing pure functional programming any more.

Because you don't have values assigned to variables, in functional programming you have no state.

Because of no state and no assignments, in functional programming functions have no side effect. And because of the three previous reasons, functions are always predictable. This basically means that if you call a function with the same parameters again and again and again... you will always have the same result. This is a huge advantage over object oriented programming and greatly reduces the complexity of multi-threaded and massively multi-threaded applications.

But, if we want to express everything in functions, we need to be able to assign them to parameters or return them from other functions. Thus functional programming requires the support for higher-order functions. This basically means that a function can be assigned to a "variable", sent in as a parameter to another function, and returned as a result of a function.

Finally, because we have no values in variables, while and for loops are unusual to functional programming and are replaced with recursion.


Show Me the Code!

Enough talking and philosophy for one lesson. Let's code!

Set up a PHP project in your favorite IDE or code editor. Create in it a "Tests" folder. Create two files: FunSets.php in the project's folder, and FunSetsTest.php in the Tests folder. We will create an application, with tests, that will represent the concept of sets.

In mathematics, a set is a collection of distinct objects, considered as an object in its own right. (wikipedia)

That basically means that sets are a bunch of things in a single place. These sets can be and are characterized by mathematical operations: unions, intersections, differences, etc. And by actionable properties like: contains.

Our Programming Limitations

So let's code! But wait. How? Well, to respect the concepts of functional programming we will have to apply the following restrictions to our code:

  • No assignments. - We are not allowed to assign values to variables. We are however allowed to assign functions to variables.
  • No mutable state. - We are not allowed, in case of an assignment, to change the value of that assignment. We are also not allowed to change the value of any variable that had its value set as the parameter for the current function. So, no changing of parameters.
  • No while and for loops. - We are not allowed to used PHP's "while" and "for" commands. We, however, may define our own method to cycle through the elements of a set and call it foreach/for/while.

No limitations apply to tests. Because of the nature of PHPUnit, we will use classic object oriented PHP code there. Also, to better accommodate our tests, we will wrap all our production code into a single class.

The Set's Defining Function

If you are a seasoned programmer, but unfamiliar with functional programming, now is the time to stop thinking as you do usually and be ready to leave your comfort zone. Forget all your previous ways of reasoning about a problem and imagine all in functions.

The defining function of a set is its "contains" method.

OK... This is not that obvious, so let's see how would we use it.

Well, this explains it a little bit better. The function "contains" has two parameters:

  • $set - represents a set defined as a function.
  • $elem - represents an element defined as a value.

In this context, all that "contains" has to do is to apply the function in $set with the parameter $elem. Let's wrap all in a test.

And wrap our production code inside FunSets.php into a class:

You can actually run this test and it will pass. The set we defined for this test is just a function that always returns true. It is a "true set".

The Singleton Set

If the previous chapter was a little bit confusing or looked useless in logic, this one will clarify it a little bit. We want to define a set with a single element, a singleton set. Remember, this has to be a function, and we will want to use it like in the test below.

We need to define a function called "singeltonSet" with a parameter representing an element of the set. In the test, that is the number one (1). Then, we expect our contains method, when called with a singleton function, to return true if the sent in parameter equals one. The code that makes the test pass is as follows:

Wow! That's crazy. So, the function "singletonSet" gets as a parameter an element as $elem. Then it returns another function which has a parameter $otherElem and this second function will compare $elem to $otherElem.

So, how does this work? First, this line:

is transformed into what "singletonSet(1)" returns:

Then "contains($singleton, 1)" is called. Which, in turn calls whatever is in $singleton. So the code becomes:

Which actually executes the code in it with $otherElem having the value one.

Which is of course true and our test passes.

Are you already smiling? Do you feel your brain starting to boil? I certainly did when I first wrote this example in Scala and I did again when I first wrote this example in PHP. I think this is extraordinary. We managed to define a set, with one element, with the ability to check that it contains the value we passed in to it. We did all these without a single assignment of value. We have no variable containing the value one or having a state of one. No state, no assignment, no mutability, no loops. We are on the right track here.


Union of Sets

Now that we can create a set with a single value, we need to be able to create a set with several values. The obvious way to do it is to define the union operation on our sets. The union of two singleton sets will represent another union with both values. I want you to take a minute and think about the solution before scrolling to the code, maybe take a peak on the tests below.

We want a function called "union" that gets two parameters, both sets. Remember, sets are just functions for us, so our "union" function will get two functions as parameters. Then, we want to be able to check with "contains" if the union contains an element or not. So, our "union" function must return another function that "contains" can use.

This is actually working quite well. And it is perfectly valid even when your union is called with another union plus a singleton. It calls contains inside itself for each parameter. If it's a union, it will recurse. It's that simple.


Intersect and Difference

We can apply the same one liner logic with minor changes to obtain the next two most important functions that characterize a set: intersection - contains only the common elements between two sets - and difference - contains only those elements from the first set that are not part of the second set.

I won't flood you with the test code for these two methods. The tests are written and you can check them if you look in the attached code.


Filter Set

Well, this is a bit more complicated, we won't be able to solve this with a single line of code. A filter is a function that uses two parameters: a set and a filtering function. It applies the filtering function to a set and returns another set that contains only the elements that satisfy the filtering function. To better understand it, here is the test for it.

We create a set with three elements: 1, 2, 3. And we place it into the variable $u123 so it is easily identifiable in our tests. Then we define a function we want to apply to the test and place it into $condition. Finally, we call filter on our $u123 set with $condition and place the resulting set into $filteredSet. Then we run assertions with "contains" to determine if the set looks how we want. Our condition function is simple, it will return true if the element is greater than one. So our final set should contain only the values two and three, and this is what we check in our assertions.

And here you go. We implemented filtering with just three lines of code. More precisely, if the condition applies to the provided element, we run a contains on the set for that element. If not, we just return false. That's it.


Looping Over Elements

The next step is to create various looping functions. The first one, "forall()", will take a $set and a $condition and return true if $condition applies to all the elements of the $set. This leads to the following test:

We extracted the $u123 creation from the previous test into a private method. Then we define three different conditions: higher than zero, higher than one, and higher than two. As our set contains the numbers one, two, and three, only the higher than zero condition should return true, the rest should be false. Indeed, we can make the test pass with the help of another recursive method use to iterate over all the elements.

We start by defining some limits for our set. The values must be between -1000 and +1000. This is a reasonable limitation we impose in order to keep this example simple enough. The function "forall" will call the private method "forallIterator" with the necessary parameters to recursively decide if all elements respect the condition. In this function, we first test if we are out of bounds. If yes, return true. Then check if our set contains the current value and return the current value applied to the condition together with a logical "AND" with a recursive call to ourselves with the next value. Otherwise, just call ourselves with the next value and return the result.

This works just fine, we can implement it in the same manner as "exists()". This returns true if any of the elements satisfies the condition.

The only difference is that we return false when out of bounds and we use "OR" instead of "AND" in the second if.

Now, "map()" will be different, simpler and shorter.

Mapping means we apply an action to all the elements of a set. For map, we need no helper iterator, we can reuse "exists()" and return those elements of "exists" that satisfy the result of $action applied to $element. This may not be obvious at first site, so let's see what is happening.

  • We send the set { 1, 2 } and the action $element * 2 (double) to map.
  • It will return a function, obviously, which has a parameter as an element and uses the set and action from one level higher.
  • This function will call exists with the set { 1, 2 } and the condition function $currentElement equals $elem * 2.
  • exists() will iterate over all the elements between -1000 and +1000, our bounds. When it finds an element, double of what comes from "contains" (the value of $currentElement) it will return true.
  • In other words, the last comparison will return true for the call to contains with value two, when the value of the current multiplied by two, results in two. So for the first element of the set, one, it will return true on two. For the second element, two, on value four.

A Practical Example

Well, functional programming is fun but it is far from ideal in PHP. So, I do not recommend you to write whole applications this way. However, now that you've learned what PHP can do functionally, you can apply parts of this knowledge in your everyday projects. Here is an example authentication module. The AuthPlugin class provides a method that receives a user and password and does its magic to authenticate the user and set its permissions.

Now, this may sound OK, but it has a huge problem. 80% of the "authenticate()" method uses information from the "AdminModules". This creates a very strong dependency.

AuthFanOut

It would be much more reasonable to take the three calls and create a single method on AdminModules.

AuthReducedDep

So, by moving the generation into AdminModules we managed to reduce three dependencies to just one. The public interface of AdminModules was also reduced from three to only a single method. However, we are not yet there. AuthPlugin still directly depends on AdminModules.

An Object Oriented Approach

If want our authentication plugin to be usable by any module, we need to define a common interface for these modules. Let's inject the dependency and introduce an interface.

AuthPlugin got a constructor. It gets a parameter of type ApplicationModule, an interface, and calls "getPermissions()" on this injected object.

ApplicationModule defines a single public method, "getPermissions()", with a username as a parameter.

Finally, AdminModules needs to implement the ApplicationModule interface.

AuthAdminInterface

Now, this is much better. Our AuthPlugin depends only on an interface. AdminModules depends on the same interface, so the AuthPlugin became module agnostic. We can create any number of modules, all implementing ApplicationModule, and AuthPlugin will be able to work with all of them.

A Functional Approach

Another way to reverse the dependency, and make AdminModule, or any other module, to use the AuthPlugin is to inject into these modules a dependency to AuthPlugin. AuthPlugin will provide a way to set the authentication function and each application will send in its own "getPermission()" function.

We start with AdminModule. It does not implement anything anymore. However, it uses an injected object that has to implement Authentication. In AdminModule there will be an "authenticate()" method that will call "setPermissions()" on AuthPlugin and pass in the function that needs to be used.

The Authentication interface simply defines the two methods.

Finally, AuthPlugin implements Authentication and sets the incoming function in a private class attribute. Then, "authentication()" becomes a dumb method. It just calls the function and then sets the return value. It is completely decoupled of whatever comes in.

AuthAdminCallBack

If we look at the schema, there are two important changes:

  • Instead of AdminModule, AuthPlugin is the one implementing the interface.
  • AuthPlugin will "Call Back" AdminModule, or whatever other module sent in the permissions function.

Which One to Use?

There is no correct answer to this question. I would argue that if the process of determining the permissions is quite dependent on the application module, then the object oriented approach is more appropriate. However, if you think that each application module should be able to provide an authentication function, and your AuthPlugin is just a skeleton providing the authentication functionality but without knowing anything about users and procedures, then you can go with the functional approach.

The functional approach makes your AuthPlugin very abstract and you can depend on it. However, if you plan to allow your AuthPlugin to do more and know more about users and your system, then it will became too concrete, and you don't want to depend on it. In that case choose the object oriented way and let the concrete AuthPlugin depend on the more abstract application modules.

Tags:

Comments

Related Articles