
H|i, Does anyone know of a simple and straightforward way to use global variables in Haskell? E.

eeoam:
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
E.
The usual way is to run the code that needs a global variable in a State monad. The next answer is: you don't really need global variables, since you don't have mutable variables anyway :-) -- Don

On 17/05/07, Eric
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
You can pass around an environment with the State or Reader monads (read/write and read-only respectively). If you want to do IO with the data you'll probably need the transformer equivalents: StateT or ReaderT. I think there are some hackish ways of making IO variables but I don't know how that's done. I'd imagine it's frowned on from a stylistic point of view, too... D.

You can also use mutable variables (MVars) found in Control.Concurrent.MVar
http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent-MV...
They might work depending on your implementation. The reading and
writing of MVars returns IO actions.
On 5/17/07, Dougal Stanton
On 17/05/07, Eric
wrote: H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
You can pass around an environment with the State or Reader monads (read/write and read-only respectively). If you want to do IO with the data you'll probably need the transformer equivalents: StateT or ReaderT.
I think there are some hackish ways of making IO variables but I don't know how that's done. I'd imagine it's frowned on from a stylistic point of view, too...
D. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Tom Harper Computer Science Major '07 Syracuse University +1 949 235 0185 Public Key: http://aftereternity.co.uk/rth.asc

On Thu, May 17, 2007 at 02:41:33PM +0100, Eric wrote:
Does anyone know of a simple and straightforward way to use global variables in Haskell?
Just declare them at the top level, as a function but without arguments: ======================================================================= x = 2 main = print x ======================================================================= Stefan

Eric wrote:
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
(Perhaps annoyingly) the answer to this question, like so many other questions on this list, is a question. "What are you trying to do?". The reason for this is that haskell's abstractions are different from those of the mainstream imperative languages, and the mapping isn't 1-1. So for a particular 'C' feature, there may be 3 or 4 haskell features which achieve similar effects, and the correct choice depends on the detailed context. So, in particular, all those tasks which you use global variables for in C, can be achieved in haskell, but which solution depends what you are trying to do: 1. If you have some globals which are constant, then just define them at the top level. No problem. pi = 3.14; progname = "MyCoolApp". 2. If you have a global environment, which various functions read from (and you might, for example, initialise from a configuration file) then you should thread that as a parameter to your functions (after having, very likely, set it up in your 'main' action). If the explicit parameter passing annoys you, then you can 'hide' it with a monad. 3. If you have a need to store some global state which various of your functions modify, then you have a design issue. This style of design is frowned upon in C as well! The correct solution in C is to pass a 'bundle' of appropriate parameters (you might call it an environment) to those functions which need it. In C++, perl or python you'd very likely make this bundle an object. In haskell this bundle will be a data value in some custom type; and using Monads you can 'hide the details' of passing it around, if you wish. Hope that helps a bit, Jules

On Thu, 17 May 2007, Jules Bean wrote:
Eric wrote:
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
(Perhaps annoyingly) the answer to this question, like so many other questions on this list, is a question. "What are you trying to do?".
I've put your answer to the FAQ category on Haskell-Wiki: http://haskell.org/haskellwiki/Global_variables

Eric wrote:
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
I assume what you're looking for is to be able to have IORefs,MVars Chans etc at the top level. The standard (for want of a better word) way to do this is known commonly known as the "unsafePerformIO hack". myTopLevelFlag :: IORef Bool {-# NOINLINE myTopLevelFlag #-} myTopLevelFlag = unsafePerformIO (newIORef False) With ghc you should also compile which make use of the unsafePerformIO hack using the -fno-cse flag (to inhibit common sub-expression elemination). Use something like this at the top of the module.. {-# OPTIONS_GHC -fno-cse #-} BTW, this is the commonly the subject of flame wars on the Haskell mailing lists because there appear to be many who passionately believe and assert that so called "global variables" are (at best) unnecessary and (at worst) are "evil". These people are quite simply wrong and should be ignored :-) They are necessary because they are the only way to ensure important safety properties of many IO APIs. I ported the old wiki page about this to the new wiki.. http://www.haskell.org/haskellwiki/Top_level_mutable_state If you want to see more examples of the use of the unsafePerformIO hack you need only look at the source code of the current base package (you'll find a dozen or so uses of this hack to create top level mutable state). Regards -- Adrian Hey

Please take this message in the fashion that is intended. My criticism is light hearted, as I believe yours is. Adrian Hey wrote: [hack snipped]
BTW, this is the commonly the subject of flame wars on the Haskell mailing lists because there appear to be many who passionately believe and assert that so called "global variables" are (at best) unnecessary and (at worst) are "evil". These people are quite simply wrong and should be ignored :-)
Adrian Hey is not only wrong, but actually evil. He should be ignored. :-) The above hack is not actually Haskell. It's a hack, and it depends on the particular implementation characteristics of GHC. It is not unreasonable to imagine that a future GHC might contain different compilation techniques (I hesitate to use the word 'optimisations' because that sounds like something easy to turn off) which invalidate the technique in other ways or make it dangerous.
They are necessary because they are the only way to ensure important safety properties of many IO APIs.
That's a bold claim. It's very hard to prove that things don't exist. (That is, that other ways to ensure these safety properties don't exist). In snipped text you comment that the problems are often in low-level FFI library code: this makes me wonder if the real culprit doesn't lie at the FFI-haskell boundary. Perhaps there are good ways to specify this kind of invariant there.
If you want to see more examples of the use of the unsafePerformIO hack you need only look at the source code of the current base package (you'll find a dozen or so uses of this hack to create top level mutable state).
All of these are, in a sense, failings. Because unsafePerformIO is not haskell, and we'd like base to be a haskell library. Not a GHC library. I'd be willing to take a sportsman's bet that the original poster does not actually need to use this hack; I doubt his application falls into the categories you have outlined. I would discourage people from using this hack unless it is, in fact, the only feasible approach. Jules

On 17/05/07, Jules Bean
I'd be willing to take a sportsman's bet that the original poster does not actually need to use this hack; I doubt his application falls into the categories you have outlined. I would discourage people from using this hack unless it is, in fact, the only feasible approach.
I find it amusing that questions like these elicit such a wide variety of responses. The original poster probably thought they were asking a fairly straightforward question and then... woosh :-) Everyone responds to the question at the level that suits their own proficiency in the subject, I suppose. D.

