
Some people may suggest that you to create top-level IORefs using unsafePerformIO, but I don't recommend that for this situation.
Well I can't imagine which particular people you have in mind :-)
But, as a vocal advocate of sound support for top level mutable state, I would just like to go on record as saying I certainly would not advocate it for this problem.
But then again, I wouldn't advocate the use of explicit "entire program state" record passing either :-)
Fair enough. The main reason I suggested it is a fairly painless way to emulate global variables within a main control loop, which was the OPs stated goal. ("it's important to implement it in as most imperative form as possible...") I would personally attempt to adopt a more functional way of approaching the problem (Arrows and whatnot), but those are still pretty murky waters.