Why do developers fall in love with functional programming?

Functional programming (FP) has been around for 60 years , but until now it has always had a fairly narrow scope of use. While world-changing companies like Google rely on core concepts , the average modern programmer knows very little, if anything, about this phenomenon.



But that will change soon. More and more FP concepts are being integrated into languages ​​such as Java and Python . And more modern languages ​​like Haskell are fully functional. To put functional programming in simple terms







, then this is the creation of functions for working with immutable variables. In contrast, object-oriented programming is when a relatively constant set of functions is used, and the programmer is mainly busy modifying existing variables and creating new ones.



FP, by its nature, is suitable for solving urgent problems, such as data analysis and machine learning . This does not mean that you need to say goodbye to object-oriented programming and completely switch to functional programming. It is simply useful for the modern programmer to know the basic principles of FP, which will enable him to apply these principles where they can serve him well.



Functional programming is about eliminating side effects



In order to understand the principles of functional programming, you first need to understand what a "function" is. It may seem boring, but in the end, it will allow you to see what at first glance is imperceptible. So let's talk about functions.



A function, to put it simply, is an entity that converts some input passed to it into output that it returns to the call site. True, in fact, everything does not always look so simple. Take a look at the following Python function:



def square(x):
    return x*x


This function is extremely simple. It takes one argument, xwhich is probably of type int, and maybe of type floator double, and returns the result of xsquaring that.



And here's another function:



global_list = []
def append_to_list(x):
    global_list.append(x)


At first glance, it seems that it accepts xsome type and returns nothing, since there is no expression in it return. But let's not jump to conclusions!



The function will not be able to work normally if the variable is not declared in advance global_list. The result of this function is a modified list stored in global_list. Even though it is global_listnot declared as a value that is passed to the input of a function, this variable changes after the function is called.



append_to_list(1)
append_to_list(2)
global_list


After a couple of calls to the function from the previous example, there global_listwill be no longer an empty list, but a list [1,2]. This allows us to say that the list, in fact, is the value supplied to the input of the function, although this is not fixed in any way when the function is declared. This can be a problem.



Dishonesty when declaring functions



These implicit input or output values ​​have an official name: side effects. We're using very simple examples here, but in more complex programs, side effects can lead to real complications .



Think about how you would test the function append_to_list. It will not be enough to read the first line of its declaration, and find out that it needs to be tested by passing it some value x. Instead, you will need to read the entire function code, figure out what exactly is happening there, declare a variable global_list, and then test the function. What in our simple example, as it seems, does not cause any special difficulties, in programs consisting of thousands of lines of code, it will look completely different.



Fortunately, the above problem is easy to fix. You just need to be honest when specifying what exactly should go to the input of the function. The next version of our function looks much better than the previous one:



newlist = []
def append_to_list2(x, some_list):
    some_list.append(x)
append_to_list2(1,newlist)
append_to_list2(2,newlist)
newlist


We haven't changed much in this code. As a result, the function in newlist, as before in global_list, turns out [1,2], and everything else looks the same as before.



However, we made one significant change to this code. We got rid of the side effects. And this is very good.



Namely, now, after reading the first line of the function declaration, we know exactly what input data it works with. As a result, if the program does not behave as expected, you can easily test every function it contains and find the one that is not working properly. Pure functions are easier to maintain.



Functional programming is writing pure functions



A function that, when declared, clearly indicates what it takes and what it returns is a function with no side effects. A function without side effects is a pure function.



Here's a very simple definition of functional programming. This is writing programs that consist only of pure functions. Pure functions never modify the data passed to them, they just create new ones and return them. (Note that I cheated a little in the previous example. It is written in the spirit of functional programming, but in it the function modifies the global variable-list. But here we are only examining the basic principles of FP, which is why I did just that. If you want, here you can find stricter examples of pure functions.)



Further, when working with pure functions, you can expect that they, receiving the same data as input, will always generate the same output. And functions that are not pure may depend on some kind of global variables. As a result, they, receiving the same input, can produce different results, depending on the value of the global variables. This fact can significantly complicate the debugging and maintenance of the code.



There is a simple rule of thumb to detect side effects. Since when declaring pure functions, it must be clearly defined what they receive as input and return, functions that do not accept or return anything will not be clean. If you decide to incorporate functional programming techniques into your project, the first thing you want to do is probably check your function declarations.



