
One issue here is that I think there is often not enough clarity about the two different kinds of functional activity going on ie: 1) The actual construction of the monadic action using >>= and return (and fail) 2) The functional interpretation of the action thus constructed
I agree that this distinction is important, and the latter stage is where the context-sensitivity of i/o comes in - unlike all other uses of monads in Haskell, i/o actions are interpreted by an external entity (see my other email).
My current understanding of this is that lazy evaluation allows you to consider that these two different activities really do happen in the sequence above, the lazyness just acting as a helpful optimization that avoids wasting time constructing bits of action that are never going to get executed (ie lazyness pushes the future back into the present).
(the picture is slightly less simple, as 1/2 are repeated, hence construction and interpretation are interleaved, and later construction may depend on the results of earlier interpretation, but I assume you know that;) you don't actually need lazyness. you don't even need >>= to be non-strict in its second parameter, because that second parameter will be a continuation function, and even strict functional languages tend not to evaluate under lambdas (so wrapping expressions in extra lambdas or eta-extending functions to make their outer lambda explicit is a standard technique to delay evaluation in those languages). the standard papers do not make this very clear, but neither lazyness nor static typing are needed to add a monadic i/o system to a purely functional language (it took me a while to figure that out when I had to decide how to add an i/o system to a purely functional language without either feature..). cheers, claus