[Haskell] Re: Global Variables and IO initializers

This is funny. When I got no immediate reaction from you, I started implementing it myself. I ended up with something similar. It has less features but is also a lot simpler. This is the interface:
initGlobal :: Typeable a => a -> IO () getGlobal :: Typeable a => IO a
Your implementation is probably much simpler than mine because you don't implement withEmptyDict. I'm really quite keen about withEmptyDict, because one of the MAJOR conceptual problems I have with unsafePerformIO global variables is that you only get one universe, corresponding to the Haskell program. There shouldn't really be a single "the Haskell program" anyway; imagine something like GHC or an operating system written in Haskell which run sub-systems which require their own global variables. Or imagine a program split between lots of processors where, for efficiency reasons, you don't want everyone to have to refer to the same set of global variables.o
Storing (TypeRep,Dynamic) pairs is redundant, since Dynamics already contain their own TypeRep (that is how they are made to work).
It is, but I'm not sure if it can be avoided without using stuff not in the standard libraries.
I also use a list for the dictionary; and I share your view about TypeRep badly needing an Ord instance (probably trivial to provide but I could be wrong).
Even better would be a hashable integer. TypeRep actually is implemented internally on GHC using a hashcons'd unique integer, so exposing it should be trivial ...

George Russell
Your implementation is probably much simpler than mine because you don't implement withEmptyDict. I'm really quite keen about withEmptyDict, because one of the MAJOR conceptual problems I have with unsafePerformIO global variables is that you only get one universe, corresponding to the Haskell program.
I think global variables are a lot less evil if they behave as if they were dynamically scoped, like Lisp special variables. That is, there is a construct which gives the variable a new mutable binding visible in the given IO action. It's used more often than assignment. Assignment is still available though. In Common Lisp implementations these variables are not inherited by threads: each thread starts with toplevel bindings of dynamic variables. I think this is wrong and they should be inherited. In my language Kogut they are inherited. With threads it makes a difference that the variable gets a new binding, not just a new value. The old binding is still mutable by threads which have not shadowed it. When the scope of the new binding finishes, the value restored in this thread might be different than the value from the time the scope was entered, if other threads have changed it in the meantime. In Haskell it would be a new kind of reference, parallel to IORef and MVar. In principle dynamic variables need not to be defined at the toplevel. In Lisp they are effectively always toplevel variables (even if declared locally); in my language they can be created in arbitrary places, e.g. as fields of objects. But usually they are toplevel. It would be pointless to *not* have toplevel dynamic variables, because their purpose is to avoid manually threading them through all actions which need them. This is an alternative design to Haskell's implicit parameters. It's different in that it applies to the IO monad only (dynamic variables obviously can't be read from pure code) and that the fact that an action uses a particular variable is not reflected in its type. Their primary use is to provide a default setting used in deep places in a computation, with the assumption that usually a single setting applies to the whole computation started from a given place. Like the random number generator, the default output handle (not the *internals* of stdOut as a statefulobject, but binding the stdOut variable to different handles, not possible in Haskell), the current locale or individual settings implied by the locale (I don't know yet how "inheritable" settings should be designed, like the locale as a whole and its parts). -- __("< Marcin Kowalczyk \__/ qrczak@knm.org.pl ^^ http://qrnik.knm.org.pl/~qrczak/

On Thursday 25 November 2004 10:02, you wrote:
This is funny. When I got no immediate reaction from you, I started implementing it myself. I ended up with something similar. It has less features but is also a lot simpler. This is the interface:
initGlobal :: Typeable a => a -> IO () getGlobal :: Typeable a => IO a
Your implementation is probably much simpler than mine because you don't implement withEmptyDict. I'm really quite keen about withEmptyDict, because one of the MAJOR conceptual problems I have with unsafePerformIO global variables is that you only get one universe, corresponding to the Haskell program. There shouldn't really be a single "the Haskell program" anyway;
Doesn't that run contrary to Adrian Hey's "oneShot" example/requirement?
imagine something like GHC or an operating system written in Haskell which run sub-systems which require their own global variables.
Well, that's indeed one major problems with global variables. Sure, you can try to solve it with multiple dictionaries, but that makes understanding what a certain part of the program does even harder. How do I find out what dictionary a write or read to a (no longer global) variable refers to? Furthermore, I have great difficulty in understanding why different threads need different dictionaries. Could you explain why this is useful, or rather, more useful than a global single dictionary? It reminds me of the usual "thread-local" variables that are offered by most systemlevel thread libraries. I think they put them in there so that they can easily port non-reentrant libraries (i.e. ones that use global variables internally) to a multi-threaded setting without changing their APIs. This approach leads to libraries that are extremely inconvenient and dangerous to use. Their existence is one of the reasons why I have been arguing so much against global variables.
Storing (TypeRep,Dynamic) pairs is redundant, since Dynamics already contain their own TypeRep (that is how they are made to work).
It is, but I'm not sure if it can be avoided without using stuff not in the standard libraries.
What non-standard libraries have I used (that you don't)? Ben
participants (3)
-
Benjamin Franksen
-
George Russell
-
Marcin 'Qrczak' Kowalczyk