1. OOP is not necessarily bad.
2. The original OOP should be Smalltalk-like languages (messaging), but the term OOP we refer today basically related to Simula-like languages (inheritance, polymorphism).
3. OOP and FP are not exclusive. Language like Scala you can have a 'case class Stack[A]' with a method 'def push[A](a: A): Stack' returns new stack. First, it's a class and be able to have methods, inheritance etc. Second it's an ADT (Algebraic Data Type) and without any function with size effect. So both OOP and FP still makes perfect sense.
4. Main stream OOP languages design are mostly terrible. (Basically the 'Blub language' according to Paul Graham, I'll call those language Objective-Blubs because I want it to be a mainstream OO language without hurting someone).
5. There are a lot of arguments against Objective-Blub or OOP itself are related to state, side-effect etc. These are partially true. But I can't say stateless and pure is totally better than other approaches, I just feel better when I'm tackling complex domain so code does not affect each other in a crazy way. However if the whole program is pretty easy to make sense for you. For FP approach there is no obvious advantage over Objective-Blub. In this case choose the one with higher velocity.
6. The core problem that Objective-Blub to me is not simple state or side effects. It's the wrong/implicit modeling, if you are not modeling a problem, 9/10 you cannot address the problem. It's would bite you again and again. For small application these are fine, for large application these omitted modeling could be a real problem. But there are so few people mention this explicitly, please allow me to list some of them here:
a. Quick example - NullPointerException: Not modeling nullable concept makes you handle null everywhere. By explicit modeling Optional/Maybe/Some it could be resolved.
b. In Objective-Blub objects are reference type by default: Which means it's assume you only use the object in this specific machine and thread. Objective-Blub programmers always complain about object-relational impedance mismatch, and then they blame SQL. But the real problem is Objective-Blub itself. For Objective-Blub you have to throw away all the methods and references. Not only SQL, JSON or other serialization is also a big deal. If you use FP languages you just map tables to an ADT which is not a big deal. There's no assumption on identity on FP language at all, if you want an entity, just give it an id field. That also explained why FP languages is more concurrent friendly, because same data on different machines are still same data.
c. Sub-type polymorphism does not let programmer do the correct modeling easily: ADT have sum and product type. That's how you describe domain types, it's so straight-forwarded. Done. While languages like Objective-Blub have class and enums respectively, but no parametric enums. You have to write less obvious code to model simple concept like 'Payment = CreditCard(no, cvv) | Cash(amount) | FreeCoupon'
d. Sub-type polymorphism means strong assumption: This is so called inheritance, which encourage you do strong assumption like a Person extends a Head, which works but implicitly indicates a Person is a Head. This is ridiculous but if you convert Person and Head to some business type and services you can find too many code bases having this problem.
e. Sub-type polymorphism is under-powered than other polymorphisms: Less powerful is good when it's enough. But it's a huge problem when it's under-powered, which means you have to hack all the way round - like with old day Java, if you loop through an array, you have to cast type to every element. This is insane for a verbose statically typed language. Language should focus on simplify parametric polymorphism like OCaml or Haskell does to guide programmer choose a better way by default. Statically typed FP languages usually model effect with parametric/ad-hoc polymorphism, which could be a huge deal of separating dirty world concerns - like in Scala you can have your domain service accept F[_] as effect type parameter without knowing what this effect would be, it could be database IO or random generator or totally different abstract things.
f. Methods are everywhere: In Objective-Blub you have to write all methods in same class. So if a class having 100 methods is considered a code smell. And someone refactored them, move some of them to other classes and extract some of them to a new class. That's typically one reason why large code base are so hard to read, because typically in FP languages it's no big deal when you have hundreds of function operates on the same type, you just split them in several files if you want. For those extracted class and moved methods, they largely blurred how domain type and logic should be, because you have to create a new concepts, or move concepts to other places just because the old concept is too big.
g. Less expressive also blurred things: For the first time I was looking at a code base with design patterns everywhere I was so confused. Most patterns are the problem that language is so constraint that it cannot express simple idea, like TypeScript allow Partial in constructor it would eliminate most of the builders. Like singleton and static properties could be addressed with Scala object. When a code base is filled with BarFactoryBuilder etc, that means the language itself does not modeling these common patterns at all. This make people(both reader and writer) fighting with languages instead of just expressing domain business.