Why Functional Programming Is on the Rise Again

Intertech, Inc.
7 min readDec 12, 2018

--

In recent years, the number of articles touting the benefits of functional programming and the number of projects using functional languages has been on the rise. For many developers who don’t have exposure to the principles of functional programming, it can be difficult to wrap your head around exactly what it entails.

Even though functional programming has become a buzzword of late, it’s not a new paradigm, and its roots go all the way back to the early days of computing. For any developer, it’s worth acquainting yourself with the principles of functional programming and testing out this paradigm on your own. Even if you don’t use functional programming professionally, you’ll find that exposing yourself to new ways of thinking about programming will make you a more effective and creative developer in the long run.

Foundations in Lambda

Functional programming has its roots in lambda calculus that predates the first computer. Alonso Church introduced the tenets of lambda calculus in the 1930s. The basic premise behind lambda calculus is that any computation can be expressed as a series of abstracted functions:

f(x) = x * 2
g(x) = x + 4
(f ⚬ g)(x) = (x + 4) * 2
(g ⚬ f)(x) = (x * 2) + 4
... and so on, for h(x), i(x), etc. in many permutations

In programming, we see the influence of lambda calculus all the time when we declare a new function and pass it abstracted parameters:

int add(int a, int b) {
return a + b;
}

In the simple example above, the function “add” takes two abstract parameters, “a” and “b.” When we call add, we give it real arguments (variables) to pass into the abstract function. This separation between real arguments and abstract functions/parameters is the foundation of lambda calculus and, as we’ll see, functional programming.

Of course, most programming languages support abstract functions and nearly every developer uses them. The difference between functional programming and other paradigms like imperative or object-oriented programming is the extent to which a function is pure and self-contained. While some paradigms rely on global and local states and mutable variables, functional programming argues for constant variables and functions that are independent of state.

History of Functional Programming

Functional programming has been codified and promoted at least since John McCarthy’s creation of the language Lisp in the 1950s at MIT. Over the decades, more functional languages have joined the fray, and many experts have argued for a functional approach to programming. John Backus’s 1977 Turing Award lecture that decried the problems with imperative programming was a turning point for the study of functional programming. Since then, functional programming has become a preferred approach in academic computing study.

Haskell began in 1987 as an open standard for programming research using a functional paradigm. Erlang also arose in the late 1980s. At the time, it seemed functional programming had reached an inflection point. However, it failed to make an entrance into business use because functional programs are structured differently and look foreign to most developers. Additionally, the switching costs of refactoring existing software to functional programs were too high.

Recently, with the advent of new compilers and improvements to functional languages, businesses have begun to give functional programming a second look. The rise of multi-core processing has also increased interest in functional programs that support concurrency more readily.

What Is Functional Programming?

Outside of its storied history and foundations in lambda calculus, what exactly is functional programming and what does it mean for a working programmer?

To answer that, let’s look at a common type of challenge that many developers face as they work on large or complex projects that rely on imperative or object-oriented programming paradigms.

Side Causes & Side Effects

Functional programming’s main objective is eliminating side effects and hidden impacts in a code base. When we declare a function, for instance, we usually specify parameters for that function. However, sometimes a function also takes hidden inputs that we can’t predict or control as easily. As a simple example:

Consider a function like this (code from Chris Jenkins):

public Program getCurrentProgram(TVGuide guide, int channel) {
Schedule schedule = guide.getSchedule(channel);
Program current = schedule.programAt(new Date());
return current;
}

getCurrentProgram seems to take two inputs, guide and channel, listed in the function declaration. However, there’s really a third input in this function: the current time as requested with new Date().

These hidden inputs are called side effects. They’re the things in the function that rely on global or local state or another function. It’s these hidden dependencies and downstream effects that make massive codebases hard to debug and spaghetti code difficult to understand.

Pure Functions

Functional programming addresses this complexity by relying on pure functions. Each function should be self-contained and independent of state. To “purify” getCurrentProgram above, for instance, we’d need to move the current date into the function declaration along with the other parameters.

public Program getProgramAt(TVGuide guide, int channel, Date when) {
Schedule schedule = guide.getSchedule(channel);
Program program = schedule.programAt(when);
return program;
}

A function is pure when all its inputs are declared as parameters and all its outputs are returned as outputs. Pure functions are much easier to test and reason about. They fit together modularly and independently without needing to know anything about the other functions in the application.

Recursion Rather Than Loops

Functional programming also removes the iterators that are popular in imperative and object-oriented programming. There are no “for” or “while” in the functional paradigm. Instead, functional programs use recursion to iterate, calling themselves until they reach the base case.

This is also for the sake of purity and no dependency. Instead of relying on a counter variable or an outside condition, recursive iteration relies on the function itself to decide when to end its work.

Immutable State & Variables

Functional programming also removes side effects by making variables immutable and constant. If you need access to a new value, you’ll have to assign it to a new variable. Something like…

a = a * 2;

…is not possible in a functional program. Instead, you’d need to create a new variable to hold the newly multiplied value. As a result, the state is constant throughout the runtime of the program. Values never change once they’re initialized.

Functional Programming Languages

Put simply, a functional programming language is one that limits the ability to create side effects in your code. These languages don’t support for and while loops. Functions only have access to their own parameters, and variables can’t be changed once initialized.

Haskell, Erlang, Perl, Elixir, Clojure are some of the most popular examples of functional languages. They follow the above rules (and others) with varying degrees of strictness and orthodoxy. Of course, it’s possible to apply the principles of functional programming in almost any language. Kotlin, Ruby, and Python developers have been among the most eager to adopt parts of the functional paradigm.

Pros & Cons

Functional programs are concise and often beautifully modular. However, they can feel weirdly unfamiliar for most developers and they’re often slow.

The major benefit of functional programming is it becomes easier to reason about what the program is doing and how. States don’t change and you can clearly see in a function’s declaration all the inputs and outputs. As a consequence, functional programs are also a breeze to write tests for, since every function is self-contained.

Of course, readability and comprehension are real factors in software development. Pure functions can be harder to understand, especially in functional languages with unique syntax. Using recursive loops also can be confusing and intimidating. Additionally, combining pure functions into a complete application can be a challenge without access to mutable variables and when I/O operations are involved.

Finally, immutability can be a blessing and a curse when it comes to runtime. Easy access to immutable variables does facilitate concurrency and parallelism. However, it also can consume lots of memory and recursion can decrease performance.

A New Paradigm Changes the Way You Think About Code

Practically, functional programming doesn’t make sense in every application. In fact, most developers probably won’t use a functional programming language professionally in their careers. It’s still a niche specialization to be a Haskell or Erlang developer, for instance.

That said, the principles of functional programming can apply in nearly every software project. Reducing dependencies and side effects in your code makes sense. Thinking like a functional programmer can have major benefits for the testability, consistency, and logic of your code. As such, every developer should take the time to study and practice functional programming. The challenge of thinking about code in a new way will open up new possibilities, productivity, and efficiency in your work, no matter what it is that you’re developing.

About Intertech

Founded in 1991, Intertech delivers technology training and software development consulting to Fortune 500, Government and Leading Technology institutions. Learn more about us. Whether you are a developer interested in working for a company that invests in its employees or a company looking to partner with a team of technology leaders who provide solutions, mentor staff and add true business value, we’d like to meet you.

Originally published at Intertech Blog.

--

--

Intertech, Inc.
Intertech, Inc.

Written by Intertech, Inc.

A leading software development consulting firm with a unique blend of consulting, training, and mentoring.

No responses yet