Jules Bean wrote:
BTW, this is the commonly the subject of flame wars on the Haskell mailing lists because there appear to be many who passionately believe and assert that so called "global variables" are (at best) unnecessary and (at worst) are "evil". These people are quite simply wrong and should be ignored :-)
Adrian Hey is not only wrong, but actually evil. He should be ignored. :-)
I am right, I might well be evil, and if past experience is anything to go by I already know that I will be ignored. We've been talking about this problem for years, but nothing is ever done about it (a solution to this problem isn't even on the agenda for Haskell' AFIAK).
The above hack is not actually Haskell. It's a hack, and it depends on the particular implementation characteristics of GHC. It is not unreasonable to imagine that a future GHC might contain different compilation techniques (I hesitate to use the word 'optimisations' because that sounds like something easy to turn off) which invalidate the technique in other ways or make it dangerous.
Well of course, that's why something needs to be done about this. Just being in a state of complete denial regarding the reality of this problem won't make it go away.
They are necessary because they are the only way to ensure important safety properties of many IO APIs.
That's a bold claim. It's very hard to prove that things don't exist. (That is, that other ways to ensure these safety properties don't exist). In snipped text you comment that the problems are often in low-level FFI library code: this makes me wonder if the real culprit doesn't lie at the FFI-haskell boundary. Perhaps there are good ways to specify this kind of invariant there.
No. Even if we stripped away all other code apart from the Haskell rts itself (OS, device drivers etc) and performed your IO entirely in Haskell (just using peek and poke on bare hardware), you'd still need top level mutable state to implement common IO API's (e.g. The socket API, does anybody really believe this is entirely stateless?). I wouldn't dispute the assertion that at the level of complete programs or processes, implementations that don't use "global variables" are possible. But this does not hold at the level of individual IO library API's. If we want to keep our software *modular* (I take we do), then we need top level mutable state.
If you want to see more examples of the use of the unsafePerformIO hack you need only look at the source code of the current base package (you'll find a dozen or so uses of this hack to create top level mutable state).
All of these are, in a sense, failings. Because unsafePerformIO is not haskell, and we'd like base to be a haskell library. Not a GHC library.
But what's the problem? Is it the use of "global variables"? Or is it the use of the unsafePerformIO hack to create them? Regards -- Adrian Hey

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Adrian Hey wrote:
They are necessary because they are the only way to ensure important safety properties of many IO APIs.
That's a bold claim. It's very hard to prove that things don't exist. (That is, that other ways to ensure these safety properties don't exist). In snipped text you comment that the problems are often in low-level FFI library code: this makes me wonder if the real culprit doesn't lie at the FFI-haskell boundary. Perhaps there are good ways to specify this kind of invariant there.
No. Even if we stripped away all other code apart from the Haskell rts itself (OS, device drivers etc) and performed your IO entirely in Haskell (just using peek and poke on bare hardware), you'd still need top level mutable state to implement common IO API's (e.g. The socket API, does anybody really believe this is entirely stateless?).
I wouldn't dispute the assertion that at the level of complete programs or processes, implementations that don't use "global variables" are possible. But this does not hold at the level of individual IO library API's. If we want to keep our software *modular* (I take we do), then we need top level mutable state.
If you want to see more examples of the use of the unsafePerformIO hack you need only look at the source code of the current base package (you'll find a dozen or so uses of this hack to create top level mutable state).
All of these are, in a sense, failings. Because unsafePerformIO is not haskell, and we'd like base to be a haskell library. Not a GHC library.
But what's the problem? Is it the use of "global variables"? Or is it the use of the unsafePerformIO hack to create them?
It's only slightly the "unsafePerformIO hack", IMHO - if that were all, a mechanism not requiring it would have been implemented long ago. Now I launch into a long discussion: The difficult question is "how global?". GHCi already has problems with this (varying persistence of those global variables, and they never last between separate invocations of ghci). Obviously "global" variables to truly be global should be shared with one persistent state across the whole wide world forever :P - but then we get identity problems, e.g. fancy-package:GlobalVariable.Fancy.nuclearMissilesLaunched :: IORef/MVar MannerOfNuclearMissileLaunch where one day or on one hacker's computer there is type MannerOfNuclearMissileLaunch = Int --number launched already and another, data MannerOfNuclearMissileLaunch = NoMissilesLaunched | WorldDestroyed | Unknown The usual meaning relies on the size of a program invocation. This is a link to main:Main.main . As you observe, this is like inserting a wrapper over everywhere the IO monad is used. Clearly this adds modularity by not requiring main's code to be modified, and also destroys modularity by forcing main's semantics to be modified. A Haskell program is notionally executed by running Main.main. Consider:
global "foo" (initial value: False) main1 = setGlobal "foo" True main2 = getGlobal "foo" >>= print
Compile with -main-is main1 to the binary 'main1' and with -main-is main2 to the binary 'main2'. Now consider two possible overall definitions of main:
main = main1 >> main2 or main = executeBinary "main1" >> executeBinary "main2"
Basically, all existing operating systems require executing a binary to be more than just running its IO monad; they set up a "global" (process-specific) environment for the process, which is why the two hypothetical example defintions of main give different results. Note that operating systems also serve the root of filesystems "/" in unix, and variables global to the root filesystem's sharedness can be simulated in this way. Operating systems could serve process-specific spaces this way, as long as it is possible for them to define something like getProcessID :: IO ProcessID. Note that Haskell has ThreadID which is usefully Eq-comparable in GHC, whereas Hugs chooses not to distinguish the identity of threads. It is a similar design tradeoff. Hardware: readHardware, writeHardware are IO specific to the hardware. Kernels generally rely on storing information in RAM about the state of the hardware, and they presume to have global variables whose scope is the present run of the computer. This is straightforward for monolithic kernel designs. Although, if you want persistent settings it is more difficult, the system explicitly saving ALSA state to disk or whatever. Operating systems: I don't know sockets in particular, but indeed operating systems are expected to provide some IO operations that don't do exactly the same thing depending on which computer in the world the program is running on. Kernel/OS design variation is certainly one area to look into for further consideration of these issues. There are non-monolithic kernels (GNU Hurd...), systems that can run on multiple hardware-computers as a cluster... There is usually expected to be one name resolver (e.g. kernel) that all code can call into, even if it is only to access the current filesystem server or whatever - or else at program startup all those references are passed, like ELF dynamically linked binaries? It can certainly be done without extensions to the Haskell syntax or semantics, implementing every program in haskell and using no "top level mutable state" of the usual variety (although a way to store arbitrary Haskell values such as (Int -> Bool) in available shared spaces would need to be implemented). Consider the recent Haskell library that internally only uses unsafePerformIO to provide an otherwise safe extensible global named state (relying on Haskell's/Typeable's definition of type-equivalence for soundness). Well, that one unsafePerformIO would not be needed if the OS provided system calls to access a named area of memory (with initializer as given if it's not already existent - creation and detecting whether it exists yet could be separate system calls). Names would be the Haskell/Typeable system's unique identifiers such as the "fancy-package:GlobalVariable.Fancy.nuclearMissilesLaunched". It of course still suffers from the "how global is it? - no way for the caller to specify" problem as above. A full identification of the global variable might also involve the computer's IP address and the process's ID, for example. imagine that we essentially have: data AllGlobalVariables = ??? --contains some IORefs main :: AllGlobalVariables -> IO () --as it is with unsafePerformIO hack, C-style global variables, etc. --AllGlobalVariables is an abstract data type which the caller can --only create with the default mechanism (possibly after creation -- it can be passed to more than one invocation of main, as GHCi -- attempts to do), but it is extensible internally. or main :: SocketVariables -> TypeableVariables -> ... (-> OtherGlobalVariables) ... -> IO () --The caller can thread more precisely if desired. C programs would --definitely have an OtherGlobalVariables argument among them, which -- is based on some section of the binary file if I am not mistaken. This could of course be done somewhat abstractly with accessors, so that everything doesn't break when a new "global" thing is added. I might prefer the arguments to be grouped/distinguished by distinctions in scope definitions though (hardware-computer, "process" abstraction, etc.) It is more difficult when the scopes are not fixed for the complete run of a Haskell program (if it is suspended and resumed on a different computer... if the program was a kernel it would almost certainly not be happy about that!) So if a modular OS/kernel design, say, for sockets, required some certain globalness of variable, its main would have to be passed the ability to create such an area (meaning that a library/system call would have to be provided. Providing more system calls or exported library functions doesn't actually break everything, as existing systems prove!). I think this design can be done modularly (at least hypothetically, with the right system design). Many (Unix-like, some others) systems are stuck with the process as a unit of abstraction from which it's difficult to separate out anything particular (but e.g. Linux implements such ways anyway since they can be important). This is a design choice that simplifies some things (the unix philosophy; worse is better; and we do _have_ systems today) and makes it very difficult to do some other odd things, since much software (potentially) relies on quirks of that model. Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGTLLiHgcxvIWYTTURApvzAJ4qDQodQ1iKgb1eESIQMLV0qnDB6ACfWEyH nNSxFMN8PJOs8uxsXScrheg= =mSJZ -----END PGP SIGNATURE-----

There is no reality about global variables. Global variables are syntactic sugar for local variables. That is the reality we need to think through. This syntactic sugar streamlines many practical programs and is indeed valuable.

Albert Y. C. Lai wrote:
There is no reality about global variables. Global variables are syntactic sugar for local variables. That is the reality we need to think through. This syntactic sugar streamlines many practical programs and is indeed valuable.
I agree that the use of the term "global variable" is both inaccurate an highly emotive, which is why I don't like it. But even the term I use ("top level mutable state") is not entirely accurate. The mutable state is not at the top level, the mutable state is already part of "the world" before main starts running. What is top level are references to that state (IORefs, MVars etc). As these are perfectly ordinary (I.E. *immutable*) Haskell values there doesn't seem to be any obvious reason why they should not exist at the top level. All that's missing is a semantically sound mechanism to achieve this (such as the ACIO monad proposal). Regards -- Adrian Hey

Adrian Hey wrote:
We've been talking about this problem for years, but nothing is ever done about it (a solution to this problem isn't even on the agenda for Haskell' AFIAK).
The problem needs talking about, it's important. My objection was the implication that top-level mutable state was the right answer to the OP's question, which my strong hunch is it isn't. I don't deny the existence of a problem here.
No. Even if we stripped away all other code apart from the Haskell rts itself (OS, device drivers etc) and performed your IO entirely in Haskell (just using peek and poke on bare hardware), you'd still need top level mutable state to implement common IO API's (e.g. The socket API, does anybody really believe this is entirely stateless?).
I'm not sure that's quite to the point. Clearly we can set up state at the top of our main action: main = do sockstate <- initSocks graphstate <- initGraphics ... disposeGraphics graphstate disposeSocks sockstate exit Voila. Mutable state which persists for our entire program. Arguably it's a pain passing around the state explicitly. Alternatively, you can argue that passing this stuff around explicitly makes code much easier to reason about. And we know a dozen tricks to hide this stuff (reader monads, state monads, withSocketsDo-style bracket constructs). So I don't think this is really the issue, is it? As I understood it, the issue was more about whether or not *library* modules should be allowed to some 'set up' initialisation code to run at the beginning of 'main' to start up their own global state. I was never convinced this was a nice idea (I don't like the thought than an 'import' alone can add hidden IO actions to main). Mind you, I'm not convinced it's wrong, either. I think it's a hard one.
I wouldn't dispute the assertion that at the level of complete programs or processes, implementations that don't use "global variables" are possible. But this does not hold at the level of individual IO library API's. If we want to keep our software *modular* (I take we do), then we need top level mutable state.
That's assuming you feel having an explicit 'init' command and a 'withLibXYZDo' construct breaks modularity. It doesn't feel like a terrible modularity break to me. (Plenty of C libraries I've used require explicit init calls).
Is it the use of "global variables"? Or is it the use of the unsafePerformIO hack to create them?
The latter is definitely a problem. The former, I'm not sure. My gut feeling is that it is, too. Jules

Jules Bean wrote:
main = do sockstate <- initSocks graphstate <- initGraphics ... disposeGraphics graphstate disposeSocks sockstate exit
Voila. Mutable state which persists for our entire program.
Arguably it's a pain passing around the state explicitly. Alternatively, you can argue that passing this stuff around explicitly makes code much easier to reason about. And we know a dozen tricks to hide this stuff (reader monads, state monads, withSocketsDo-style bracket constructs).
So I don't think this is really the issue, is it?
Have you ever wondered why our IO API's don't look like this? I think there are many problems with this approach. What are the consequences of this philosophy for issues such safety, platform independence, maintainance of stable APIs?
As I understood it, the issue was more about whether or not *library* modules should be allowed to some 'set up' initialisation code to run at the beginning of 'main' to start up their own global state. I was never convinced this was a nice idea (I don't like the thought than an 'import' alone can add hidden IO actions to main).
I agree, which is why I'm not keen on the top level mdo proposal. But addressing this issue is the point of the ACIO monad proposal.
Mind you, I'm not convinced it's wrong, either. I think it's a hard one.
I've pretty much convinced it's wrong. There should be one and only one "main" from which all subsequent IO activity derives. But creating internal state in the form of mutable data structures is not an IO activity. It just so happens that at the moment the only way to do this is newIORef :: a -> IO(IORef a), but this need not be so (readIORef and writeIORef are IO activities, but newIORef isn't).
I wouldn't dispute the assertion that at the level of complete programs or processes, implementations that don't use "global variables" are possible. But this does not hold at the level of individual IO library API's. If we want to keep our software *modular* (I take we do), then we need top level mutable state.
That's assuming you feel having an explicit 'init' command and a 'withLibXYZDo' construct breaks modularity. It doesn't feel like a terrible modularity break to me. (Plenty of C libraries I've used require explicit init calls).
Indeed they do, unfortunately. In fact it was this very problem that lead me to conclude the top level mutable state is not only not "evil", but is a necessity. Having to call explicit initialisation code is a big problem in complex systems. Exactly who is responsible for initialising what? and in what order? You could make it the users responsibility to do it right at the begining of main. But this places a heavy burden on the user to fully understand the dependencies of their hardware and the software that controls it. Or you could make it the users responsibility to initialise whatever APIs they actually make use of, in no particular order. Those APIs then initialise whatever sub-systems they actually use as part of their own initialisation. But what happens if the same sub-system is used (and initialised) by two different higher level IO libs? (The second will initialisation will destroy any state that the first may have set up.) Of course it's perfectly straight forward to avoid accidental re-initialisation, but only by making use of..you know what.
Is it the use of "global variables"? Or is it the use of the unsafePerformIO hack to create them?
The latter is definitely a problem.
Yes.
The former, I'm not sure. My gut feeling is that it is, too.
If it's necessary that there is only one libWhateverState to preserve safety properties (because the state must be kept in sync with what's really being done to "the world", of which there is also only one) then what's the point of making it a parameter (the lib is not truly parameterisable by that state). Furthermore, if it is going to take this state handle as an explicit argument then you need to provide some way for users to get this state handle. This could be by.. 1 - Making it an argument of main. 2 - Exposing a newLibWhateverState constructor 3 - Exposing a getLibWhateverState "getter". Problems.. 1 Requires the type of main to depend on what IO libs are used. Also the Boot code that invokes main must get this state handle from somewhere. 2 Potentially permits 2 or more libWhateverStates to be created (in which case all bets are off re. the safety proprties I was talking about). 3 Can't be implemented without making use of..you know what. Regards -- Adrian Hey

[I agree with your points, but...] Adrian Hey wrote:
I've pretty much convinced it's wrong. There should be one and only one "main" from which all subsequent IO activity derives. But creating internal state in the form of mutable data structures is not an IO activity. It just so happens that at the moment the only way to do this is newIORef :: a -> IO(IORef a), but this need not be so (readIORef and writeIORef are IO activities, but newIORef isn't).
I find this point rather slippery. 'newIORef' is a unique-name-provider, but 'unique over what'? When a module is imported twice, should it not create new unique names?
Indeed they do, unfortunately. In fact it was this very problem that lead me to conclude the top level mutable state is not only not "evil", but is a necessity. Having to call explicit initialisation code is a big problem in complex systems. Exactly who is responsible for initialising what? and in what order?
Maybe I'm being old-fashioned here, but isn't that a *fundamental* problem, that can't be automatically solved? Isn't it just a fact that in a complex system you, the programmer, have to make some decisions about who is responsible for acquiring resources?
If it's necessary that there is only one libWhateverState to preserve safety properties (because the state must be kept in sync with what's really being done to "the world", of which there is also only one) then what's the point of making it a parameter (the lib is not truly parameterisable by that state).
A slightly irrational point of concern in my mind is that by encouraging this practice, we encourage lazy library design. A large proportion of libraries in fact *can* be written as "reentrant" in this sense, and truly are parameterisable by their state. Only a few are not: those which actually manage connections to some "real" entity which is in fact unique. Of course that doesn't mean the problem doesn't exist. Jules

Jules Bean wrote:
I've pretty much convinced it's wrong. There should be one and only one "main" from which all subsequent IO activity derives. But creating internal state in the form of mutable data structures is not an IO activity. It just so happens that at the moment the only way to do this is newIORef :: a -> IO(IORef a), but this need not be so (readIORef and writeIORef are IO activities, but newIORef isn't).
I find this point rather slippery. 'newIORef' is a unique-name-provider, but 'unique over what'? When a module is imported twice, should it not create new unique names?
No, it's just binding a name to a value, same as any other binding. The difference is unlike normal top level bindings, that name is not "equal to" any other expression. It just "is", if you get what I mean (I'm sure you do). That's why the proposed syntax borrows the <- from do expressions.
If it's necessary that there is only one libWhateverState to preserve safety properties (because the state must be kept in sync with what's really being done to "the world", of which there is also only one) then what's the point of making it a parameter (the lib is not truly parameterisable by that state).
A slightly irrational point of concern in my mind is that by encouraging this practice, we encourage lazy library design.
Yes, this is slightly irrational :-)
A large proportion of libraries in fact *can* be written as "reentrant" in this sense, and truly are parameterisable by their state.
I can't help being sceptical about this. AFAICS it doesn't matter how many explicit state handles you tack on to the left of the "-> IO Whatever", the fact that the whole lot ends in "-> IO Whatever" means there's almost certainly some state left unaccounted for and unparameterised (calls the OS are missing an OS state handle for example). Also, I don't really understand what you mean by "reentrant" in this context. Are you talking about thread safety? (I guess not) Are you implying that APIs of IO libs that mutate Haskell "global" state are some how different from other IO APIs which mutate other "global" state (such as OS state or "world state")? Are they deficient in some way? If so, how can you tell the difference? Regards -- Adrian Hey

Adrian Hey wrote:
Jules Bean wrote:
I've pretty much convinced it's wrong. There should be one and only one "main" from which all subsequent IO activity derives. But creating internal state in the form of mutable data structures is not an IO activity. It just so happens that at the moment the only way to do this is newIORef :: a -> IO(IORef a), but this need not be so (readIORef and writeIORef are IO activities, but newIORef isn't).
I find this point rather slippery. 'newIORef' is a unique-name-provider, but 'unique over what'? When a module is imported twice, should it not create new unique names?
No, it's just binding a name to a value, same as any other binding. The difference is unlike normal top level bindings, that name is not "equal to" any other expression. It just "is", if you get what I mean (I'm sure you do). That's why the proposed syntax borrows the <- from do expressions.
That's not my point. newIORef creates unique names (references). The whole job of newIORef is to create unique names; unique names which "refer to" little tiny bits of state parcelled up somewhere in that mysterious IO monad. It is the scope of this uniqueness I'm talking about: do libraries need these unique names to be unique over each importer, over each thread, over each subprocess: consider a haskell application server or a haskell OS... what is the correct domain of uniqueness?
A large proportion of libraries in fact *can* be written as "reentrant" in this sense, and truly are parameterisable by their state.
I can't help being sceptical about this. AFAICS it doesn't matter how many explicit state handles you tack on to the left of the "-> IO Whatever", the fact that the whole lot ends in "-> IO Whatever" means there's almost certainly some state left unaccounted for and unparameterised (calls the OS are missing an OS state handle for example).
Also, I don't really understand what you mean by "reentrant" in this context. Are you talking about thread safety? (I guess not) Are you implying that APIs of IO libs that mutate Haskell "global" state are some how different from other IO APIs which mutate other "global" state (such as OS state or "world state")? Are they deficient in some way? If so, how can you tell the difference?
'reentrant' is not the right word, although it's a related notion. I was talking about libraries which can safely be initialised more than once, for multiple clients, and they keep around 'separate' state for each client/each time they are initialised. This kind of design is often a precondition for being thread-safe; and it's often plain good design, unless some external 'real world' uniqueness makes it impossible. Jules

Jules Bean wrote:
That's not my point. newIORef creates unique names (references). The whole job of newIORef is to create unique names; unique names which "refer to" little tiny bits of state parcelled up somewhere in that mysterious IO monad. It is the scope of this uniqueness I'm talking about: do libraries need these unique names to be unique over each importer, over each thread, over each subprocess: consider a haskell application server or a haskell OS... what is the correct domain of uniqueness?
The scoping rules are exactly the same as for any other top level identifier, whether or not exported or imported. There's no reason why an importing module should know or care that one or more of the identifiers it's importing is the result of a <- binding. Should every module that imports the prelude get a different stdout? (they currently don't of course)
'reentrant' is not the right word, although it's a related notion. I was talking about libraries which can safely be initialised more than once, for multiple clients, and they keep around 'separate' state for each client/each time they are initialised. This kind of design is often a precondition for being thread-safe; and it's often plain good design, unless some external 'real world' uniqueness makes it impossible.
I don't see a problem here. The fact that an IO lib presents some controlled interface one or more to real world resources (I.E. resources where you can't simply conjour up a new ones by using a "newResource" constructor) should not stop it being able to service multiple clients or sessions. The socket API and indeed the OS itself seem to be obvious examples of this (or even something really simple like Data.Unique). Or take a look at the hypothetical embedded device driver API I put on the wiki. This makes a clear distinction between DeviceHandles and DeviceSessionHandles (which will wrap the correspnding DeviceHandle). Users cannot create new DeviceHandles but are free to create as many DeviceSessionHandles with a particular device as they like. Regards -- Adrian Hey

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Adrian Hey wrote:
Furthermore, if it is going to take this state handle as an explicit argument then you need to provide some way for users to get this state handle. This could be by.. 1 - Making it an argument of main. 2 - Exposing a newLibWhateverState constructor 3 - Exposing a getLibWhateverState "getter".
Problems.. 1 Requires the type of main to depend on what IO libs are used. Also the Boot code that invokes main must get this state handle from somewhere. 2 Potentially permits 2 or more libWhateverStates to be created (in which case all bets are off re. the safety proprties I was talking about). 3 Can't be implemented without making use of..you know what.
Making it an argument of main (1) is somewhat the same as doing (3) - consider getArgs. What bothers me is the IO-state in the Haskell standard (arguments, random state...) which is a set not extensible without the hack. (It bothers me that there _are_ so many things in that set, somewhat ad-hoc-ly it seems :)
Unfortunately the situation seems worse than this. AFAICT we haven't even got a consensus that that there is a real problem here that needs any kind of solution :-)
The unsafePerformIO hack being used is not very satisfactory given how many optimizations make it difficult to use safely in practice. This hack is also used many places. I would be happier if that situation were not true, and I suspect there's something like a consensus on _that_. (maybe not as strong as "_needs_ a solution" in the short-to-mid term future) Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGTg2+HgcxvIWYTTURAvWfAKC36q24IKTX5YQOVi+A4gNYLzBMMACePwkL dwnAlZh++e2EqiFKvJEmn1M= =NBHG -----END PGP SIGNATURE-----

[cc'ing HPrime] Isaac Dupree wrote:
The unsafePerformIO hack being used is not very satisfactory given how many optimizations make it difficult to use safely in practice. This hack is also used many places. I would be happier if that situation were not true, and I suspect there's something like a consensus on _that_. (maybe not as strong as "_needs_ a solution" in the short-to-mid term future)
Considering the value that the Haskell community normally places on sound semantics, reliance on such an appalling hack seems pretty bad to me. If a solution doesn't find it's way into H' then how many more years is it going to be with us? It's just embarrassing :-) Also, I don't know if the OP was a noob, but telling people (especially noobs) that if they can't figure out how to solve a problem without using a "global variable" then that must be down to inexperience and general cluelessness on their part just seems wrong to me. It simply isn't true. (Anyone who disagrees with this should feel free to submit the patches needed to fix up the base package :-) Regards -- Adrian Hey

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Adrian Hey wrote:
[cc'ing HPrime]
Isaac Dupree wrote:
The unsafePerformIO hack being used is not very satisfactory given how many optimizations make it difficult to use safely in practice. This hack is also used many places. I would be happier if that situation were not true, and I suspect there's something like a consensus on _that_. (maybe not as strong as "_needs_ a solution" in the short-to-mid term future)
Considering the value that the Haskell community normally places on sound semantics, reliance on such an appalling hack seems pretty bad to me. If a solution doesn't find it's way into H' then how many more years is it going to be with us? It's just embarrassing :-)
Yes, also it places value on REALLY EXTREMELY (excessively?) SOUND semantics, and on the modularity of the language even more than the modularity of its uses (or something like that :-) Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least. Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGUF4yHgcxvIWYTTURAvqWAJ46eFRt5LK1lUwqr2BmHVSrHljxzwCfYGJB x5ivAFEw5vYKbxTPIg+PrIU= =0xVK -----END PGP SIGNATURE-----

Isaac Dupree wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least.
I have a hard time trying to understand why anyone would prefer this to the simple and clear <- syntax that's been proposed. As for the ACIO monad itself, this is utterly trivial and requires no language change. It's just a library. Maybe the first pragma you propose might have other uses to control optimisations, so I'm not totally anti this. But generally I dislike pragmas (I always find myself wondering what's wrong with the language design that makes the pragma necessary). So pragmas that influence optimisation are something I can live with. But using pragmas to influence *semantics* really is an evil practice IMO and is something that should be discouraged, not made an unavoidable necessity. But yes, if this problem isn't going to be properly addressed then at the very least the -fno-cse flag or something similar needs standardising (NOINLINE already is I think). Or we port all existing unsafePerfomIO hacked code to use Johm Meachams variant of the hack (uses types to ensure the compiler doesn't see common sub-expressions). Regards -- Adrian Hey

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Adrian Hey wrote:
Isaac Dupree wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least.
I have a hard time trying to understand why anyone would prefer this to the simple and clear <- syntax that's been proposed. As for the ACIO monad itself, this is utterly trivial and requires no language change. It's just a library.
Maybe the first pragma you propose might have other uses to control optimisations, so I'm not totally anti this. But generally I dislike pragmas (I always find myself wondering what's wrong with the language design that makes the pragma necessary).
So pragmas that influence optimisation are something I can live with. But using pragmas to influence *semantics* really is an evil practice IMO and is something that should be discouraged, not made an unavoidable necessity.
Indeed. My rationale: - It would get some reliable semantics implemented in GHC (and/or other compilers hopefully). Since what we have already is a multi-part hack, this might be a nontrivial/important piece of work, and should make such things more reliable. - Pragmas (NOINLINE) are already used to influence semantics here. This idea doesn't introduce anything "worse" than that. And it doesn't require that people subscribe to particular syntax, ACIO implementation, etc. - Once implemented, if I understand correctly (do I?), it should make it easier for non-Simon to try out the hard work of a "real" solution involving non-pragma-syntax changes, ACIO libraries, or whatever is desired. Not because I think it's a great solution (nor even deserve to be called a real "solution" at all), but because nothing is being implemented now, for whatever reason. So I'm putting out this idea, in case it's a step in the right direction that someone is willing to take. Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGUJlEHgcxvIWYTTURAiGRAJ9ovzlD1Tc/Ce5tbCbYBBGcWLX/9ACfYzc3 a+xC3hQrXB3V9Iq+0vzxnmg= =EGk7 -----END PGP SIGNATURE-----

