The code you sketch out above is basically the Writer Monad, which exists in the libraries, and does see use, but it's not IO -- as I mentioned above the semantics of IO don't entirely just fall out of the monadic structure.
But I find the computed-imperative-program description lends itself to thinking about IO values as values in a way that thinking about them as type system goop to tame side effects doesn't; for example, the program:
import Data.List (intersperse)
import Control.Concurrent (threadDelay)
main = sequence_ $ intersperse (threadDelay 1000000) (map print [1..])
will print out the natural numbers, one per second until you kill it. It works by constructing a (lazily evaluated) list of actions to perform, which alternately print out a number or wait for one second. sequence_ has type [IO a] -> IO (), and it just performs each action in the list in sequence (its type is actually a bit more general than this, but..).This kind of code is really weird to think about from the "tamed side-effects" model, but very natural from the "computing a script" model.