I am sorry for having mixed-up arguments (but who throws the first stone?...)

Jerzy seemed to suggest that the "impurity" of IO was somehow related to it
not supporting very many operations.
No, not really. I added

First, it is not true  that you can do with, say, (printStr "Ho!" ) whatever you want. In fact, you can do almost nothing with it. You can transport it "as such", and you can use it as the argument of (>>=).

after the message of Jake McA.

You can do whatever you want with them with no harmful effects in any Haskell expression.

This was an additional layer of bikeshedding, not exactly about purity.
Or, just a bit: the ONLY "real" operation on an action, i.e. (>>=)  produces side-effects... Other don't, but --

Again, here my point is that calling "pure" an entity which is  opaque and inert, is meaningless (or "redundant" if you wish...), this was all.

Jerzy K.

PS. Tom Ellis:

One could simply implement IO as a free monad
Interesting. I wonder how.