What functional programming is not



▍Map and reduce functions



Loops are mechanisms that have nothing to do with functional programming. Take a look at the following Python loops:



integers = [1,2,3,4,5,6]
odd_ints = []
squared_odds = []
total = 0
for i in integers:
    if i%2 ==1
        odd_ints.append(i)
for i in odd_ints:
    squared_odds.append(i*i)
for i in squared_odds:
    total += i


With the help of this code, we solve simple problems, but it turned out to be quite long. And it, moreover, is not functional, since global variables are modified here.



And now - another version of this code:



from functools import reduce
integers = [1,2,3,4,5,6]
odd_ints = filter(lambda n: n % 2 == 1, integers)
squared_odds = map(lambda n: n * n, odd_ints)
total = reduce(lambda acc, n: acc + n, squared_odds)


This is fully functional code. It's shorter. It is faster because you don't have to iterate over many array elements. And, if you understand the functions filter, mapand reduce, it turns out that this code is not much more difficult to understand than the one in which loops are used.



This does not mean that in any function code is used map, reduceand other such functions. And this does not mean that in order to deal with such functions, you need to know functional programming. The point is that these functions are often used when getting rid of loops.



▍Lambda Functions



When people talk about the history of functional programming, they often start by talking about the invention of lambda functions. But while lambda functions are undoubtedly the cornerstone of functional programming, they are not the main cause of FP.



Lambda functions are tools that can be used to write programs in a functional style. But these functions can be used in object-oriented programming as well.



▍Static typing



The above example is not statically typed. But it is nonetheless a functional code sample.



Even though static typing adds an extra layer of security to your code, it is not a requirement for creating functional code. It can, however, be a nice addition to the functional programming style.



It should be noted that some languages ​​are easier to program in a functional style than others.



Some languages ​​are "more functional" than others



▍Perl



Perl has an approach to dealing with side effects that sets it apart from most other languages. Namely, it has a "magic variable" $_that brings side effects to the level of one of the main features of the language. Perl has its merits, but I wouldn't try to do functional programming in this language.



▍Java



I wish you the best of luck writing functional Java code. It won't hurt you. First, the keyword will take up half of the code static. Second, most Java programmers will call your code a misunderstanding.



This does not mean that Java is a bad language. But it is not designed to solve the kind of problems that functional programming is great for. For example - for database management or for developing applications from the field of machine learning.



▍Scala



Scala is an interesting language. Its goal is to unify functional and object-oriented programming. If this seems strange to you, then know that you are not alone. After all, functional programming is aimed at completely eliminating side effects. Object-oriented programming is about limiting side effects to objects.



With this in mind, we can say that many developers see Scala as a language that will help them move from object-oriented to functional programming. Using Scala can make it easier for them to transition to a fully functional programming style in the future.



▍Python



Functional programming style is encouraged in Python. This can be understood if we take into account the fact that each function, by default, has at least one parameter - self. This is, in many ways, in the spirit of " Zen Python ": "Explicit is better than implicit."



▍Clojure



Clojure, according to the creator of the language, is about 80% functional. All values ​​are immutable by default. But this is exactly what is needed to write functional code. However, you can get around this by using mutable containers, in which immutable values ​​are placed. And if you extract the value from the container, it becomes immutable again.



▍Haskell



It is one of the few fully functional and statically typed languages. While using it in the development process may seem like it takes too long to implement functional mechanisms, such an effort will pay off many times during code debugging. This language is not as easy to learn as others, but learning it is definitely a worthwhile investment.



Outcome



It should be noted that now is still the very beginning of the era of big data. Big data is coming, and not alone, but with a friend - with functional programming.



Functional programming, when compared to object-oriented programming, is still a niche phenomenon. True, if we consider the integration of FP principles in Python and other languages ​​as a significant phenomenon, then we can conclude that functional programming is gaining popularity.



And this makes sense, since functional programming shows itself well in working with databases, in parallel programming, in the field of machine learning. And in the last decade, all this has been on the rise.



While object-oriented code has countless benefits, you shouldn't discount the benefits of functional code. If a programmer learns some of the basic principles of FP, then this, in most cases, may be enough to improve his professional level. This knowledge will also help him prepare for a "functional future."



How do you feel about functional programming?






All Articles