Hello Isaac, Sunday, May 20, 2007, 6:41:54 PM, you wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step...
or LOOK_BUT_DON'T_TOUCH :) -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Isaac Dupree wrote:
Maybe some sort of ISOLATE, DON'T_OPTIMIZE (but CAF), or USED_AS_GLOBAL_VARIABLE pragma instead of just the insufficient NOINLINE would be a good first step... if successful it would remove the occasional need for -fno-cse for a whole module in GHC, at least.
ISOLATE, DON'T_OPTIMIZE are actually bad names for the whole effect, which requires persistent CAF semantics. An implementation that doesn't make top-level definitions be CAFs, or even one that is willing to garbage-collect them when memory is tight such that they need recalculation later, would need a special case for global variables to make them work. i.e. I'm not sure if there exists a reasonable pragma while the code still uses unsafePerformIO. Hmm.... How about so, {-# NOINLINE var #-} var :: IORef Int var = unsafePerformIO (newIORef 3) - --> var :: IORef Int var = {-# EVALUATE_THIS_TEXT_ONLY_ONCE #-} (unsafePerformIO (newIORef 3)) to capture the desired semantics: text-based uniqueness, no duplication, no sharing of the IORefs (sharing the pure contents is fine), and no need to actually evaluate it any times at all. {-# EVALUATE_THIS_TEXT_ONLY_ONCE #-} is syntactically like a (special) function. Clearly it is an impossible demand for polymorphic things, so the compiler could complain (at least a warning) if the (var :: IORef Int) line was left off, for example. I guess it would also complain about non-type(class) argument dependencies too such as (f x = (unsafePerformIO (newIORef (x::Int))) )... Food for thought :-) Isaac -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFGU02YHgcxvIWYTTURAoCaAKCkDH7Pd7JbNt0TmNig9j7ujiUV9ACZAevI QOjdmMbrPfVrKBafZshCh7c= =9/5v -----END PGP SIGNATURE-----

