Also, isn't it possible to describe pretty much any non-pure function like a function of the real world? For example, can't we think of, say, C's malloc as being a function that takes a RealWorld and an Int and returns a pointer and a RealWorld, only just like in the IO monad the RealWorld is implicit?
For sure ...
The whole idea of functional programming is to describe programms as a combination of small, independent calculations building up bigger computations.
Having these independent calculations, you'll have lots of benefits, reaching from concise programms to efficient and efficiently parallelizable codes, laziness up to the the rigorous guarantee that control flows as intended - with no chance of interference or corruption of arbitrary data.
Now - in some cases (like IO), we need impure code. Calculations involving such operations cannot be independent - they could mutate arbitrary data of another computation.
The point is - Haskell is always pure, IO
doesn't change this.
So, our impure, non-independent codes have to get a common dependency - we have to pass a RealWorld
. So whatever stateful computation we want to run, we have to pass this RealWorld
thing to apply our changes to - and whatever other stateful computation wants to see or make changes has to know the RealWorld
too.
Whether this is done explicitly or implicitly through the IO
monad is irrelevant. You build up a Haskell programm as a giant computation that transforms data, and one part of this data is the RealWorld
.
Once the initial main :: IO ()
gets called when your programm is run with the current real world as a parameter, this real world gets carried through all impure calculations involved, just as data would in a State
. That's what monadic >>=
takes care of.
And where the RealWorld
doesn't get (as in pure computations or without any >>=
-ing to main
), there is no chance of doing anything with it. And where is does get, that happened by purely functional passing of an (implicit) parameter. That's why
let foo = putStrLn "AAARGH" in 42
does absolutely nothing - and why the IO
monad - like anything else - is pure. What happens inside this code can of course be impure, but it's all caught inside, with no chance of interfering with non-connected computations.