
All Haskell programs start as
main :: IO ()
though... so they all get evaluated in the context of another IO () don't they?
True for most cases now, but historically false. Haskell existed and people wrote programs for years before the Monad class and IO were created. A Haskell98 program can be taken to have a "main :: IO ()", but that is not essential. Which is the point jcc made:
Well... Haskell compilers and runhaskell-style interpreters (not regular Hugs/ghci!) take the value of Main.main as `the program'. But that feels (to me --- I could be wrong) like an aspect of a particular hosted environment. REPLs can handle programs that aren't wrapped up in IO at all; and there's no reason why IO has to be the type of IO-performning-things in REPLs, either. You could just as well write a REPL that took, say, tangible values [http://haskell.org/haskellwiki/TV] as input instead, and displayed them. So it's more a matter of Haskell implementations can be given an IO value to run than that combining IO values together somehow runs them.
jcc