
On Fri, 30 May 2008 15:23:46 +0100
Andrew Butterfield
Robin Green wrote:
I have been thinking about to what extent you could cleanly do I/O without explicit use of the I/O monad, and without uniqueness types (which are the main alternative to monads in pure functional programming, and are used in the Concurrent Clean programming language).
Suppose you have a main event handler function, like this:
eventMain :: (Event, SystemState AppState) -> (Command, SystemState AppState)
This function could be called over and over in an event loop, until an EndProgram command was received, and the event loop would itself do all the actual I/O (the SystemStates are only in-memory representations of some part of the system state, plus the application's own state). Things like disk I/O could be done with commands which generate events when complete. Interprocess communication could be done in the same way.
Then eventMain, and everything called by it, would be referentially-transparent, and yet non-monadic. You could of course build higher-level stuff on top of that.
Given the above, without uniqueness typing, and because there is clearly no monad, I could write
breakMain :: (Event,Event,SystemState AppState) -> ((Command,SystemState AppState),(Command,SystemState AppState)) breakMain (e1,e2,sys) = ( eventMain (e1,sys) , eventMain (e2,sys) )
Now what happens? Do we get two copies of SystemState ?
Simpler still, I can write (sys,eventMain e sys) -- what happens here? I have references to both before- and after- state.
Yes, you do - but they're only in-memory representations. Sorry, I didn't fully explain what I meant. Two points: 1. The SystemState record only contains in-memory representations of *some* parts of the system state - e.g. on an embedded system they could be the on/off status of the LEDs, motor speeds, the state of the toggle switches, which buttons are currently being pressed, etc. It would be infeasible to record the entire state of, say, an attached 120GB hard drive - and even less feasible to record the state of the external environment - so only some parts of the system would be covered by this data structure. 2. It's the event loop's job to do any necessary I/O to update the *actual* system state to match the SystemState returned by eventMain (ignoring any changes which are impossible, e.g. if the program tries to say a toggle switch is on when it isn't). As I said, only in the event loop is any I/O actually performed. So when you evaluate breakMain or whatever, nothing happens - it's just manipulating representations. You can only return one SystemState from eventMain, and that is used to update the real system state - so there's no paradox. -- Robin