
[I agree with your points, but...] Adrian Hey wrote:
I've pretty much convinced it's wrong. There should be one and only one "main" from which all subsequent IO activity derives. But creating internal state in the form of mutable data structures is not an IO activity. It just so happens that at the moment the only way to do this is newIORef :: a -> IO(IORef a), but this need not be so (readIORef and writeIORef are IO activities, but newIORef isn't).
I find this point rather slippery. 'newIORef' is a unique-name-provider, but 'unique over what'? When a module is imported twice, should it not create new unique names?
Indeed they do, unfortunately. In fact it was this very problem that lead me to conclude the top level mutable state is not only not "evil", but is a necessity. Having to call explicit initialisation code is a big problem in complex systems. Exactly who is responsible for initialising what? and in what order?
Maybe I'm being old-fashioned here, but isn't that a *fundamental* problem, that can't be automatically solved? Isn't it just a fact that in a complex system you, the programmer, have to make some decisions about who is responsible for acquiring resources?
If it's necessary that there is only one libWhateverState to preserve safety properties (because the state must be kept in sync with what's really being done to "the world", of which there is also only one) then what's the point of making it a parameter (the lib is not truly parameterisable by that state).
A slightly irrational point of concern in my mind is that by encouraging this practice, we encourage lazy library design. A large proportion of libraries in fact *can* be written as "reentrant" in this sense, and truly are parameterisable by their state. Only a few are not: those which actually manage connections to some "real" entity which is in fact unique. Of course that doesn't mean the problem doesn't exist. Jules