HGFP-II : Why you should learn Functional Programming

why-functional-programming
why-functional-programming

So, here we are, with the second article of our “humble” guide to Functional Programming series.

In the previous article, we talked about abstractions, programming paradigms and sneaked a peek into simple functional programming examples.

In this article, we will talk about the reasons behind the rising popularity of functional paradigm.

So, let’s start with a question that actually, we better answer even earlier: “Why functional programming became popular now, after all those years? And why do I care?”

Most of us probably already have an idea about this, but I want to briefly share my two cents about it.

I guess it wouldn’t be wrong to say that it all started with the Internet era. Think about the most elegant, most complex software systems before the Internet. Most people who are dealing with software development those days were mostly developing software, based on the client-server model, where we have many clients and a single server connected via a network (lan/wan) who answers the requests send by them.

I remember the times when we develop software with tools like Delphi (which was awesome those days btw) with Object Pascal and Oracle Forms/Reports using PL/SQL. How many users a typical client/server application would have? Hundreds, a couple of thousands, tens of thousands at most. But not hundreds of thousands or even millions. Of course, we had the internet those days, but the connections were slow and unreliable, and it was not common as it is today.

With the internet becoming widespread, it is expected from the software systems to respond to the number of users that they have never encountered before. Especially after browser being the primary client and user interface for all applications that serve via the internet, the number of users gone crazy. Systems like Google, Facebook or Yahoo has started to respond to millions of users per second. This requirement emerged a critical concept that continues to shape our modern software architectures: Scalability.

Wikipedia defines Scalability as, the capability of a system, network, or process to handle a growing amount of work or its potential to be enlarged in order to accommodate that growth.

Let’s have a look at the graphic. The response times of the application with a “good” scalability trend don’t change that much. The response time is around 25 when it has 500 users, and it’s about 90 when it has 2500 users. On the other hand, the application with a “poor” scalability trend leap to 300+ from 40 when it reaches a user count of 2500.

Scalability has a paramount importance in modern software systems. And there are mainly two ways of scaling a software architecture: vertical and horizontal. Vertical scalability is the approach where we replace a system with a bigger and a more capable one, while horizontal scalability is the approach where we scale the system by adding new nodes to an existing system and grow horizontally.

Before we connect the dots and talk about the functional programming and scalability, let’s mention another concept that also plays a paramount role in modern software architectures: Concurrency & Parallelism.

Concurrency means that an application is making progress on more than one task at the same time, and parallelism means than an application splits its tasks up into smaller tasks which can be processed in parallel [1]. These concepts became more and more important in recent years, because due to the physical limitations on hardware production, hardware producers start to increase amount of cores within the CPU instead of increasing transistor counts and the clock speeds.

What does that mean to us as software developers? It means that even if you write the most efficient algorithm in the world and found the most elegant way to solve a problem, if your application runs with a single thread on a machine that has a CPU with 16 cores, you’re not utilizing the machine and wasting the clock cycles of remaining 15 CPU cores which has nothing to do with your application.

So we have two fundamental issues to solve in order to fulfill the expectations and service to the growing amount of users of our software systems.

Let’s look at which tools we have to solve these problems. The best option seems to be Object Oriented Programming, right? We all know what it excels at: it gives us to solve and model problems similar to the way we think about the world. But unfortunately, it falls short badly when it comes to concurrency and scalability.

Let’s give a very basic example in regard to concurrency. Have a look at this function:

public class Increment {

    private int i = 0;

    public void increment() {
        i++;
    }
}

What does it do? Almost the simplest thing possible: it increases the value of an integer variable by 1. Great. Now think of the situation where this method is called by two threads at the same time. What happens do you think?

Many different things can happen. Because underneath, the i++ operation is not “atomic”, let’s have a look at the bytecode it generates.

$ javap -c Increment

public class Increment {
  public Increment();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: iconst_0
       6: putfield      #2                  // Field i:I
       9: return

  public void increment();
    Code:
       0: aload_0
       1: dup
       2: getfield      #2                  // Field i:I
       5: iconst_1
       6: iadd
       7: putfield      #2                  // Field i:I
      10: return
}

As you can see, i++ operator consist of different steps. It doesn’t correspond to a single operation, it gets the value of the field, loads to the stack, increases stack value by one and puts the new value to the field. So scheduler may decide to switch the CPU usage to another thread in anytime between these operations.

If the new thread messes with the same variable, we may see interesting results. For example, the value gets increased by two threads at the same time and we may actually see that it’s increased by one, which is a case named “lost update”.

