ref-extras-0.2.0: src/RefExtras/Classes.hs
-- | Classes that generalize operations on mutable references.
module RefExtras.Classes where
import Control.Concurrent.STM.TVar (TVar, readTVar, writeTVar)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.ST (ST)
import Control.Monad.STM (STM)
import Data.STRef (STRef, readSTRef, writeSTRef)
import LittleRIO (SomeRef, readSomeRef, writeSomeRef)
import UnliftIO.IORef (IORef, atomicModifyIORef', modifyIORef', readIORef, writeIORef)
-- | A reference that we can read and write, but not necessarily without
-- contending with intervening mutations.
class ReadWriteRef r m where
readRef :: r a -> m a
writeRef :: r a -> a -> m ()
-- | /Unsafe/ in the sense that in most cases we can implement modify by
-- reading then writing, but we're not guaranteed that no writes have occurred in
-- the meantime. This is true for IORefs, which is why we need a separate core method.
-- It is not true for STM, which means this is actually safe for TVars.
unsafeModifyRef :: (Monad m, ReadWriteRef r m) => r a -> (a -> a) -> m ()
unsafeModifyRef ref f = do
a <- readRef ref
let a' = f a
writeRef ref $! a'
-- | See notes on safety for 'unsafeModifyRef'.
unsafeAtomicModifyRef :: (Monad m, ReadWriteRef r m) => r a -> (a -> (a, b)) -> m b
unsafeAtomicModifyRef ref f = do
a <- readRef ref
let (a', b) = f a
writeRef ref $! a'
pure $! b
-- | A reference that offers the ability to mutate with a pure function.
-- However, we might not be able to observe the result without intervening mutations.
class ReadWriteRef r m => ModifyRef r m where
modifyRef :: r a -> (a -> a) -> m ()
-- | A reference that we can mutate *and observe* with a pure function.
class ModifyRef r m => AtomicRef r m where
atomicModifyRef :: r a -> (a -> (a, b)) -> m b
-- | Atomically swap a value in a reference.
swapRef :: AtomicRef r m => r a -> a -> m a
swapRef r a = atomicModifyRef r (\z -> (a, z))
instance MonadIO m => ReadWriteRef IORef m where
readRef = readIORef
writeRef = writeIORef
instance MonadIO m => ModifyRef IORef m where
modifyRef = modifyIORef'
instance MonadIO m => AtomicRef IORef m where
atomicModifyRef = atomicModifyIORef'
instance ReadWriteRef (STRef s) (ST s) where
readRef = readSTRef
writeRef = writeSTRef
instance ReadWriteRef TVar STM where
readRef = readTVar
writeRef = writeTVar
-- These /unsafe/ impls are safe for TVars because STM guarantees that
-- the vars don't change between reading and writing.
instance ModifyRef TVar STM where
modifyRef = unsafeModifyRef
instance AtomicRef TVar STM where
atomicModifyRef = unsafeAtomicModifyRef
instance MonadIO m => ReadWriteRef SomeRef m where
readRef = readSomeRef
writeRef = writeSomeRef