Skip to content

The second and third implementation of atomicModifyIORef' does not keep the semantics of the first implementation

Summary

  • The first implementation of atomicModifyIORef' (by Joey Adams #5926 (closed)) installs a new value to IORef before it is forced.
  • It seems to me that the second implementation (by parcs #8345 (closed)) installs a new value after it is forced.
  • Also, the third implementation (by @treeowl) installs a new value after it is forced.

Cc: @simonmar @simonpj @hvr @carter

Steps to reproduce

Load the following file with GHCi:

{-# LANGUAGE BangPatterns #-}

import GHC.IORef
import Debug.Trace
import Data.IORef

f n = (trace "tupple" ((trace "new" n + 1), (trace "ret" ())))

joey ref f = do
    b <- atomicModifyIORef ref
            (\x -> let (a, b) = f x
                    in (a, a `seq` b))
    putStrLn "swap"
    b `seq` return b

parcs ref f = do
    b <- atomicModifyIORef ref $ \a ->
            case f a of
                v@(a',_) -> a' `seq` v
    putStrLn "swap"
    b `seq` return b

david ref f = do
    (_old, (_new, !res)) <- atomicModifyIORef2 ref $
        \old -> case f old of
            r@(!_new, _res) -> r
    putStrLn "swap"
    pure res

And type:

> ref <-newIORef (1::Int)

> joey ref f
swap
tupple
new
ret

> parcs ref f
tupple
new
swap
ret

> david ref f
tupple
new
ret
swap

Expected behavior

If I understand correctly, "new" must appear after "swap".

Environment

  • GHC version used: 9.4.4

Note

The following code also keeps the semantics of the first implementation:

kazu ref f = do
    b <- atomicModifyIORef ref $ \a ->
            case f a of
                (a, b) -> (a, a `seq` b)
    putStrLn "swap"
    b `seq` return b

I think that creating a new tuple cannot be avoided to keep the semantics.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information