Not long ago I wrote a bit of rant about things that annoy me about OOP. They were largely conceptual issues, but on the practical side it boiled down to how OOP concepts are largely unnecessary. In development, it's way too easy to write OOP spaghetti and people fight to keep things testable while spending most of their lives debugging the mutable global state.
Why functional programming (FP) can be better?
- Functions are functions, regardless of language. If you develop around functions, keeping them idempotent, then moving from language to language is simple. Functions are functions everywhere and as long as the languages support FP style development (aka functions are first class citizens) you won't run into surprises. No more 'interfaces mean just slightly different things in that language' or 'inheritance comes with a couple of gotchas' or classes mean slightly different things and they miss stuff. Sure, in Haskell you have real immutability and you may or may not have 'map' as a built-in tool but otherwise, it's a matter of whether or not there are curly braces or whether you need semicolons at line's end or not.
- Everything's testable - no side effects, it's all about transforming input to output with no surprises. No worries about mocking instances, whether member visibility comes in the way or not.
- Composition is at the core - yeah, OOP adopts it as well. No more entangled code that you can't really reuse in different projects.
- Immutability - sadly it's mostly up to developers to respect immutability but when you live by FP you know that there won't be surprises. Things don't change from under you in unexpected ways and code is mostly self documented (particularly if you design for totality)
- Composition, immutability and idempotency go a long way to make sure that FP programs are concurrency-friendly. Whether the language itself offers concurrency constructs (Golang, Erlang, Rust, etc) your approach already allows you to identify potential race conditions and to manage them in various ways.