[Haskell-cafe] Re: [Haskell] Top Level <-

Adrian Hey ahey at iee.org
Mon Sep 1 09:02:33 EDT 2008


Ganesh Sittampalam wrote:
> On Sun, 31 Aug 2008, Adrian Hey wrote: 
>> Eh? Please illustrate your point with Data.Unique. What requirements
>> does it place on it's context? (whatever that might mean :-)
> 
> It requires that its context initialises it precisely once.

It's context being main? If so this is true, but I don't see why this
is  a problem. It's a happy accident with the unsafePerformIO hack
as it is, and part of the defined semantics for *all* hypothetical
top level <- bindings. Though to be more precise, the requirement
is that it may be initialised at any time prior to first use, but
never again (there's no requirement to initialise it at all if
it isn't used). Also ACIO monad properties guarantee that it's
always initialised to the same value regardless of when this occurs.
So I don't see the problem.

> Data.Unique is actually a poor example, as it is actually fine to 
> initialise it multiple times as long as the resulting Unique values 
> aren't treated as coming from the same datatype.

I just don't see what you're getting at. There's no problem here
and Data.Unique is not special. We don't even have to consider
whether or not it's OK to reinitialise these things unless the
programmer explicitly allows this in the API (which Data.Unique
doesn't). This is true for all top level <- bindings.

myCount :: MVar Int
myCount <- newMVar 0

In a hypothetical second initialisation, do you mean..
1 - myCount somehow gets rebound to a different/new MVar
2 - The binding stays the same but MVar gets reset to 0 without
     this being explicitly done in the code.

I assume you mean the latter (2). But either case seems like an
absurdity to me. No top level bindings randomly change halfway
through a program and MVars (I hope) are not prone to random
corruption (no need to suppose things are any different if they
occur at the top level).

> But equally it can be 
> implemented with IORefs,

Actually it couldn't as IORefs are not an Ord instance.

> so it's not a good advert for the need for 
> global variables.

Oh please!

We have to have something concrete to discuss and this is the simplest.
Like I said there are a dozen or so other examples in the base package
last time I counted and plenty of people have found that other libs/ffi
bindings need them for safety reasons. Or at least they need something
that has "global" main/process scope and so far the unsafePerformIO hack
is the only known way to get that and still keep APIs stable,sane and
modular.

Also, AFAICS going the way that seems to be suggested of having all this
stuff reflected in the arguments/types of API is going to make it
practically impossible to have platform independent APIs if all platform
specific implementation detail has to be accounted for in this way.

>> The real irony of your remark is that making APIs this robust is
>> practically impossible *without* using global variables, and you're
>> now saying that because they've done this work to eliminate these
>> constraints they now have to be held to account for this with
>> an absurd API.
> 
> I think there are two cases to consider here.
> 
> A Data.Unique style library, which requires genuinely *internal* state, 
> and which is agnostic to having multiple copies of itself loaded 
> simultaneously. In that case, there is no requirement for a 
> process-level scope for <-, just that each instance of the library is 
> only initialised once - the RTS can do this, as can any dynamic loader.
> 
> The other is some library that really cannot be safely loaded multiple 
> times, because it depends on some lower-level shared resource. Such a 
> library simply cannot be made safe without cooperation from the thing 
> that controls that shared resource, because you cannot prevent a second 
> copy of it being loaded by something you have no control over.
> 
> If the <- proposal were only about supporting the first of these 
> applications, I would have far fewer objections to it. But it would have 
> nothing to do with process-level scope, then.

The <- proposal introduces no new problems that aren't already with us.
It solves 1 problem in that at least there's no room for the compiler to
get it wrong or for people do use "dangerous things" when using the
unsafePerformIO hack. I think that is really the only problem that can
be solved at the level of Haskell language definition.

I also think we need to be careful about the use of the term "process".

IMO when we say the "process" defined by main, we are talking about an
abstract process that is essentially defined by Haskell and may have
nothing in common with a "process" as defined by various OS's (assuming
there's an OS involved at all). Perhaps we should try be more clear and
say "Haskell process" or "OS process" as appropriate. In particular
when we say an MVar or IORef has "global" process scope (whether or
not it occurs at top level) we are talking about a Haskell process,
not an OS process.

The issues you raise seem to me to be more to do with correct
implementaton on various platforms using various tools of varying
degrees of brokeness. So I don't really know what problems might
be encountered in practice. But whatever these problems might be
I don't think they can be fixed at the level of Haskell language
definition as the solutions are likley to be platform specific
"hacks".

But this problem is going to be with us whether or not top level <-
bindings are implemented (If they're not implemented people will
still be doing the same thing with the unsafePerformIO hack).

Regards
--
Adrian Hey



More information about the Haskell-Cafe mailing list