
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