Isaac Dupree wrote:
var :: IORef Int var = {-# EVALUATE_THIS_TEXT_ONLY_ONCE #-} (unsafePerformIO (newIORef 3))
I think I still prefer.. var :: IORef Int var <- newIORef 3 or, more likely.. var :: IORef Int var <- ACIO.newIORef 3 The <- syntax should make the intended semantics clear and unambiguous, so it becomes the problem of individual implementors (not standards writers) to make sure that whatever optimisations or transformations that may be appropriate for their implementation preserve those semantics. (IOW there's no need to worry about what a pragma really means in operational terms, AFAICS). The ACIO monad also restricts what programmers may use on the rhs of the <-. But if you want a good name for the pragma how about this..
var :: IORef Int var = {-# <- #-} (unsafePerformIO (newIORef 3))
:-) Regards -- Adrian Hey

On 5/23/07, Adrian Hey
I think I still prefer..
var :: IORef Int var <- newIORef 3
So do I. For one very good reason: this syntax could be defined as a
"constructor" syntax and guaranteed to run before main.
The other syntaxes proposed don't strike me as sufficiently rigorous.
--
Taral

Taral wrote:
On 5/23/07, Adrian Hey
wrote: I think I still prefer..
var :: IORef Int var <- newIORef 3
So do I. For one very good reason: this syntax could be defined as a "constructor" syntax and guaranteed to run before main.
Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all). But anyway, the constraints of the ACIO monad allow creation to occur at any time before the first attempt to read or write the IORef.
The other syntaxes proposed don't strike me as sufficiently rigorous.
Me neither. It's always been a great source of puzzlement to me why this very simple and IMO conservative proposal should be so controversial. Unless someone can point out some severe semantic difficulty or suggest something better it seems like a no-brainer to me. Regards -- Adrian Hey

On 5/24/07, Adrian Hey
Taral wrote:
The other syntaxes proposed don't strike me as sufficiently rigorous.
Me neither. It's always been a great source of puzzlement to me why this very simple and IMO conservative proposal should be so controversial. Unless someone can point out some severe semantic difficulty or suggest something better it seems like a no-brainer to me.
I think it lacks implementation. I don't have time, or I'd look into
hacking this into GHC.
--
Taral

Adrian Hey wrote:
Taral wrote:
On 5/23/07, Adrian Hey
wrote: I think I still prefer..
var :: IORef Int var <- newIORef 3
So do I. For one very good reason: this syntax could be defined as a "constructor" syntax and guaranteed to run before main.
Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all). But anyway, the constraints of the ACIO monad allow creation to occur at any time before the first attempt to read or write the IORef.
The other syntaxes proposed don't strike me as sufficiently rigorous.
Me neither. It's always been a great source of puzzlement to me why this very simple and IMO conservative proposal should be so controversial. Unless someone can point out some severe semantic difficulty or suggest something better it seems like a no-brainer to me.
This is going to be highly subjective, but to me it still doesn't feel like it falls under the bar for implementation cost given its usefulness. The new syntax requires additions all the way through the front end of the compiler: parser, abstract syntax, renamer, type checker, desugarer, for something that is rarely used. It's a first-class language construct (a new top-level binding form, no less), and it has to pay its way. Also you want to add the ACIO monad as a built-in to the language. Not that my gut feeling should in any way be considered the final word on the subject, but I thought I should say something about why we're not running to implement this right now. To me it seems like we should let it simmer some more. Cheers, Simon

