ref <- newIORef 0 replicateM_ 1000000 $ modifyIORef ref (+1) readIORef ref >>= printTo avoid this problem, use modifyIORef' instead.
>>> :{ runST (do ref <- newSTRef "" modifySTRef ref (const "world") modifySTRef ref (++ "!") modifySTRef ref ("Hello, " ++) readSTRef ref ) :} "Hello, world!"Be warned that modifySTRef does not apply the function strictly. This means if the program calls modifySTRef many times, but seldom uses the value, thunks will pile up in memory resulting in a space leak. This is a common mistake made when using an STRef as a counter. For example, the following will leak memory and may produce a stack overflow:
>>> import GHC.Internal.Control.Monad (replicateM_) >>> :{ print (runST (do ref <- newSTRef 0 replicateM_ 1000 $ modifySTRef ref (+1) readSTRef ref )) :} 1000To avoid this problem, use modifySTRef' instead.
atomicModifyIORef ref f = do -- Begin atomic block old <- readIORef ref let r = f old new = fst r writeIORef ref new -- End atomic block case r of (_new, res) -> pure resThe actions in the section labeled "atomic block" are not subject to interference from other threads. In particular, it is impossible for the value in the IORef to change between the readIORef and writeIORef invocations. The user-supplied function is applied to the value stored in the IORef, yielding a new value to store in the IORef and a value to return. After the new value is (lazily) stored in the IORef, atomicModifyIORef forces the result pair, but does not force either component of the result. To force both components, use atomicModifyIORef'. Note that
atomicModifyIORef ref (_ -> undefined)will raise an exception in the calling thread, but will also install the bottoming value in the IORef, where it may be read by other threads. This function imposes a memory barrier, preventing reordering around the "atomic block"; see Data.IORef#memmodel for details.
atomicModifyIORef' ref f = do -- Begin atomic block old <- readIORef ref let r = f old new = fst r writeIORef ref new -- End atomic block case r of (!_new, !res) -> pure resThe actions in the "atomic block" are not subject to interference by other threads. In particular, the value in the IORef cannot change between the readIORef and writeIORef invocations. The new value is installed in the IORef before either value is forced. So
atomicModifyIORef' ref (x -> (x+1, undefined))will increment the IORef and then throw an exception in the calling thread.
atomicModifyIORef' ref (x -> (undefined, x))and
atomicModifyIORef' ref (_ -> undefined)will each raise an exception in the calling thread, but will also install the bottoming value in the IORef, where it may be read by other threads. This function imposes a memory barrier, preventing reordering around the "atomic block"; see Data.IORef#memmodel for details.
atomicModifyMutVar# :: MutVar# s a -> (a -> (a, b)) -> State# s -> (# State# s, b #)but there may be code that uses this with other two-field record types.