The one core concept that makes functional programming different from anything else is "referential transparency". Referential Transparency is the property that you can substitute a call to a function with a given set of arguments with the result of that function in the program, and the behavior won't change at all.
To put it another way - when you call a function, its result has to be determined based *only* on the arguments you pass in, not any external state that might be floating about. It's not allowed to go and fetch an instance variable; it's not allowed to poke a singleton and grab state out of it; it's not allowed to print something; and it's not allowed to launch missiles.
What that gives you is:
1) It's very easy to test those functions - you define a set of arguments, you define a correct result, and you say 'call this, and you should get this result'.
2) It's very easy to parallelise these functions - since they're not affecting any outside state, no locking is necessary, you just run them side by side, and you're done.
3) It's very easy to debug these functions, since you have a defined input, and a defined correct output, you can sit there and easily understand what's going on.
4) it's very easy to reason about these functions, since you never read a chunk of code in them and go "god, what could all the values of this external state be?"
5) It makes code clearer, since you can easily document exactly what that bit of code is doing, without any reference to any external parts of the system, or any prerequisites.