Functional programming has been around for the last 60 years, but so far it’s always been a niche phenomenon. Although game-changers like Google rely on its key concepts, the average programmer of today knows little to nothing about it.
In simple terms, functional programming is all about building functions for immutable variables. In contrast, object-oriented programming is about having a relatively fixed set of functions, and you’re primarily modifying or adding new variables.
Because of its nature, functional programming is great for in-demand tasks such as data analysis and machine learning. This doesn’t mean that you should say goodbye to object-oriented programming and go completely functional instead. It is useful, however, to know about the basic principles so you can use them to your advantage when appropriate.
It’s all about killing side effects
To understand functional programming, we need to understand functions first. This might sound boring, but at the end of the day, it’s pretty insightful. So keep reading.
A function, naively stated, is a thing that transforms some input into some output. Except that it’s not always that simple. Consider this function in Python:
This function is dumb and simple; it takes one variable x, presumably an int, or perhaps a float or double, and spits out the square of that.
Now consider this function:
At the first glance, it looks like the function takes a variable x, of whichever type, and returns nothing since there is no return statement. But wait!
The function wouldn’t work if global_list hadn’t been defined beforehand, and its output is that same list, albeit modified. Even though global_list was never declared as an input, it changes when we use the function:
Instead of an empty list, this returns [1,2]. This shows that the list is indeed an input of the function, even though we weren’t explicit about it. And that could be a problem.
Being dishonest about functions
These implicit inputs — or outputs, in other cases — have an official name: side effects. While we were only using a simple example, in more complex programs these can cause real difficulties.
Think about how you would test append_to_list: Instead of just reading the first line and testing the function with any x, you need to read the whole definition, understand what it’s doing, define global_list, and test it that way. What’s simple in this example can quickly become tedious when you’re dealing with programs with thousands of lines of code.
The good news is that there is an easy fix: being honest about what the function takes as an input. This is much better:
We haven’t really changed much. The output is still [1,2], and everything else remains the same, too.
We have changed one thing, however: the code is now free of side effects. And that’s great news.
When you now look at the function declaration, you know exactly what’s going on. Therefore, if the program isn’t behaving as expected, you can easily test each function on its own and pinpoint which one is faulty.
Functional programming is writing pure functions
A function with clearly declared in- and outputs is one without side effects. And a function without side effects is a pure function.
A very simple definition of functional programming is this: writing a program only in pure functions. Pure functions never modify variables, but only create new ones as an output. (I cheated a bit in the example above: it goes along the lines of functional programming, but still uses a global list. You can find better examples, but it was about the basic principle here.)
Moreover, you can expect a certain output from a pure function with a given input. In contrast, an impure function may depend on some global variable; so the same input variables may lead to different outputs if the global variable is different. The latter can make debugging and maintaining code a lot harder.
There’s an easy rule to spot side effects: as every function must have some kind of in- and output, function declarations that go without any in- or output must be impure. These are the first declarations that you might want to change if you’re adopting functional programming.
What functional programming is not (only)
Map and reduce.
Loops are not a thing in functional programming. Consider these Python loops:
For the simple operations that you’re trying to do, this code is rather long. It’s not functional, either, because you’re modifying global variables.
Instead, consider this:
This is fully functional. It’s shorter. It’s faster because you’re not iterating through many elements of an array. And once you’ve understood how filter, map, and reduce work, the code isn’t much harder to understand either.
That doesn’t mean that all functional code uses map, reduce, and the likes. It doesn’t mean that you need functional programming to understand map and reduce, either. It’s just that when you’re abstracting loops, these functions pop up rather a lot.
When talking about the history of functional programming, many start with the invention of lambda functions. But although lambdas are without doubt a cornerstone of functional programming, they’re not the root cause.
Lambda functions are tools that can be used to make a program functional. But you can use lambdas in object-oriented programming, too.
The example above isn’t statically typed. Yet it is functional.
Even though static typing adds an extra layer of security to your code, it isn’t essential to make it functional. It can be a nice addition, though.
Some languages are getting more functional than others
Perl: Perl takes a very different approach to side effects than most programming languages. It includes a magic argument, $_, which makes side effects one of its core features. Perl does have its virtues, but I wouldn’t try functional programming with it.
Java: I wish you good luck with writing functional code in Java. Not only will half of your program consist of static keywords; most other Java developers will also call your program a disgrace. That’s not to say that Java is bad. But it’s not made for those problems that are best solved with functional programming, such as database management or machine learning applications.
Scala: This is an interesting one: Scala’s goal is to unify object-oriented and functional programming. If you find this kind of odd, you’re not alone: while functional programming aims at eliminating side effects completely, object-oriented programming tries to keep them inside objects. That being said, many developers see Scala as a language to help them transition from object-oriented to functional programming. This may make it easier for them to go fully functional in the years to come.
Python: Python actively encourages functional programming. You can see this by the fact that every function has, by default, at least one input, self. This is very much à la the Zen of Python: explicit is better than implicit!
Clojure: According to its creator, Clojure is about 80% functional. All values are immutable by default, just like you need them in functional programming. However, you can get around that by using mutable-value wrappers around these immutable values. When you open such a wrapper, the thing you get out is immutable again.
Haskell: This is one of the few languages that are purely functional and statically typed. While this might seem like a time-drainer during development, it pays off big when you’re debugging a program. It’s not as easy to learn as other languages, but it’s definitely worth the investment!
Big data is coming. And it’s bringing a friend: functional programming.
In comparison to object-oriented programming, functional programming is still a niche phenomenon. If the inclusions of functional programming principles in Python and other languages are of any significance, however, then functional programming seems to be gaining traction.
That makes perfect sense: functional programming is great for big databases, parallel programming, and machine learning. And all these things have been booming over the last decade.
While object-oriented code has uncountable virtues, those of functional code, therefore, shouldn’t be neglected. Learning some basic principles can often be enough to up your game as a developer and be ready for the future.