Why functional programming?
As developers we want to write robust and correct code. This is our ultimate goal.
To reach this goal, we write tests that assure us of the correct behaviour of our code.
To make code testable, we define code in isolated units, that are also tested in isolation.
Dependencies between units should soley be described through dependency injection. A unit describes on what units it depends. If unit C depends on unit A and B, and A and B are tested to function correctly, then if we test unit C then we can be assured that A and B won't cause tests to fail suddenly as we know how A and B behave. This way of thinking is what we call unit testing and is one of the most important parts of Test Driven Development.
So what if we would limit a language to only be able to define units that can only depend on behaviour of well-functioning other units? So that it's output (its behaviour) is soley dependend on its input?
Well in mathematics we have a concept called functions. Here have one $ f(x) = 3x$. I'm sure you've used them in high school. Behaviour of functions is fully dependent on its input.. $f(5)$ will always yield $15$. No matter how many times you call it. If we compose functions, then one function is dependent on the behaviour of another function. Say we have $g(x) = x+2$ then $f(g(x))$ is $f$ with as input the behaviour of $g$. and because we know the behaviour of $g$ is well-defined, we know that $f$ well defned as well. $f(g(5))$ will always yield $21$, not matter how much you call it.
In mathematics, a function is a relation between a set of inputs and a set of permissible outputs with the property that each input is related to exactly one output.
So basically a function is exactly the definition of a unit and nothing more. It encapsulates exactly what we expect a unit to be. And this forms the basis of functional programming.
In functional programming, our programs are soley composed of functions, and nothing else. And this has a very powerful consequence. It means our code is unit-testable by default, because functions are exactly what makes a unit a unit.
Well-testable and well-tested software is easier to refactor, extend, and to trace back bugs in. This makes functional languages a great tool for robust software with an easy development cycle.