Monads don't make side effects in I/O go away, but they do help you manage them. Crucially, the IO monad describes a computation which must then be run. Computations are describable in side-effect-free code. Running them is handled automatically by the Haskell runtime, and can be done without introducing side effects into the language.