
I finally understood that George Russell's Library is not really about global variables. Rather it is about what I want to call 'execution contexts', which are -- as Marcin Kowalczyk observed -- a restricted form of dynamically scoped variables. [NB: Another (maybe better) name would have been 'execution environment' but the name "environment" is too heavily associated with the related concept of process environment (the string to string map given to user processes as an implicit argument).] An execution context is a mutable finite map from types to (monomorphic) values. Each IO action implicitly carries exactly one such map and by default passes it on to the actions that follow. A function is provided to (implicitly) create a new mapping and run a given IO action with the new mapping as its execution context, instead of the default one. [NB: I also understand now why the library uses ThreadIds. This was obscure to me at first because in principle all this has nothing to do with concurrency (beside the requirement that accessing the context should be thread safe). ThreadIds are used simply because they are available as an index and nothing else is. Its just a hack.] Seen this way, the whole thing smells very much of monads. Indeed, the monadic implementation is trivial. I attached a proof-of concept implementation, using George Russel's 'Dict' as an abstract data type in a separate module (copied verbatim from GlobalVariables.hs, see attached file Dict.hs). The idea: we define type Context = MVar Dict and introduce an eXtended version of the IO monad type XIO a = StateT Context IO a together with a small number of simple functions that implement the same interface as the original GlobalVariables.hs; no unsafe operations are used, everything is Haskell98 + Dynamics. Also ThreadIds do not appear and it is not necessary to change forkIO (apart from lifting it, of course). (code is in ExecutionContext.hs) I modified George's test program so that it works with ExecutionContexts. The program is completely isomorphic to the original (and does the same, too ;). The only major difference is that all IO operations are lifted into the XIO monad. Again, almost everything is Haskell98, -fglasgow-exts is only needed to derive Typeable (which can also be done manually). (Code is in TestExecutionContext.hs) The only task that remains to support this programming style so that it can be used practically, is to redefine IO as XIO in the kernel libraries. The annoying liftIOs everywhere (and the necessity to invent higher order lifts along the way) would be gone. I am almost sure that even the trick of indexing the dictionary via types (and thus the dependency on Data.Typeable and ghc extensions) can be avoided with a little more effort. Ben