In my opinion, the motivation for functional programming begins with the following problem: how do we manage the complexity of programs in such a way where we can reason about them more effectively?

In your programming education, you may have heard of structured programming, which came about in the 1960s as an attempt to make reasoning about programs more effective. Even if you haven't heard of structured programming before, you have used for and while loops, and you might have been taught about the problems with using GOTO statements for control flow. The fundamental issue with GOTO statements is that it makes it difficult to reason about the program; it can sometimes be difficult to tell which path of execution led to specific states, especially in complex programs where control flow is jumping all over the place and where state is being modified throughout the program. This is why Edgser Dijkstra and other advocates of structured programming discouraged the use of GOTO statements and encouraged a style of programming where control flow is handled by function calls and looping constructs. Some go further, demanding that all control flow constructs have one entry point and one exit point. In practice, this means banning the use of break and continue statements.

Even when removing GOTO statements, there is still the problem of reasoning about how paths of execution in the program led to specific states. The modification of variables that were declared outside of the scope of the block of code modifying the variable can lead to hard-to-debug problems when done incorrectly. This also complicates testing, since it's difficult to test that one block of code as a unit; you need to take into account the variable that is outside the scope of the unit.

Enter functional programming. In pure functional programming, once a variable has been assigned a value, it cannot be modified. While this makes certain stateful computations harder to express, it has the benefit where functions have no side effects, which are modifications of state outside of the scope of the function. By avoiding side effects, the functions can be reasoned about more easily. This also has nice implications for parallel and distributed programming.

In practice, side effects are usually unavoidable since programs still need to perform state modifications such as printing output to the screen, writing to a file, etc. Typically functional programming languages offer escape hatches to handle these tasks. Most functional language programmers encourage a style of programming that separate "pure" and "impure" functions as much as possible, reducing the scope of "impure" functions in order to better reason about the program.

Functional programming can be thought of as the reification of the lambda calculus (https://en.wikipedia.org/wiki/Lambda_calculus), a mathematical expression of computation. There is a heavy emphasis on recursion as opposed to loop constructs, though thanks to tail recursion you don't have to worry about blowing up your stack.

There is a divide between dynamically-typed functional programming and statically-typed functional programming. The dynamically-typed functional programming language world is dominated by Lisp's derivatives, such as Common Lisp, Scheme, Racket, and Clojure. The statically-typed functional programming language world can be split into two categories: (1) those with strict evaluation such as Standard ML, Ocaml, and F#, and (2) those with lazy evaluation such as Miranda and Haskell.

Part of the reason why the functional programming language community has such a strong mathematical bent, especially in the statically-typed world, is because functional programming lends itself well as a vehicle for applying concepts from abstract mathematics to programming. There is a lot of interesting work being done on type systems, theorem-proving systems, formal verification, and other math-influenced work, with languages such as Standard ML and Haskell being the major tools for these researchers.

It can be a tough road getting used to functional programming at first, and some of the literature can be quite intimidating ("a monad is just a monoid in the category of endofunctors, what's the problem?"). However, if you are highly interested in learning about a programming world that encourages reasoning about your programs in an easier (and perhaps even provable) way, then I highly recommend learning about functional programming. Common Lisp/Scheme/Racket/Clojure will show you the beauty of Lisp's high degree of flexibility, while one of the statically-typed functional languages such as Standard ML or Haskell would show you that static types go far beyond what Java and C++ has to offer.