{-#LANGUAGE MultiParamTypeClasses #-}
{-#LANGUAGE FlexibleContexts #-}
{-#LANGUAGE FlexibleInstances #-}
{-#LANGUAGE TypeOperators #-}
{-#LANGUAGE FunctionalDependencies #-}

module Control.OOP.MonadVar
where

import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Proxy

-- | Generalized read-only access to mutable variables
class Monad m => MonadConstVar v m | m -> v where
  readVar :: v a -> m a

-- | Generalized mutable variables
class (Monad m, MonadConstVar v m) => MonadVar v m | m -> v where
  newVar :: a -> m (v a)
  writeVar :: a -> v a -> m ()

  modifyVar :: (a -> a) -> v a -> m ()
  swapVar :: a -> v a -> m a
  modifySwapVar :: (a -> a) -> v a -> m a

  transaction :: Proxy v -> m a -> m a

  modifyVar f var =
    modifySwapVar f var >> return ()

  swapVar val' =
    modifySwapVar (const val')

  modifySwapVar f var = do
    val <- readVar var
    writeVar (f val) var
    return val

instance MonadIO m => MonadConstVar IORef m where
  readVar = liftIO . readIORef
  
instance MonadIO m => MonadVar IORef m where
  newVar = liftIO . newIORef
  writeVar v = liftIO . flip writeIORef v
  transaction _ = id