[Haskell-cafe] [Haskell] Re: Global Variables and IO initializers

Marcin 'Qrczak' Kowalczyk qrczak at knm.org.pl
Thu Nov 25 06:54:17 EST 2004


George Russell <ger at informatik.uni-bremen.de> writes:

> 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 at knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/


More information about the Haskell-Cafe mailing list