Here is a philosophical view of the whole thing. You can think of effectful computations as functions from a World to a World where a World contains everything that we consider mutable (including RAM, the disk, the internet and so on). For example, putStrLn takes a World and a string and produces a World which is exactly like the old one except that the string has been written to the standard output.
Of course, the two Worlds - the one in which the string hasn't been output yet and the one in which it has - can't coexist. That is, once you actually output the string you can't go back to the old World. This means that if you apply putStrLn (or any other "World transformer") to a World you can't reference that World any longer, only the one that putStrLn gives you as its result. The programming language has to ensure that Worlds are accessed linearly - if you have a World and apply a function to it, you get a new World and can't reference the old one. If this is guaranteed, then World transformers like putStrLn can be evaluated simply by modifying the one and only real world. This doesn't make the language impure because the program can't detect that the old World has changed - it can't reference it.
This kind of linearity can be implemented through linear types (which is what Clean does) or monads. In general, monads don't have anything to do with side effects. But there is one particular type of monad, called state transformer, which provides linear access to an encapsulated state. And there is one particular state transformer monad (called IO in Haskell) whose state is the World. The standard library defines primitive World functions (like putStrLn) based on this monad and programmers write their programs by composing these primitives in interesting ways. There is nothing deeply magic about it.
He has not acquired a fortune; the fortune has acquired him. -- Bion