-- | Mutable variables in the IO Monad.
module Data.Variable where

import Data.IORef
import Control.Concurrent.MVar
import Control.Concurrent.STM

class Variable v where
   -- | Create a new variable.
   newVar     :: a -> IO (v a)
   -- | Read a variable.
   readVar    :: v a -> IO a
   -- | Write a variable.
   writeVar   :: v a -> a -> IO ()
   -- | Modify a variable.
   modifyVar  :: v a -> (a -> a) -> IO ()
   -- | Modify a variable, and return some value.
   modifyVar' :: v a -> (a -> (a,b)) -> IO b

instance Variable IORef where
   newVar     = newIORef
   readVar    = readIORef
   writeVar   = writeIORef
   modifyVar  = modifyIORef
   modifyVar' = atomicModifyIORef

instance Variable MVar where
   newVar         = newMVar
   readVar        = takeMVar
   writeVar       = putMVar
   modifyVar v f  = modifyMVar_ v (return . f)
   modifyVar' v f = modifyMVar v (return . f)

instance Variable TVar where
   newVar         = newTVarIO

   readVar        = readTVarIO

   writeVar v x   = atomically $ writeTVar v x

   modifyVar v f  = atomically $ do x <- readTVar v
                                    writeTVar v (f x)

   modifyVar' v f = atomically $ do x <- readTVar v
                                    let (x', y) = f x
                                    writeTVar v x'
                                    return y

instance Variable TMVar where
   newVar         = newTMVarIO

   readVar v      = atomically $ takeTMVar v

   writeVar v x   = atomically $ putTMVar v x

   modifyVar v f  = atomically $ do x <- takeTMVar v
                                    putTMVar v (f x)

   modifyVar' v f = atomically $ do x <- takeTMVar v
                                    let (x', y) = f x
                                    putTMVar v x'
                                    return y