An answer and a question to GHC implementors [was Re: How to make Claessen's Refs Ord-able?]

Simon Marlow
Mon, 8 Apr 2002 11:58:22 +0100

> 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=20
>   {-# 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 =3D 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).