As a senior engineer writing Haskell professionally, this just isn’t really true. We just push side effects to the boundaries of the system and do as much logic and computation in pure functions.
It’s basically just about minimizing external touch points and making your code easier to test and reason about. Which, incidentally, is also good design in non-FP languages. FP programmers are just generally more principled about it.
I’m not sure what you mean by “locality of reference”. I assume you mean something other than the traditional meaning regarding how processors access memory?
Anyway, it’s often been said (half-jokingly) that Haskell is a nicer imperative language than imperative languages. Haskell gives you control over what executing an “imperative” program actually means in a way that imperative languages don’t.
To give a concrete example: we have a custom monad type at work that I’m simply going to call
Transaction
(it has a different name in reality). What it does is allow you to execute database calls inside of the same transaction (and can be arbitrarily composed with other code blocks of typeTransaction
while still being guaranteed to be inside of the same transaction), and any other side effects you write inside theTransaction
code block are actually collected and deferred until after the transaction successfully commits, and are otherwise discarded. Very useful, and not something that’s very easy to implement in imperative languages. In Haskell, it’s maybe a dozen lines of code and a few small helper functions.It also has a type system that is far, far more powerful than what mainstream imperative programming languages are capable of. For example, our API specifications are described entirely using types (using the servant library), which allows us to do things like statically generate API docs, type-check our API implementation against the specification (so our API handlers are statically guaranteed to return the response types they say they do), automatically generate type-safe API clients, and more.
We have about half a million lines of Haskell in production serving as a web API backend powering our entire platform, including a mobile app, web app, and integrations with many third parties. It’s served us very well.