Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of the extra result parameter of atomicModifyIORef?

The signature of modifyIORef is straightforward enough:

modifyIORef :: IORef a -> (a -> a) -> IO ()

Unfortunately, this is not thread safe. There is an alternative that adresses this issue:

atomicModifyIORef :: IORef a -> (a -> (a,b)) -> IO b

What exactly are the differences between these two functions? How am I supposed to use the b parameter when modifying an IORef that might be read from another thread?

like image 875
leftaroundabout Avatar asked Sep 22 '16 13:09

leftaroundabout


3 Answers

As you stated in a comment, without concurrency you'd be able to just write something like

modifyAndReturn ref f = do
  old <- readIORef ref
  let !(new, r) = f old
  writeIORef r new
  return r

But in a concurrent context, someone else could change the reference between the read and the write.

like image 174
dfeuer Avatar answered Nov 09 '22 08:11

dfeuer


The extra parameter is used to provide a return value. For example, you may want to be able to atomically replace the value stored in a IORef and return the old value. You can do that like so:

atomicModifyIORef ref (\old -> (new, old))

If you don't have a value to return, you can use the following:

atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
atomicModifyIORef_ ref f =
    atomicModifyIORef ref (\val -> (f val, ()))

which has the same signature as modifyIORef.

like image 12
redneb Avatar answered Nov 09 '22 07:11

redneb


Here's how I understand this. Think of functions that follow the bracket idiom, e.g.

withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r

These function take a function as argument and return the return value of that function. atomicModifyIORef is similar to that. It takes a function as an argument, and the intention is to return the return value of that function. There is just one complication: the argument function, has also to return a new value to be stored in the IORef. Because of that, atomicModifyIORef requires from that function to return two values. Of course, this case is not completely similar with the bracket case (e.g. there is no IO involved, we are not dealing with exception safety, etc), but this analogy gives you an idea.

like image 2
safsaf32 Avatar answered Nov 09 '22 08:11

safsaf32