Re: [Haskell] Re: Global Variables and IO initializers

I might really want to call the initialisation twice. If you use global variables, the library can only be initialised once... but what if I really want to use the library twice? With the state in a type passed between functions, you can have multiple different states active at once. Keean. Benjamin Franksen wrote:
Yes, whenever possible I would use this approach. Unfortunately, there are libraries (or just modules) that need to do some IO action in order to produce the (A)DT. In this case it _will_ make a difference how often you call it. But then this is just how IO actions are by nature, isn't it?
Ben

On Sunday 07 November 2004 17:41, Keean wrote:
I might really want to call the initialisation twice. If you use global variables, the library can only be initialised once... but what if I really want to use the library twice? With the state in a type passed between functions, you can have multiple different states active at once.
Yes, exactly. Just as you might, in fact, *want* to call putString twice... ;) Ben

On Sunday 07 Nov 2004 6:19 pm, Benjamin Franksen wrote:
On Sunday 07 November 2004 17:41, Keean wrote:
I might really want to call the initialisation twice. If you use global variables, the library can only be initialised once... but what if I really want to use the library twice? With the state in a type passed between functions, you can have multiple different states active at once.
Yes, exactly. Just as you might, in fact, *want* to call putString twice...
Exactly wrong. I have stated several times that realInit cannot be used more than once, and this fact is the reason for the existance of oneShot. I don't want to get to hung up on this one simple example (heaven knows there are plenty of other possible examples), but I'm still waiting for yourself or Keean to demonstrate a simpler and safer way of ensuring this, not waffle your way out by saying it's unnecessary. I can assure you it is necessary. Oh and while we're at it, perhaps one of you could explain what it is you think is unsafe about the hypothetical top level <- bindings we're discussing (I've asked this before too, but no answer has been provided). Are your objections dogmatic, aesthetic, or rational? Do either of you object to the existance of such things as stdout? Or are you happy with their existance and it's just their safe creation you object to? If so, this seems like a strange contradiction to me. Regards -- Adrian Hey

Adrian Hey wrote:
Oh and while we're at it, perhaps one of you could explain what it is you think is unsafe about the hypothetical top level <- bindings we're discussing (I've asked this before too, but no answer has been provided).
Are your objections dogmatic, aesthetic, or rational?
Do either of you object to the existance of such things as stdout?
Or are you happy with their existance and it's just their safe creation you object to? If so, this seems like a strange contradiction to me.
Just because something is possible does not make it desireable. There may be certain extreme examples that really do require this kind of thing - but I would have thought that if the code could be refactored not to require it then that would be better. Keean.

I think the broad issue is that there are many different levels of the system at which something can be global: a module, a thread, a process, a user, a computer, a network segment, the internet, the universe, etc.. If your model of a concept is more global than the concept itself, you lose flexibility. If your model is less global than the concept, you get cache-coherency problems. The global variables we're talking about here are global to a single invocation of a Haskell program [*] [**]. The only concepts which are appropriately modeled by such globals are those whose natural scope is a single invocation of a Haskell program. Are there any? Adrian Hey's example of a badly-written C library is one. But I agree with Robert Dockins that such cases are better solved in C than in Haskell. (stdin,stdout,stderr) seem to naturally have this scope (assuming you don't use freopen). There needs to be only one Handle created for each of these because otherwise the buffering won't work properly. Global <- initializers seem like the right thing here. Are there any others? -- Ben [*] Adrian Hey has argued that "global" variables aren't really global. I think he's talking about hiding them through module scoping. What I mean by global is different: something is global at a particular level of the system if there's only one instance of it at that level. [**] Wouldn't it make sense to support thread-local global state also, if we're going to support process-local global state?

On Tuesday 09 November 2004 03:18, Ben Rudiak-Gould wrote:
Adrian Hey's example of a badly-written C library is one. But I agree with Robert Dockins that such cases are better solved in C than in Haskell.
Yes. Your code won't depend on compiler (dependent) flags to disable optimizations, which for my taste is just a little bit too obscure and is anyway non-portable. What follows are very raw ideas. Please bear with me if you think it is nonsense. We could reduce the pain of applying the C wrapper solution a bit by adding some support in the FFI. I imagine a feature to allow writing small C wrappers around imported foreign routines directly inside the Haskell module. Such code would need to be quoted properly (e.g. encoded as Haskell string, similar to the foreign entity names now). Eiffel has such a feature for its own variant of FFI and it has proven quite useful in practice. I know of at least one other case where such a feature might be helpful, namely for APIs where C structures get passed by value. At the moment one has to either lie to the FFI (declare the routine as if teh structure fields were passed separately, after checking that this is ok with respect to argument order and alignment) or else write a C wrapper routine (which is somewhat safer and more portable). The advantage of doing it through FFI support is that we avoid encouraging bad programming style by making global mutable variables an all too easily accessible and apparently safe feature. I have some ideas how to handle stdxyz without using global state but they are not though out and it is too late now anyway. Ben -- Top level things with identity are evil. -- Lennart Augustsson

Benjamin Franksen
We could reduce the pain of applying the C wrapper solution a bit by adding some support in the FFI. I imagine a feature to allow writing small C wrappers around imported foreign routines directly inside the Haskell module.
Such a facility is already available - the FFI pre-processor `GreenCard' allows you to write "in-line" C code in your Haskell module. Regards, Malcolm

(stdin,stdout,stderr) seem to naturally have this scope (assuming you don't use freopen). There needs to be only one Handle created for each of these because otherwise the buffering won't work properly. Global <- initializers seem like the right thing here.
I think I would rather have them passed to 'main' as arguments... Afterall, what if somone calls hClose on one? The fact that a function may fail (on a closed handle) due to an action taken in another function (closing the handle) is not nice... Passing the handles explicitly might make things more obvious. A more functional way would be to reference count the handle, and only really close it then the last reference is closed or goes out of scope. I guess you wouldn't want a huge number of arguments to main, so a record would let you have all the 'environment' in one variable.
Are there any others?
The fact that the concept of 'one program' is artificial (created by the loading process used by the OS), it would suggest that OS variables associated with the process (IE process-ID, or anything stored in the OS's process-table may have a natural lifetime of one process - although these days most of these things tend to be associated with threads instead (thread-id etc...) and many threads can start and end in the life of a program. I cannot think of any library that could not be written in a multiple reference way - if it wasn't dependant on some legacy code. Even a library to manage a single physical piece of hardware (say a special encryption chip) would be better written allowing more than one chip to be supported - even if current hardware does not allow this - some future system might, and you don't want to have to change the software-architecture because of this... Far better to allow multiple threads to independantly open multiple devices - and limit the number of devices to 1 for now. In this case the OS would need to manage the device-counter. A nice way to do this would be to use the FFI to get the operating system to manage this. You could use an OS semaphore or a unix-socket. If you use the PID as part of the socket name, then only the first call to open-socket would succeed. This suggests that a nice library to make this sort of thing easy would be a 'namedChannel' library, where read and write ends of a channel could be opened independantly and the address would be an arbitrary string. Keean.
participants (5)
-
Adrian Hey
-
Ben Rudiak-Gould
-
Benjamin Franksen
-
Keean Schupke
-
Malcolm Wallace