[Haskell-cafe] I/O without monads, using an event loop

Robin Green greenrd at greenrd.org
Fri May 30 10:45:36 EDT 2008

On Fri, 30 May 2008 15:23:46 +0100
Andrew Butterfield <Andrew.Butterfield at cs.tcd.ie> wrote:

> 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.

More information about the Haskell-Cafe mailing list