On 24/05/07, Adrian Hey
Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all).
You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time. -- -David House, dmhouse@gmail.com

David House wrote:
On 24/05/07, Adrian Hey
wrote: Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all).
You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time.
There seems to be quite a few implicit (and incorrect) assumptions in your argument, which is fallacious IMO. The logic of your argument would imply that *no* top level expression can be evaluated at compile time. This might be the case with ghc, though I doubt it (and even if it was this would just be a ghc problem). BTW, the Haskell standard says nothing about any kind of heap, let alone a C style malloc. Regards -- Adrian Hey

On 2007-05-24, David House wrote:
On 24/05/07, Adrian Hey
wrote: Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all).
You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time.
You can allocate bss or data space at compile time for the executable you are compiling. (Well, if you read compile as compile and link. It's a bit fuzzy.) -- Aaron Denney -><-

Aaron Denney wrote:
On 2007-05-24, David House wrote:
On 24/05/07, Adrian Hey
wrote: Or even at compile time (which is why I think it's reasonable to regard operations like newIORef etc.. as not really being "IO" operations at all). You can allocate heap space at compile time? (Well, I guess you could, but that wouldn't still be usable at run time...) I imagine newIORef as mallocing() some room, then returning a pointer to that memory. That doesn't seem like something that could be done at compile time.
You can allocate bss or data space at compile time for the executable you are compiling. (Well, if you read compile as compile and link. It's a bit fuzzy.)
Well we don't need to get too bogged down with the details of how any particular compiler/linker/rts might work. The point being that with any.. myIORef <- newIORef initialExpression whether or not it's at the top level, the only information needed to create the IORef is the initialExpression, and if it's at the top level then this is available at compile time (it doesn't even have to be evaluated at compile time in order to create the IORef). But it doesn't require any information from, nor should it have any effect on, the outside world that an executing program is interacting with. It is conceivable that for some newIORef implementations this would not be true, but in that case it's difficult to see how such implementations could safely put their newIORef in the ACIO monad anyway. Regards -- Adrian Hey

Adrian Hey wrote:
Also, I don't know if the OP was a noob, but telling people (especially noobs) that if they can't figure out how to solve a problem without using a "global variable" then that must be down to inexperience and general cluelessness on their part just seems wrong to me. It simply isn't true.
Then what is right and what is true?

Albert Y. C. Lai wrote:
Adrian Hey wrote:
Also, I don't know if the OP was a noob, but telling people (especially noobs) that if they can't figure out how to solve a problem without using a "global variable" then that must be down to inexperience and general cluelessness on their part just seems wrong to me. It simply isn't true.
Then what is right and what is true?
I see no need two answer this again I believe I have already made my views perfectly clear already and provided ample evidence to justify them. Surely I don't need to do it again? If you disagree with any of them then feel free to explain why. Regards -- Adrian Hey

| I see no need two answer this again I believe I have already made my | views perfectly clear already and provided ample evidence to justify | them. Surely I don't need to do it again? Is there a Wiki page about this somewhere? Often email gets into a loop because not everyone reads everything on Haskell Cafe. (There's just too much of it.) When that happens, a good thing do to is to summarise the various positions on a Wiki page, so the debate can progress by refining text rather than by repeating it. There is the additional advantage that someone coming along later can still make sense of the debate. Simon

