[Haskell] Re: Global Variables and IO initializers

Koen Claessen koen at cs.chalmers.se
Thu Nov 4 10:16:00 EST 2004


Benjamin Franksen wrote:

 | 1) I strongly disagree with ideas to execute IO actions
 | implicitly in whatever defined or undefined sequence
 | before or during main for whatever reasons.

I agree with the objections you make. Having full IO actions
as initialization actions might be a bit too much.

 | What I originally wanted to propose was therefore some
 | sort of source-to-source program transformation that adds
 | all the intermediate extra function arguments. Then I
 | realized that this is almost exactly what the so called
 | 'implicit parameters' extension to Haskell is all about,
 | and that using them as a replacement for global variables
 | has already been proposed by John Hughes.

The problem with John's approach is that it breaks
modularity. It does this in two ways:

(1) Whenever a module uses an implicit parameter like that,
it has to have a name that is different from all implicit
parameters used by any other (future) module. (Yes, implicit
paramers cannot be quantified by a module name.) This is
difficult to ensure.

(2) Having the implicit parameter breaks the abstraction
barrier. I might want to re-implement a module that does not
make use of global variables, into one that uses a cache or
hash-table or whatever (think BDD library), and not change
the interface of the functions that are provided.

 | What I've been asking myself is: Wouldn't it be possible
 | for the compiler to silenty add the implicit parameter
 | type constraints behind the scenes?

You would be back at square 1, since your program will still
look and behave exactly the same as a program that
implicitly executes all initializations; the only difference
is implementation.

I have a different proposal.

Imagine a commutative monad, CIO. Commutative monads have
the property that it does not matter in what order actions
are performed, they will have the same effect. In other
words, for all m1 :: CIO A, m2 :: CIO B, k :: A -> B -> CIO
C, it should hold that:

  do a <- m1             do b <- m2
     b <- m2     ===        a <- m1
     k a b                  k a b

Now, one could imagine an extension X of Haskell98, in which
modules are allowed to contain definitions of the form:

  p <- m

Here, p is a (monomorphic) pattern, and m is of type CIO A,
for some type A. CIO is an (abstract) monad provided in a
library module, just like IO is today.

One could wonder where the primitive actions in the monad
CIO come from? Well, library providers (compilers) could
provide these. For example:

  newIORefCIO     :: a -> CIO (IORef a)
  newEmptyMVarCIO :: CIO (MVar a)

And so on.

The implementer of these functions has to guarantee that the
actions do not destroy the commutativity of the CIO monad.
This is done in the same way as today, compiler writers and
users of the FFI guarantee that certain primitive operations
such as + on Ints are pure.

The FFI could even adapt CIO as a possible result type
(instead of having just pure functions or IO functions in
the FFI).

(One could even imagine a compiler feature that provides us
with a function:

  unsafeIOtoCIO :: IO a -> CIO a

But this could of course never be a part of the official
Haskell98 extension X!)

Comments welcome.

Kind regards,
/Koen


More information about the Haskell mailing list