A monad encapsulates state
To do IO, you must carry around the state of the IO system, so all functions that perform IO must be in the IO monad. (If a function calls another function that performs IO, the called function must also be in the state monad.)
"do a <- m" -- will bind a to the output of the monad function m.
GHC has a library module (Control.Monad.State) which you can use to carry state.
You can write your own monads to carry state (instead of using
Control.Monad.State), in which case, you must "overload" the
(>>=) and return functions.
Further Reading:
Monads for the Working Haskell Programmer: http://www.engr.mun.ca/~theo/Misc/haskell_and_monads.htm
Monads for the Dazed and Confused: http://www.cs.fit.edu/~satkin/monads.html