[Haskell] threading mutable state through callbacks

Jules Bean jules at jellybean.co.uk
Thu Oct 7 08:28:02 EDT 2004


I've been playing with some haskell bindings which basically bind C 
procedures in the IO monad. To write 'interesting' programs, you often 
need to manage your own state in addition to the implicit IO state.

I have been allocating references with newIORef and then passing them 
around all my functions. But having to thread a parameter through all 
your functions is inconvenient (and fragile). I can see the solution to 
this problem in layering a state monad over the IO monad, and I can 
just about grok the syntax I need to use StateT, with a nice 
encapsulated environment record type and nice selector functions to use 
with gets. Now of course I don't even need to use IORefs any more since 
I can be more explicit and precise about my state and how it's 
manipulated. (this is potential even a useful feature, since I can step 
back to earlier time points just by saving the state, etc.)

Unfortunately, it's not going to work. It's not going to work because 
some of the procedures take callbacks, and the callbacks are values of 
type IO (). I can see two solutions to this:

a) revert to using an IORef, and use lexical scoping to define my 
callbacks in a location such that the reference to the environment is 
in scope.  This will work, but it's just not convenient to define all 
your functions inside another function so you can do a lexical scoping 

b) write the callbacks as values of type

StateT Env IO ()

and then use the following 'environment binder'

bindEnv env s = do { x <- readIORef env ; s' <- execStateT s x; 
writeIORef env s'}

when I pass them to a callback. (which works fine as long as I have 
used lexical scoping to make sure env is visible at the binding site)

Is this the right approach? Is there a better one?

 From the library perspective, would it be cool if library callback 
routines, which currently have a type like

addCallback :: IO() -> IO()

could be given the type

addCallback :: MonadIO() -> IO()

So that they'll let you add callbacks for any monad layered over IO. Or 
doesn't this idea work?


More information about the Haskell mailing list