| In the meantime, I'm glad we have got a new function | atomicModifyIORef | which I for one will use, when it gets into GHC's regular release. Just before this gets out of the door... any chance of calling it modifyIORef and documenting that it's atomic? Sometimes names can get too long! Not a big deal though. Simon
Just before this gets out of the door... any chance of calling it
modifyIORef
and documenting that it's atomic? Sometimes names can get too long!
There's two reasons you might use the function: convenience and atomicity. I can easily imagine someone modifying a program, thinking that it was used for convenience, replacing it with non-atomic 'equivalent' code and breaking the code in a way that will take a lot of testing to find. Being explicit that it has this special, important property protects against that. But what if I am only using it for convenience and I want to document that fact? One could make a good case for adding plain modifyIORef which may or may not be atomic. Or, we can avoid the issue altogether by not adding an atomicity guarantee. If you use concurrency, use MVars, if you don't use concurrency, you don't need it. -- Alastair
Simon Peyton-Jones wrote:
| In the meantime, I'm glad we have got a new function | atomicModifyIORef | which I for one will use, when it gets into GHC's regular release.
Just before this gets out of the door... any chance of calling it
modifyIORef
and documenting that it's atomic
My vote goes for modifyIORef :: IORef a -> (a -> (a,b)) -> IO b modifyIORef_ :: IORef a -> (a -> a) -> IO a modifyIORef_ ioRef fn = modifyIORef ioRef (\ a0 -> let a1 = fn a0 in (a1,a1)) The names are similar to those of GHC's modifyMVar and modifyMVar_. I think there's some sense in providing modifyIORef_ as well as modifyIORef, as I think for some implementations it will be even faster, since it's unnecessary to create thunks for (fst (fn a0)) and (snd (fn a0)). But you lot know a lot more about writing Haskell compilers than I do . . . Thus you can implement a counter returning integers 1,2,3,... via newCounter :: IO (IO Integer) newCounter = do ioRef <- newIORef 0 return (modifyIORef_ ioRef (+1)) I don't think it's necessary for the name to include "atomic". We are unlikely ever to want a non-atomic version of atomicModifyIORef, especially as such a thing would be quite dangerous, and you can roll your own from readIORef and writeIORef.
George Russell wrote:
Simon Peyton-Jones wrote:
| In the meantime, I'm glad we have got a new function | atomicModifyIORef | which I for one will use, when it gets into GHC's regular release.
Just before this gets out of the door... any chance of calling it
modifyIORef
and documenting that it's atomic
My vote goes for
modifyIORef :: IORef a -> (a -> (a,b)) -> IO b modifyIORef_ :: IORef a -> (a -> a) -> IO a
modifyIORef_ ioRef fn = modifyIORef ioRef (\ a0 -> let a1 = fn a0 in (a1,a1))
The names are similar to those of GHC's modifyMVar and modifyMVar_. I think there's some sense in providing modifyIORef_ as well as modifyIORef, as I think for some implementations it will be even faster, since it's unnecessary to create thunks for (fst (fn a0)) and (snd (fn a0)). But you lot know a lot more about writing Haskell compilers than I do . . .
Thus you can implement a counter returning integers 1,2,3,... via
newCounter :: IO (IO Integer) newCounter = do ioRef <- newIORef 0 return (modifyIORef_ ioRef (+1))
I don't think it's necessary for the name to include "atomic". We are unlikely ever to want a non-atomic version of atomicModifyIORef, especially as such a thing would be quite dangerous, and you can roll your own from readIORef and writeIORef.
I like the idea of consistency (in naming, signatures, and semantics) between the sets of functions manipulating IORefs and MVars. Here's the scheme I would prefer for the "modify" subset of these functions. Existing functions are labeled "(e)", proposed functions "(p)". "(*)" indicates an existing function with a different interface. (e) modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b (e) modifyMVar_ :: MVar a -> (a -> IO a) -> IO () (p) atomicModifyMVar :: MVar a -> (a -> (a, b)) -> IO b (p) atomicModifyMVar_ :: MVar a -> (a -> a) -> IO () (*) modifyIORef :: IORef a -> (a -> IO (a, b)) -> IO b (p) modifyIORef_ :: IORef a -> (a -> IO a) -> IO () (p) atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b (p) atomicModifyIORef_ :: IORef a -> (a -> a) -> IO () Notes: * I prefer having the "*modify*_" functions return IO (), despite the fact that it means the newCounter example George gives becomes more complicated. * I strongly agree with Alastair that an atomic function should be so labeled. * The nonatomic functions are all simply convenient combinations of other functions. As such, they could be dispensed with. Even if they were dispensed with, however, I would recommend against using their (without-"atomic") names for the atomic functions, because I think the natural interfaces for the nonatomic names are the ones given above. * In fact, only atomicModifyMVar and atomicModifyIORef are primitive. * The existence of modifyIORef with the "wrong" interface is an unfortunate problem with this scheme. -- Dean
Dean Herington wrote (snipped)
Here's the scheme I would prefer for the "modify" subset of these functions. Existing functions are labeled "(e)", proposed functions "(p)". "(*)" indicates an existing function with a different interface.
(e) modifyMVar :: MVar a -> (a -> IO (a, b)) -> IO b (e) modifyMVar_ :: MVar a -> (a -> IO a) -> IO () (p) atomicModifyMVar :: MVar a -> (a -> (a, b)) -> IO b (p) atomicModifyMVar_ :: MVar a -> (a -> a) -> IO ()
(*) modifyIORef :: IORef a -> (a -> IO (a, b)) -> IO b (p) modifyIORef_ :: IORef a -> (a -> IO a) -> IO () (p) atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b (p) atomicModifyIORef_ :: IORef a -> (a -> a) -> IO ()
Yes, why shouldn't we have atomicModifyMVar? Of course it will block if the MVar is empty, but that doesn't seem particularly important. I know Alastair thinks this is only a question of efficiency. To me though efficiency is important. Also I like the idea of replacing two primitives (readIORef and writeIORef) by one; you can define all accesses to IORefs in terms of atomicModifyIORef.
participants (4)
-
Alastair Reid -
Dean Herington -
George Russell -
Simon Peyton-Jones