
The problem is simple enough to restate for anyone who's interested. "Provide a simple reliable mechanism to ensure that in a given program run one particular top level IO operation cannot be executed more than once." No language can guarantee this - all I have to do is run 2 copies of the executable at once... or wven sequentially! Read what I wrote :-)
oh well, if you insist on overspecification: Loading package base ... linking ... done. Prelude> let once io = getContents >> io Prelude> let init = once $ putStrLn "okay" Prelude> init okay Prelude> init *** Exception: <stdin>: hGetContents: illegal operation (handle is closed Prelude> init *** Exception: <stdin>: hGetContents: illegal operation (handle is closed Prelude> this method may have some unexpected side-effects, but you don't mind that, do you?-) unsafePerformIO is a wonderful extension hook for making Haskell implementations do things they wouldn't normally do without having to write such an implementation from scratch. the problem is that those extended Haskell implementations may then do things they wouldn't normally do.. for instance, you shouldn't bet your life on your method working with all future Haskell implementations - you'll have to check with every new release whether your use of the extension hook is still compatible with whatever other progress has been made (e.g., a distributed implementation may decide to start with copies of the code on each node, etc.). today you can say {-# please don't mess with this #-}, or if your compiler is a bit more eager, you may have to say {-# please, please don't mess with this #-}, and it may just work most of the time as long as everybody remembers that there are these user-defined extensions hanging around that will break in horrible ways if we forget that we've left the domain of pure functional programming. i thought the point of this thread was to look for a way to take one particular use pattern of unsafePerformIO that is deemed to be safe, and to devise a proper language extension that captures exactly this use pattern in such a way that no unsafe constructs need be involved anymore. iirc, this use pattern started out as being global variables, then became IO initialisers, then IO initialisers per module, then commutative monads, then merging of IO and ST, then run-once code, .. you won't be able to capture all uses of unsafePerformIO unless you recreate it, which is exactly what you don't want - it is there already, and you want to find ways not having to use it. your example is still useful because it describes a situation at the borderline between the functional and IO worlds where one is tempted to use global variables. as has been pointed out, the reason in this particular case is that one might want to do something in Haskell-land that should perhaps be done in the outside world, because the whole point of the exercise is to make something behave as a functional object when it is not. now one could argue that things should be converted to a functional point of view before importing them into Haskell, or one could argue that as much as possible should be done on the Haskell side, even if that means compromising the language a little or balancing the library author over an abyss.both arguments have their merit. afaik, the main problem that people try to solve with global variable tricks is not executing code (you could call an init action in main), but having to distribute the results of running that code. as others have pointed out, that is similar to the situation with stdin/etc - you want to open the channels *and* make the resulting handles available everywhere. now, if every module by default had a stdinitMVar, you could do your initialisation in main and put the results into Main.stdinitMVar. and if you wanted to forward the information to an imported module, you could put the info into Module.stdinitMVar. and if you wanted per-module initialisation, you'd use Main.init to call Module.init (name init just a convention), which would put its results into its very own Module.stdinitMVar. problem solved. problem solved? i'm not so sure about that, for the same reasons global variables/registers/etc. have been considered evil by many of who reinvented them.and shouldn't multiple instances of modules be possible, each with its own stdinitMVar? but some of the proposals that have been circulating in this thread are even worse, as they include an arbitrary number of user-defined and -named initialisation variables, and arbitrary numbers of initilisation actions, to be called in some underspecified form and sequence, making them hard to predict and find for those having to maintain such code. there are actually at least *two* problems you need to solve: one is providing for those few cases where global-variable-like things are too convenient to consider anything else. that's actually fairly easy. the other is to make sure that the cases in which people consider using your mechanism are as limited as possible, for otherwise people will use them for everything (like the IO monad). that temptation is there because such things are too convenient at the start to be worried about the terrible inconveniences that appear later. it is this second problem people have failed to solve so far in every variation of the scheme. which is why so many in this thread have been burned by someone who abused one of those wonderfully convenient mechanisms. examples from non-functional languages have been mentioned. another is Erlang, where (by convention) processes are instances of modules which (by convention) tend to have init functions, and each process has a process dictionary (a collection of process-local variables). that feature used to be very popular, but its use is now heavily discouraged (although they have the additional difficulty of not distinguishing between IO and non-IO code..): http://www.erlang.se/doc/programming_rules.shtml#REF18861 hth, claus