
A while back I asked how to make the Ref's from Koen Clasessen's PhD thesis Ord-able for the purpose of making them keys for efficient finite maps.
Koen quickly responded with a clever implementation which attaches the values to the keys. While I don't rule out eventually making use of it, this solution has the drawback of requiring lookups in the finite map to be inside the ST or IO monad.
Josef Svenningsson asked if I had tried adding {-# NOINLINE refWInt #-} I had (quite hopefully.) It doesn't work. After fiddling a bit to get a sense of what works and what doesn't, I tried: {-# INLINE refWInt #-} . This does the trick! I've included the working code (which differs from that of my original message only by the addition of the INLINE directive) below.
My question to the GHC implementors is: what's going on here? Can you give us (at least Josef and I are confused) any help in predicting how unsafePerformIO will behave?
I'm afraid the answer is just "unsafePerformIO is called *unsafe*PerformIO for a reason"! You're using it in an inherently unsafe way here - the result of the program depends on whether the compiler duplicates the expression or not, something which it is normally free to do without affecting the meaning of the program. However, it is possible to have global top-level references using unsafePerformIO if you're very careful about it. In GHC we do something like this: {-# NOINLINE global_var #-} global_var :: IORef Int global_var = unsafePerformIO (newIORef 42) the NOINLINE pragma is used to ensure that there is precisely *one* copy of the right hand side of global_var in the resulting program (NOTE: you also need to compile the program with -fno-cse to ensure that the compiler doesn't also common up the RHS of global_var with other similar top-level definitions). Cheers, Simon