People say it's elegant, but to understand why, you might have to take a whole online course, read an entire book, decrypt a research paper, or study a whole functional language.
I've found some nice articles and Q&A threads here and there, but the info is scattered. Also, I'm not happy with the examples given. I wanted something "stupid" and informal.
This is my good faith effort to summarize the main characteristics and benefits of functional programming. I include examples that favor easier understanding over real-world usefulness, with hope that you'll be able to apply functional programming to your own use cases.
What is functional programming?
Functional programming means functions are everywhere. For example, functions can be passed as arguments and can be return values.
A function is something that you "call" to get results. When you call a function with the same parameters, it always returns the same result.
Data in functional programming is immutable, because if data can change between function calls, the function can't guarantee to return the same result.
Why is functional programming good?
You would probably agree that reuse is good. Abstraction leads to reuse. In functional programming, there are more ways than non-functional programming to abstract stuff.
Since there are a lot of ways to abstract stuff, you get to use abstraction a lot. When you use it more, you get better at it.
When you use abstraction a lot, you tend to think about solving problems in a higher level. Your code looks more like solving a problem than telling the computer to do things. In other words, your code is more declarative.
In short, I think functional programming makes you a better programmer, and that's why it's good.
Common functional programming features
When functions are everywhere, these common features follow naturally.
Functions can be saved to variables.
You would save a number to a variable.
You can save a function to a variable too.
Functions as parameters.
You would pass an object as a function parameter.
You can pass a function to a function as well.
As you can see, non-functional programming allows you to abstract over data (what kind of meat to cook), but functional programming allows you to abstract over actions (how to cook a given type of meat).
Functions as return values.
You would return an object from a function.
You can return functions as well.
Sometimes, you don't need to name all of your values. Instead of this.
You would rather write this.
Same for functions. Sometimes, you don't need to name all your functions. Instead of this.
You would rather write this.
When functions can be used everywhere, we need to decide what the function bodies can see.
It turns out that it's useful when a function can see variables defined outside its body.
Suppose we want to make some sauce from some ingredient.
Every time we want to make some secret sauce, we would then have to pass the secret ingredient.
But if we can define functions that can see outside variables, we would be able to hide the secret_ingredient.
And if you need to make secret sauce many places in the code, you won't need to pass secret_ingredient along with them.
Functions coupled with data are called closures. This is another way programmers can hide data. It's called encapsulation, which is another form of abstraction.
Let's make dinner.
Suppose we want to make
1) pork over white rice.
2) fish over white rice.
3) pork over brown rice.
4) fish over brown rice.
We could write a function like this.
However, you can see that white rice is used twice and brown rice is used twice. We can refactor the function like this instead.
The benefits will be clearer if you made 10 more dishes with white rice. The example above allowed you to not have to use pass white_rice every time you cook white rice dishes.
Passing one parameter at a time on a multiple argument function, to create new functions along the way, is called currying.
The function implementation above might look a little weird, but some programming languages support currying, where you don't have to explicitly return a function from a function.
Currying is also another kind of abstraction: abstracting away parameters.
Since data is immutable in functional programming, you can't really loop from i=0 to i=100, because you have no way to increment i.
However, when you think about it, one of the main reasons we loop, is to process a data structure. We want to do something to each "node" in a data structure. This forces us to think at a higher-level, which is good because it maps better to the problem we're trying to solve than to how a computer works.
Moreover, when data structures are more complex, recursion generally gives simpler code to reason about. You can easily loop over a list, but what about a tree or a graph?
The code written with recursion usually doesn't have any "boilerplate code" such as setting up the variable i to have the correct value.
Recursion is another exercise that forces you to think high-level, which makes you a better programmer.
In functional programming, you apply the idea of using functions to everything. These are some high level descriptions of other features often found in functional programming. Since I want to keep this post "stupid", I'll not dive into the details of these features.
Calling functions on types
When you can call functions on types to create new types, you have algebraic data types and generics.
Determining which function is called
You can create objects with different constructors. At some point, you need to determine which constructor was called to create a given object and extract data from that object. Pattern matching is a nice way to do that. And actually pattern matching has more general uses than this too.
Since functional programming guarantee that functions called with the same parameters always give the same results, we have freedom in the order of evaluation. In a nutshell, we can be "lazy" and evaluation expressions only if they are used. This has many benefits.
Functional programming gives us more ways to abstract stuff. It also forces us to think at a higher level. These make us better programmers.
One thing to watch out for is trying to be too clever and apply these abstractions everywhere, even when not needed.
As an extreme closing example, if you wanted to get 0, you would just write this.
I hope you don't write something like this.