This is the simplest problem that you can come across in a system that mostly uses “imperative style” to solve problems (and as we all know, systems based on OOP are among them), based on the change of global state. In order to solve these kinds of problems, many different sophisticated and complex structures have been developed. Java comes with a rich set of APIs, collections that try to make it easier and more efficient to solve these issues. Especially starting from Java version 6, you can take advantage of many different concurrency utilities like, Atomic primitives and objects, different types of locks, collections that support concurrent access in an efficient way like ConcurrentHashMap, BlockingQueues, so on and so forth.

But none of them actually can solve the fundamental problem we have: the change of a state. If you keep changing a state and do everything right using these constructs (which is an amazingly hard thing to do in a complex system) at best you will have a consistent but non-performant system, compared to the systems which don’t have the fundamental problem in the first place; remember? Change of the state.

To wrap up, we have two fundamental issues to solve, scalability and concurrency/parallelism. And both of them are really hard to solve in an environment based on state change. We already had a look at the problems with concurrency, and the problem with scalability is that, in terms of modern application requirements, the only sustainable way of doing that is horizontal scalability, and that means distribution. And distributed computing is also very hard when you have a common and global state to protect/change in a consistent way.

In short, OOP based systems fall short badly when we want truly scalable, robust, fault tolerant and highly concurrent systems that utilize underlying hardware without sacrificing resources to keep the state consistent.

In functional programming, state change considered as evil. And in the purely functional approach, we model our applications using pure functions, none of which mutate a common/global state and create side effects.

So, why do we care? Well, the goals mentioned above should be pretty good reasons to care about functional programming. As an example, take a look at Erlang, considered as a functional language and a system written with Erlang have a high availability record of nine “9”s, which is something almost impossible to achieve using an OOP environment. And as we progress, you will see we don’t have to start from scratch and build a new architecture using a purely functional language in order to take advantage of it. There are countless ways that functional approach would still help us within the boundaries of a mostly OOP environment. We’ll get to that.

Beginning with the next article, we will start using Java 8 and lambda expressions as the main platform for our functional programming exercises. We will make sense of lambda expressions and understand what they correspond to underneath, and then try to explain how they would become useful to solve our problems; design patterns and collections are being two most common application areas.

As the last point before we complete this article, I want to mention a possible confusion that I come across often: functional and procedural programming.

Functional and procedural programming are totally different approaches. Functions exist in almost all programming paradigms, but the fundamental difference between different paradigms lies in how we make use of them.

While using procedural programming languages, we tend to break the problem into steps that are expressed as functions/procedures that follow each other. As we have mentioned before comparing OOP and functional programming, imperative programming style is also widely used in procedural languages, in which we basically define each step to complete the action and mostly change a (local or global) state doing so.

Let’s remember the example in our previous post again:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
for (int i=0; i<numbers.size(); i++) {
    int val = numbers.get(i);
    System.out.println(val);
}

This sample code is close to what we could see in a procedural language: there is a struct/object/variable that represents a “state” and there are expressions written in an imperative style, by specifying each step in detail that modifies this state.

In contrast, in functional programming we use functions, and if we are talking about a purely functional approach, those functions would have no side-effects.

Let’s look at this function:

public int square(int num) {
   return num * num;
}

This function can be said to be “pure”, which means has no side-effects. It always gives the same result for a given parameter and doesn’t mess with any other state or variables exist the outer scope of itself.

Thus, at the most fundamental level, we can define a whole application as purely functional if it consists of only pure functions which have no side effects don’t mutate any global state, and a programming language as a purely functional programming language if it only allows you to structure a program in this way, by using only pure functions and no mutable state.

Of course, it’s easier said than done; and it requires a great amount of effort and invention of different concepts like functions being first class members, immutable objects/collections, higher order functions, monads and more. But we will mention them in upcoming articles. Now it’s enough for us to know the primary difference between the paradigms.

Please leave your comments below and feel free to reach me via Twitter and Linkedin. Also don’t forget to subscribe to our newsletter which will be launched soon, not to miss updates and special bonuses (documents, trainings) special to subscribers.

Hope to see you again soon with examples of functional programming using Java 8 as our environment.

[1] Concurrency vs. Parallelism

 

SHARE
Founder @ Koders. Computer engineer, entrepreneur, trainer, consultant. Mostly interested in Big Data ecosystem (Hadoop, Spark, NoSQL and related technologies), Java, Scala, Enterprise technologies and Agile software development. You may find and follow him on Twitter and Linkedin.

LEAVE A REPLY