1) The IO action that does nothing.
2) The IO action that prints the string "Hello World" and then returns nothing.
3) The IO action that reads a string from the console, reverses it, prints it back, and then returns nothing.
4) ...
It's a common misconception to think that IO X is a wrapper around X with some "type system magic" to prevent it from being used outside IO. It's nothing like that. For example, IO String is not a wrapper around a String, doesn't contain any String inside, and cannot be converted to String. Meditate on the fact that System.IO.getLine is not a function, but a value of type IO String. Then you will understand why IO () has tons of possible values.