There are a million features and defaults in C# that constantly undermine a programmer's attempts to create good compositional code. If you and your team is disciplined enough to avoid falling into the traps, that's good. But a lot of teams are not that disciplined and tend to churn out the same buggy OO code with the same subtle bugs caused by the same default semantics.
Also, F# is a pretty powerful language for language nerds. There's a reason there are literally half-dozen F# to JavaScript compilers, not to mention Erlang, CUDA, PHP (I know), WASM, etc.
Until they start removing features from C# (Maybe something like a strict mode), it will never be F#.
Agree. Mark Seeman calls these the "Functional pits of success"
https://www.youtube.com/watch?v=US8QG9I1XW0
I think the toughest thing about F# is that it doesn't offer as many Google-able easy solutions, so I'm stuck using it for utility and client libs at work. Trying to get a whole team onboard is a non-starter. It takes a good 6-12 months to adjust the mindset and most people are just looking to come into work, find their tough questions on StackOverflow and head out for the day.
For example, I may use a List at first, as it's natural to start with and offers niceties with [H|T] pattern matching or List.filter() somewhere, then if I decide to use a .Net List (ResizeArray) or Array or Set instead, I'm screwed. The pattern matching needs to either change syntax or go away, and all the List.Foo() calls need to change to Array.Foo's or whatever -- or I have to use Seq.Foo() everywhere to get extensibility even I don't want to pay for lazy evaluation. Long time F# fan here! -- but still some sore points.
Most of our platform infrastructure code is imperative, but as you get higher up in the abstraction hierarchy, you will start to see more things declared as functional or data-driven. We would really like for everything above our service/platform layer to be functional, but there are some areas where it is a better engineering choice to go imperative (at least for now).
I have always thought of imperative vs functional as a very mutual thing. The imperative code is the glue between your fictional/business reality (functional world) and the real world (CPU/OS/Networks/etc). Functional code can serve as a perfect model of the business if the imperative code (platform) supporting it is well-engineered.