
I prefer to think about IO as an abstract data type of atomic "actions" i.e. the IO primitives (which we can extend via the FFI). The "run time system" to me is a black box that "executes" these actions. A Haskell program combines abstract IO primitives into a larger and more complex action using IO's bind and return, and it does so in a purely functional way. Evaluation order is completely irrelevant to this, because what matters is the result, not how we arrive at it. The bind operator instructs the run-time system to execute its left hand side, resulting in a value to be passed to the right hand side, which is then evaluated (in a purely functional way) to yield the next action etc. There is nothing mysterious about this IMO. If you have a working model for each of the IO primitives, this gives you a working model of what a complete Haskell program does. Cheers Ben