[Haskell] threading mutable state through callbacks
Jules Bean
jules at jellybean.co.uk
Thu Oct 7 08:28:02 EDT 2004
Hi,
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
trick...
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?
Jules
More information about the Haskell
mailing list