On 21-mei-2007, at 9:31, Simon Peyton-Jones wrote:
| I see no need two answer this again I believe I have already made my | views perfectly clear already and provided ample evidence to justify | them. Surely I don't need to do it again?
Is there a Wiki page about this somewhere? Often email gets into a loop because not everyone reads everything on Haskell Cafe. (There's just too much of it.) When that happens, a good thing do to is to summarise the various positions on a Wiki page, so the debate can progress by refining text rather than by repeating it. There is the additional advantage that someone coming along later can still make sense of the debate.
As I am sure Adrian would tell you were he awake: http://www.haskell.org/haskellwiki/Top_level_mutable_state With regards, Arthur van Leeuwen -- /\ / | arthurvl@cs.uu.nl | Work like you don't need the money /__\ / | A friend is someone with whom | Love like you have never been hurt / \/__ | you can dare to be yourself | Dance like there's nobody watching

On Thu, May 17, 2007 at 11:00:18PM +0100, Jules Bean wrote:
I'm not sure that's quite to the point. Clearly we can set up state at the top of our main action:
main = do sockstate <- initSocks graphstate <- initGraphics ... disposeGraphics graphstate disposeSocks sockstate exit
Voila. Mutable state which persists for our entire program.
Arguably it's a pain passing around the state explicitly. Alternatively, you can argue that passing this stuff around explicitly makes code much easier to reason about. And we know a dozen tricks to hide this stuff (reader monads, state monads, withSocketsDo-style bracket constructs).
So I don't think this is really the issue, is it?
As I understood it, the issue was more about whether or not *library* modules should be allowed to some 'set up' initialisation code to run at the beginning of 'main' to start up their own global state. I was never convinced this was a nice idea (I don't like the thought than an 'import' alone can add hidden IO actions to main). Mind you, I'm not convinced it's wrong, either. I think it's a hard one.
indeed. the whole issue is libraries. global state need not be visible to users, but is certainly useful when hidden inside librarys to provide efficient purely functional veneers for internal algorithms. I think restricting the actions to ones that don't have externally visible effects is the way to go here for a couple reasons. * I would very much hate for 'import' to have effects in and of itself. * it would disallow (or expose) the obvious 'lazy' approach to top-level IO, where you don't actually perform the IO until the first time the value is needed, then memoize it. so, some sort of restricted ACIO monad is in order. Also, there is the somewhat related 'ForeignData' proposal of mine, the syntax is pretty much off the top of my head, but I think something like this needs to go into the haskell FFI, http://hackage.haskell.org/trac/haskell-prime/wiki/ForeignData especially since it will sever the last need for C, allowing fully native haskellp programs with the full power of C code. (plus, lots of optimizatuons are available to the compiler when it sees the definitions like this and it is really easy to implement) John
-- John Meacham - ⑆repetae.net⑆john⑈

On 5/17/07, Adrian Hey
Jules Bean wrote:
BTW, this is the commonly the subject of flame wars on the Haskell mailing lists because there appear to be many who passionately believe and assert that so called "global variables" are (at best) unnecessary and (at worst) are "evil". These people are quite simply wrong and should be ignored :-)
Adrian Hey is not only wrong, but actually evil. He should be ignored. :-)
I am right, I might well be evil, and if past experience is anything to go by I already know that I will be ignored. We've been talking about this problem for years, but nothing is ever done about it (a solution to this problem isn't even on the agenda for Haskell' AFIAK).
The above hack is not actually Haskell. It's a hack, and it depends on the particular implementation characteristics of GHC. It is not unreasonable to imagine that a future GHC might contain different compilation techniques (I hesitate to use the word 'optimisations' because that sounds like something easy to turn off) which invalidate the technique in other ways or make it dangerous.
Well of course, that's why something needs to be done about this. Just being in a state of complete denial regarding the reality of this problem won't make it go away.
They are necessary because they are the only way to ensure important safety properties of many IO APIs.
That's a bold claim. It's very hard to prove that things don't exist. (That is, that other ways to ensure these safety properties don't exist). In snipped text you comment that the problems are often in low-level FFI library code: this makes me wonder if the real culprit doesn't lie at the FFI-haskell boundary. Perhaps there are good ways to specify this kind of invariant there.
No. Even if we stripped away all other code apart from the Haskell rts itself (OS, device drivers etc) and performed your IO entirely in Haskell (just using peek and poke on bare hardware), you'd still need top level mutable state to implement common IO API's (e.g. The socket API, does anybody really believe this is entirely stateless?).
At this point in the discussion I always think Haskell could probably take a lesson from the evolution of object oriented programming. As I was taught, people starting to see modules as an important abstraction (yay, Haskell has those). Then people started to also realize that instead of just modules it would be useful to have abstract data types which could be instantiated many times and sort of encapsulate the things (state) you might store in a module (yay, Haskell has ADTs). Eventually, people put the two together, data types that also encapsulated functionality. Around this time, it was sort of like having modules that could be instantiated many times instead of once per program and objects were essentially born. Well, it seems to me that Haskell modules are actually very similar to singletons. Perhaps all these problems with modules having top level mutable state could be solved if Haskell modules were parameterizable at "instantiation"? I'm not saying we should turn the Haskell module system into an OO system, just that maybe it would be wise to borrow some ideas from that paradigm. Jason

