[Haskell] Re: Global Variables and IO initializers

Ben Rudiak-Gould Benjamin.Rudiak-Gould at cl.cam.ac.uk
Thu Nov 4 11:20:47 EST 2004

Koen Claessen wrote:

 >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.

And I agree with this too.

 > | 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.

This is one of the several ways in which the current implementation of 
implicit parameters is broken. Clearly they *should* belong to the 
module namespace, and if we modify the implementation so that they do, 
the problem you describe here goes away.

 >(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.

I'm not convinced this is a problem either. All you have to do is use a 
single parameter (?MyModule.globals :: MyModule.Globals), where 
MyModule.Globals is an abstract type, and you've hidden your 
implementation as completely as if you had used unexported global variables.

 > | 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 don't think this is what he means. The original implicit-parameter 
paper suggested an extension of Haskell to support partial constraints 
in type signatures, e.g.

    pretty :: ... => Doc -> String

with the unspecified constraint being filled in by the type inferencer 
(section 5.4). I think the OP is proposing the same thing, except 
without the ellipsis: i.e. we just write

    pretty :: Doc -> String

and the compiler infers pretty :: (?width :: Int) => Doc -> String, or 
whatever. This actually sounds like a very good idea to me.

 >I have a different proposal.
 >Imagine a commutative monad, CIO. [...]
 >  newIORefCIO     :: a -> CIO (IORef a)
 >  newEmptyMVarCIO :: CIO (MVar a)

Adrian Hey proposed a "SafeIO" monad with similar properties to yours. I 
have the same objection to both of them: a whole new monad and a bunch 
of interconversion functions seems like overkill for such a minor new 
language feature. And I have the same counter-proposal: why not use 
(forall s. ST s)? It's not commutative, but I think it has all of the 
properties we need. In particular, it isolates initialization actions 
well enough that it doesn't matter what order they're run in, or even 
whether they're run at all. So importing a module doesn't have side 
effects, and init actions can be implemented easily using 
unsafePerformIO without affecting the semantics.

Note that the ST monad does not require higher-order polymorphism -- 
only the runST function requires that. ST is still useful without runST, 
as this example demonstrates.

-- Ben

More information about the Haskell mailing list