2015-02-09 18:30:54 +03:00
|
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
|
|
|
|
-- | This module provides an abstraction over 'STM', which can be used
|
|
|
|
-- with 'MonadConc'.
|
|
|
|
module Control.Monad.STM.Class where
|
|
|
|
|
|
|
|
import Control.Concurrent.STM (STM)
|
|
|
|
import Control.Concurrent.STM.TVar (TVar, newTVar, readTVar, writeTVar)
|
2015-02-11 21:54:04 +03:00
|
|
|
import Control.Exception (Exception)
|
2015-02-09 18:30:54 +03:00
|
|
|
import Control.Monad (unless)
|
2015-02-12 16:20:30 +03:00
|
|
|
import Control.Monad.Catch (MonadCatch, MonadThrow, throwM, catch)
|
2015-02-09 18:30:54 +03:00
|
|
|
|
|
|
|
import qualified Control.Monad.STM as S
|
|
|
|
|
|
|
|
-- | @MonadSTM@ is an abstraction over 'STM', in the same spirit as
|
|
|
|
-- 'MonadConc' is an abstraction over 'IO's concurrency.
|
|
|
|
--
|
|
|
|
-- This class does not provide any way to run transactions, rather
|
|
|
|
-- each 'MonadConc' has an associated 'MonadSTM' from which it can
|
|
|
|
-- atomically run a transaction.
|
|
|
|
--
|
|
|
|
-- A minimal implementation consists of 'retry', 'orElse', 'newCTVar',
|
|
|
|
-- 'readCTVar', and 'writeCTVar'.
|
2015-02-12 16:20:30 +03:00
|
|
|
class (Monad m, MonadCatch m, MonadThrow m) => MonadSTM m where
|
2015-02-09 18:30:54 +03:00
|
|
|
-- | The mutable reference type. These behave like 'TVar's, in that
|
|
|
|
-- they always contain a value and updates are non-blocking and
|
|
|
|
-- synchronised.
|
|
|
|
type CTVar m :: * -> *
|
|
|
|
|
|
|
|
-- | Retry execution of this transaction because it has seen values
|
|
|
|
-- in @CTVar@s that it shouldn't have. This may result in the thread
|
|
|
|
-- running the transaction being blocked until any @CTVar@s
|
|
|
|
-- referenced in it have been mutated.
|
|
|
|
retry :: m a
|
|
|
|
|
|
|
|
-- | Run the first transaction and, if it @retry@s, run the second
|
|
|
|
-- instead. If the monad is an instance of
|
|
|
|
-- 'Alternative'/'MonadPlus', 'orElse' should be the '(<|>)'/'mplus'
|
|
|
|
-- function.
|
|
|
|
orElse :: m a -> m a -> m a
|
|
|
|
|
|
|
|
-- | Check whether a condition is true and, if not, call @retry@.
|
|
|
|
--
|
|
|
|
-- > check b = unless b retry
|
|
|
|
check :: Bool -> m ()
|
|
|
|
check b = unless b retry
|
|
|
|
|
|
|
|
-- | Create a new @CTVar@ containing the given value.
|
|
|
|
newCTVar :: a -> m (CTVar m a)
|
|
|
|
|
|
|
|
-- | Return the current value stored in a @CTVar@.
|
|
|
|
readCTVar :: CTVar m a -> m a
|
|
|
|
|
|
|
|
-- | Write the supplied value into the @CTVar@.
|
|
|
|
writeCTVar :: CTVar m a -> a -> m ()
|
|
|
|
|
2015-02-11 21:54:04 +03:00
|
|
|
-- | Throw an exception. This aborts the transaction and propagates
|
|
|
|
-- the exception.
|
2015-02-12 16:20:30 +03:00
|
|
|
--
|
|
|
|
-- > throwSTM = throwM
|
2015-02-11 21:54:04 +03:00
|
|
|
throwSTM :: Exception e => e -> m a
|
2015-02-12 16:20:30 +03:00
|
|
|
throwSTM = throwM
|
2015-02-11 21:54:04 +03:00
|
|
|
|
|
|
|
-- | Handling exceptions from 'throwSTM'.
|
2015-02-12 16:20:30 +03:00
|
|
|
--
|
|
|
|
-- > catchSTM = catch
|
2015-02-11 21:54:04 +03:00
|
|
|
catchSTM :: Exception e => m a -> (e -> m a) -> m a
|
2015-02-16 06:37:16 +03:00
|
|
|
catchSTM = Control.Monad.Catch.catch
|
2015-02-11 21:54:04 +03:00
|
|
|
|
2015-02-09 18:30:54 +03:00
|
|
|
instance MonadSTM STM where
|
|
|
|
type CTVar STM = TVar
|
|
|
|
|
|
|
|
retry = S.retry
|
|
|
|
orElse = S.orElse
|
|
|
|
newCTVar = newTVar
|
|
|
|
readCTVar = readTVar
|
|
|
|
writeCTVar = writeTVar
|