Jason Dagit wrote:
On 5/17/07, Adrian Hey
wrote: Jules Bean wrote:
BTW, this is the commonly the subject of flame wars on the Haskell mailing lists because there appear to be many who passionately believe and assert that so called "global variables" are (at best) unnecessary and (at worst) are "evil". These people are quite simply wrong and should be ignored :-)
For the record, there are also a few who do not believe that global variables even exist. A best case scenario to save the term requires that you can prove the finite bounds of our physical universe. Generally, those same people also believe that it is this oversight of both parties that ensures that the so-called "flame war" persists, since declaring something "global" also requires a declaration of the domain of discourse, which is decided arbitrarily and shifts many times throughout any real attempt to resolve the issue. Ever heard the phrase, "of course it works! it works on my machine!"? Tony Morris http://tmorris.net/

| >> and (at worst) are "evil". These people are quite simply wrong and | >> should be ignored :-) | > | > Adrian Hey is not only wrong, but actually evil. He should be ignored. :-) | | I am right, I might well be evil, and if past experience is anything to | go by I already know that I will be ignored. We've been talking about | this problem for years, but nothing is ever done about it I know that Jules preceded his remarks by saying that they were lighthearted, but I'd like to urge moderation in language. One of the best things about the Haskell community is that politeness is pretty much universal. Email is too fragile a medium to sustain the wry smile that can accompany an in-person conversation. Also Adrian, you may feel ignored, but I don't think that's really so. For example, I was looking back at your ACIO mail a couple of months ago, when I was thinking about concurrency. Not immediately achieving a critical mass behind a language change is not the same as being ignored. One of the good things about Haskell is that we put up with woefully inadequate situations (such as the total lack of sensible I/O in early lazy languages) because we can't yet find a solution that feels satisfying. To say that "nothing is ever done about it" implies that there are clear things that could be done, but I don't think that is so (yet). And sometimes people don't reply because they are just busy, or because they don't have anything useful to say. In short, don't be discouraged. Keep identifying problems, suggesting solutions, maintaining Wiki pages that summarise both, and so on. There are lots of bright people on this mailing list, and sooner or later an "aha" moment will happen. Simon

