This quote describes perfectly the way my thinking has changed:
In fact, you can think of any impure function as having three "steps": 1) An "input" side effect,from actors-are-not-good-concurrency-model.Unit => B
, a pure function(A,B) => C
, and an "output" side effectC => Unit
. It makes perfect software engineering sense to decouple these components - and that is exactly what is done in purely functional programming.
One side benefit is that the core logic of the functionality is now perfectly testable; as pure functions are the easiest thing in the world to write tests for.