Simon Marlow wrote: [snip]
Ok, so it relies crucially on the fact that the 'if' causes evaluation. (you might have written it to be more lazy, so that the pair was returned immediately without evaluating contents1 first, for example).
In that case, yes I agree it is horrible :)
[snip] Flattery like that will get you nowhere. However in general I think we can hide some of the horribleness from the user: modify2IORefs :: IORef a -> IORef b -> (a -> b -> (a,b,c)) -> IO c modify2IORefs ioRefA ioRefB updateFn = do res <- atomicModifyIORef ioRefA (\ a0 -> unsafePerformIO (do atomicModifyIORef ioRefB (\ b0 -> let (a1,b1,c) = updateFn a0 b0 in a0 `seq` b0 `seq` (b1,(a1,c)) ) ) ) block (res `seq` return res) Modulo whatever stupid type errors I have made, I think this should provide a safe atomic update of two IORefs, except that you can get non-termination should you set up circular dependencies. However the latter problem (except that you will get deadlock) can occur if you try to write modify2MVars :: MVar a -> MVar b -> (a -> b -> (a,b,c)) -> IO c and are I fear unsolvable, without a global lock. I am not sure if all those seqs are necessary, but as Simon PJ would say, my brain is hurting.