Simon Peyton-Jones wrote:
For example, I was looking back at your ACIO mail a couple of months ago, when I was thinking about concurrency.
Actually, this is Ian Starks proposal.. http://www.haskell.org/pipermail/haskell-cafe/2004-November/007664.html ..but is one with which I agree. I just wrote some stuff on the wiki about this actually solved some currently insoluble problems.
Not immediately achieving a critical mass behind a language change is not the same as being ignored.
Unfortunately the situation seems worse than this. AFAICT we haven't even got a consensus that that there is a real problem here that needs any kind of solution :-) Regards -- Adrian Hey

On Thu, May 17, 2007 at 07:25:03PM +0100, Adrian Hey wrote:
The above hack is not actually Haskell. It's a hack, and it depends on the particular implementation characteristics of GHC. It is not unreasonable to imagine that a future GHC might contain different compilation techniques (I hesitate to use the word 'optimisations' because that sounds like something easy to turn off) which invalidate the technique in other ways or make it dangerous.
Well of course, that's why something needs to be done about this. Just being in a state of complete denial regarding the reality of this problem won't make it go away.
Indeed, I made a proposal a while ago to allow top level IO actions, foo <- newIORef "foo" which are quite straigtforward, and would not even be inconsistant in the view of the type system, as <- bindings are always monomorphic anyway. Others pointed out that if we allow arbitrary IO actions, it could cause undesirable things like causing importing a mobule to change program behavior, or expose implementatino details, like when these are executed, right away or lazily. The solution was to have a special restricted version of IO containing only those actions that are "safe". like newMVar etc.. where safe means more or less commutative and omittable. this doesn't require any special support, just a newtype ACIO a = ACIO (IO a) deriving(Monad,Functor) and then have a module only export the trusted things in the ACIO monad. John -- John Meacham - ⑆repetae.net⑆john⑈

Eric
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
No, no-one does. Global variables are neither simple nor straightforward. :-P In addition to what others have said (assuming you don't just mean providing a name for a constant¹), to avoid the problems caused by global variables is one of the reasons for using a functional language. [1] as in
e = exp 1 -- Jón Fairbairn Jon.Fairbairn@cl.cam.ac.uk

eeoam:
H|i,
Does anyone know of a simple and straightforward way to use global variables in Haskell?
E.
As other posters have said, you'll need to state what you're trying to do. For one particular case, that of a program that needs access to state over its lifetime, State monads are used. Here's a small example: -- import global variable support import Control.Monad.State -- declare our global variables. here we just have a single Int type Global = Int -- initialise them on startup main = print (evalState main' 7) -- 7 is the default value -- code that runs with access to these globals, note that its type -- indicates that it uses globals: main' :: State Global Int main' = do i <- get -- get and modify the global a few times put (i ^ 2) modify $ \i -> i `div` 2 i <- get return i We can run this: $ runhaskell A.hs 24 -- Don
participants (22)
-
Aaron Denney
-
Adrian Hey
-
Albert Y. C. Lai
-
Arthur van Leeuwen
-
Bulat Ziganshin
-
David House
-
dons@cse.unsw.edu.au
-
Dougal Stanton
-
Eric
-
Henning Thielemann
-
Isaac Dupree
-
Jason Dagit
-
John Meacham
-
Jules Bean
-
Jón Fairbairn
-
Robin Green
-
Simon Marlow
-
Simon Peyton-Jones
-
Stefan O'Rear
-
Taral
-
Tom Harper
-
Tony Morris