
Hi,
On 23/11/06, Benjamin Franksen
One answer is in fact "to make it so that Console.Write can be rolled back too". To achieve this one can factor the actual output to another task and inside the transaction merely send the message to a transactional channel (TChan):
So, you could simply return the console output as (part of) the result of the atomic action. Wrap it in a WriterT monad transformer, even. (one, console) <- atomic $ runWriterT $ do tell "hello world\n" return 1 putStr console (Not terribly efficient, but you get the idea.) You're just calculating what output to make inside the transaction; the actual outputting happens outside, once the transaction commits.
Another task regularly takes messages from the channel
With STM, the outputter task won't see any messages from the channel until your main atomic block completes, after which you're living in IO-land, so you might as well do the output yourself. Pugs/Perl 6 takes the approach that any IO inside an atomic block raises an exception.
Unfortunately I can't see how to generalize this to input as well...
The dual of how you described the output situation: read a block of input before the transaction starts, and consume this during the transaction. I guess you're not seeing how this generalises because potentially you won't know how much of the input you will need to read beforehand... (so read all available input?(!) You have the dual situation in the output case, in that you can't be sure how much output it may generate / you will need to buffer.) input <- hGetContent file atomic $ flip runReaderT input $ do input <- ask -- do something with input return 42 (This is actually a bad example, since hGetContents reads the file lazily with interleaved IO...) later, /Liyang