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

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

On Mon, Apr 08, 2002 at 11:58:22AM +0100, Simon Marlow wrote:
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).
this usage of unsafePerformIO is such a staple of real-world Haskell programming, it seems there should be some language (or experemental compiler *wink wink ghc nudge*) support for it. I am not sure what form it would take though. One idea I was toying with that would actually just require a new standard library function would be perhaps a 'safer' form of unsafePerformIO called memoIO :: IO a -> IO a which takes an arbitrary io action and returns an action which only calls the original action once and from then on returns the exact same value it returned the first time. it seems that this would allow much of what people want to do with unsafePerformIO, but in a nicer way as all io operations stay io operations, but the functional mapping of an io action to a memoed io action can be used anywhere. you can then do the above like getGlobalVar :: IO (IORef Int) getGlobalVar = memoIO (newIORef 42) note that this is not exactly the same since getting the global var is in the io monad, but that really makes sense if you think about it. and chances are you are already in IO if you need an IORef. John -- --------------------------------------------------------------------------- John Meacham - California Institute of Technology, Alum. - john@repetae.net ---------------------------------------------------------------------------
participants (2)
-
John Meacham
-
Simon Marlow