An insight that might help tie functional programming (FP) and imperative programming (IP) together is that FP is spreadsheets and IP is macros (edit: recordings of human interaction and shell scripts). Ideally all business logic should be FP and event-driven interfaces are usually IP. I think of these as clusters of similar concepts:

FP
synchronous blocking
immutable
process isolation
auto optimization
auto parallelization
higher-order methods
scatter-gather, fork-join
deterministic/repeatable (sometimes) reversable execution
models and views in MVC
ugly prefix/postfix syntax that doesn't look like algebra (not always)

IP
asynchronous nonblocking
mutable
shared state
manual optimization
manual parallelization
generators/iterators
threads, locks/mutexes
nondeterministic promises/futures requiring snapshots to reverse execution
controllers in MVC
pretty infix syntax that looks like algebra (not always)

I believe that ideally we'd write everything in FP with no IP whatsoever. So no mutable variables, no pass by reference, no iteration. Just higher-order methods and copy-on-write, with static analysis simplifying intermediate code into its fastest and/or most concise representation to run in parallel as fast as possible on multicore CPUs. Each component runs single-shot using analogs of STDIN/STDOUT/STDERR. These can fully represent models and views functionally and even declaratively.

Then IP would be used to wire up components, similarly to controllers in MVP. For example when a UNIX executable blocks waiting for more data on an input stream, or more room in an output stream. The runtime/filesystem/network/OS between isolated executables is imperative.

FP and IP can be unified by enforcing immutability everywhere in an IP language, then transpiling the code to reshape it between prefix/infix/postfix notation. Unfortunately I/O still can't be implemented without monads, making this psuedo-FP language impure. So there may be no way to build an entirely pure FP runtime that can respond to dynamic input/output and exceptional behavior. It would be like having a spreadsheet with no connection to the outside world, where the values of cells can't be edited, so formulas could run but they never have reason to do so.

Attempts can also be made to eliminate IP altogether by providing impure functionality in FP languages by introducing monads for I/O and exceptional behavior, allowing mutation by isolating it in blocks, etc. I believe that makes impure FP languages equivalent to IP, corrupting them and defeating the point of using FP. Sometimes that makes sense though if/when we can live with the tradeoffs, or if we're porting code from IP languages that's too difficult to refactor to pure FP. Unfortunately most impure FP languages are also difficult to read by humans, limiting their adoption.

Without the basic understanding listed here, the world seems to naturally head towards IP, since it most closely mimicks how humans navigate the world. IP languages tend to borrow FP concepts like monads to implement async behavior like generators, futures and promises. This sacrifices determinism and expands shared state to such a degree that IP programs can only grow to a certain size before they are intractable. This can sort of be avoided with reducers like Redux or writelog databases, usually at a cost in having to maintain boilerplate.

The world also tends to double down on static types, categories, templates/generics, formal contracts/interfaces, etc, borrowed from FP without a clear understanding of when or why they're necessary. When often programs can be written much more concisely and clearly by avoiding custom types altogether and focusing on data-driven standard types like numbers, strings and JSON run through transformation functions. An obvious example being how Java programs are often bloated and overengineered because they are object-oriented instead of functional. It's amazing to see how so much boilerplate often contains so little actual business logic. And how languages inspired by Java, like C#, often seem to repeat its shortcomings.

I think of all of this as analogous to how beginners tend to implement a game's main loop with state machines for each sprite. Whereas in Unity they can use coroutines. Once we see how straightforward it is to write business logic in a one-shot fashion, it's hard to imagine going back to juggling the complexities of state machines. Especially since state machines and coroutines can be made equivalent via generators.

Another example is that digital electronic circuits are equivalent to a spreadsheet and can be represented and analyzed by FP, but it's hard to convert an IP program description into a circuit or analyze its behavior formally using techniques from logic.

Once we have these kinds of insights, it's difficult to unsee them. And to look at best practices without asking ourselves why we're doing things the hard way.

As far as I know, the only language that approximates pure FP with IP glue handled by the runtime to avoid monads is ClojureScript. Admittedly, I'm probably misunderstanding how it works.