Isn’t this IO thing a bit overused? There are more FP languages that are not as pure as Haskell, but even if we only do IO through a special monad it is not too bad at all.
And I say that as someone who is very much fore “practical FP” — I do think that local mutability is sometimes simply the better fit for a problem.
> there are more FP languages that are not as pure as Haskell
I mean, that's precisely the point I'm making, that IO doesn't fit into the FP paradigm and that languages that make IO easier necessarily deviate from it. And once you bring non-FP concepts into an FP language, most people will reasonably question why do that instead of the easier way, which is bringing FP concepts into imperative languages.
Pure functional languages model IO with monads, which are very much functional (function composition within a context) and IMO easy to use. This ease of use is probably why JS adopted the pattern with native promises -- async/await. They fit well into otherwise imperative code and people seem to to like them.
Pure functional languages can very well have normal IO. It's Haskell's lazyness that mostly forced it to use monads for IO, for better or for worse. In a pure FP language with strict evaluation semantics, IO can easily be implemented as a pure function foo -> (World, Input) -> (World, Output).
Arguably the best platform for the general io use case is functional.
From how when you console in to a cluster and run a command on another node, the standard io is redirected to you, to how tcp/up and up sockets are managed by the stdlib, it's really hard to beat Erlang.
And I say that as someone who is very much fore “practical FP” — I do think that local mutability is sometimes simply the better fit for a problem.