{-
 -      ``Data/StateRef/Instances/STM''
 -      (c) 2008 Cook, J. MR  SSD, Inc.
 -}
{-# LANGUAGE
        CPP,
        MultiParamTypeClasses,
        FlexibleInstances,
        IncoherentInstances
  #-}

-- |This module exports no new symbols of its own.  It defines 
--  basic class instances for creating, reading, and writing 'TVar's and
--  (if available) 'TMVar's, and re-exports the types for which it defines 
--  instances as well as the 'atomically' function, which is indispensible
--  when playing with this stuff in ghci.
--
--  Note that this module declares incoherent instances.  The universe should
--  refrain from imploding on itself as long as you don't define
--  \"instance MonadIO STM\".  However, hugs doesn't seem to support 
--  overlapping instances, so I may have to give up on the dream of MonadIO 
--  everywhere, or introduce some major conditional compilation stuff. (or
--  abandon hugs support)

module Data.StateRef.Instances.STM
        ( STM
        , TVar
#ifdef useTMVar
        , TMVar
#endif
        
        , atomically
        ) where

import Data.StateRef.Classes

import Control.Monad.Trans

import Control.Concurrent.STM

-- (STM a) in STM and IO-compatible monads
instance ReadRef (STM a) STM a where
        readRef = id
instance MonadIO m => ReadRef (STM a) m a where
        readRef = liftIO . atomically

-- TVar in STM monad
instance DefaultStateRef (TVar a) STM a
instance NewRef (TVar a) STM a where
        newRef = newTVar
instance ReadRef (TVar a) STM a where
        readRef = readTVar
instance WriteRef (TVar a) STM a where
        writeRef = writeTVar
instance ModifyRef (TVar a) STM a

-- TVar in IO-compatible monads
instance MonadIO m => NewRef (TVar a) m a where
        newRef = liftIO . newTVarIO
instance MonadIO m => ReadRef (TVar a) m a where
        readRef = liftIO . atomically . readRef
instance MonadIO m => WriteRef (TVar a) m a where
        writeRef ref = liftIO . atomically . writeRef ref
instance MonadIO m => ModifyRef (TVar a) m a where
        modifyRef ref = liftIO . atomically . modifyRef ref

#ifdef useTMVar
-- TMVar in STM monad
instance NewRef (TMVar a) STM (Maybe a) where
	newRef Nothing = newEmptyTMVar
	newRef (Just x) = newTMVar x
instance ReadRef (TMVar a) STM (Maybe a) where
	readRef tmv = fmap Just (readTMVar tmv) `orElse` return Nothing

-- TMVar in IO-compatible monad
instance MonadIO m => NewRef (TMVar a) m (Maybe a) where
	newRef Nothing = liftIO newEmptyTMVarIO
	newRef (Just x) = liftIO (newTMVarIO x)
instance MonadIO m => ReadRef (TMVar a) m (Maybe a) where
	readRef = liftIO . atomically . readRef
#endif