[Haskell] Re: Global Variables and IO initializers
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
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.
More information about the Haskell