{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
module Control.Concurrent.STM.TChan.Class 

import qualified Control.Concurrent.STM.TChan as C
import Control.Concurrent.STM

-- | A class capturing Chan operations in STM.
class SplitTChan i o | i -> o, o -> i where
    -- | Write a value to the in chan.
    writeTChan :: i a -> a -> STM ()
    -- | Read the next value from the out chan.
    readTChan :: o a -> STM a
    -- | Get the next value from the @TChan@ without removing it,
    -- retrying if the channel is empty.
    peekTChan :: o a -> STM a
    -- | A version of 'peekTChan' which does not retry. Instead it
    -- returns @Nothing@ if no value is available.
    tryPeekTChan :: o a -> STM (Maybe a)
    -- | A version of 'readTChan' which does not retry. Instead it
    -- returns @Nothing@ if no value is available.
    tryReadTChan :: o a -> STM (Maybe a)
    -- |Returns 'True' if the supplied 'TChan' is empty.
    isEmptyTChan :: o a -> STM Bool

-- | A class for 'SplitTChan' types that can be instantiated without programmer
-- input. /e.g./ the standard haskell @TChan@ is a member of this class, however
-- a bounded chan type that took an @Int@ to define the buffer size would not.
class (SplitTChan i o)=> NewSplitTChan i o where
    newSplitTChan :: STM (i a, o a)

-- instances -- 

-- one-bounded chan:
instance SplitTChan TMVar TMVar where
    readTChan = takeTMVar
    writeTChan = putTMVar
    peekTChan = readTMVar
    tryReadTChan = tryTakeTMVar
    tryPeekTChan = tryReadTMVar
    isEmptyTChan = isEmptyTMVar

instance SplitTChan C.TChan C.TChan where
    writeTChan = C.writeTChan
    readTChan = C.readTChan
    peekTChan = C.peekTChan
    tryReadTChan = C.tryReadTChan
    tryPeekTChan = C.tryPeekTChan
    isEmptyTChan = C.isEmptyTChan

instance NewSplitTChan TMVar TMVar where
    newSplitTChan = do v <- newEmptyTMVar
                       return (v,v)

instance NewSplitTChan TChan TChan where
    newSplitTChan = do v <- C.newTChan
                       return (v,v)