{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TypeFamilies  #-}
{-#  LANGUAGE DataKinds #-}

module Data.Ref(Ref(..), module Data.Ref.CAS )where

{-  -}

import qualified  Data.IORef as IR 
import qualified  Data.STRef.Strict as SR 
import Control.Monad.ST.Safe (ST)
import Data.Ref.CAS 
import Control.Monad 

{- I can't  choose what sort of ref you give me,
     nor can i change what the current monad is
     so why bother using different operations for either?  

    also i'm only providing the stict versions of the data types as applicable
    , though other instances that are sensible are welcome.

    also worth thinking about how to support analogous generic api for concurrent operations
      -}




class  Ref ref where
    -- | The monad type associated with a given reference type.
    -- eg, IO for IORefs, ST for STRefs.
    --  Need to consider if Functional Dependencies will yield better type inference or not.
    type RefM ref  :: * -> *

    newRef :: a -> RefM ref (ref a)
    newRef' :: a -> RefM ref (ref a)
    readRef :: ref a -> RefM ref a
    readRef' :: ref a -> RefM ref a
    writeRef :: ref a -> a -> RefM ref ()
    writeRef' :: ref a -> a -> RefM ref ()
    modifyRef :: ref a -> (a-> a) -> RefM ref ()
    modifyRef' :: ref a -> (a-> a) -> RefM ref ()

instance  Ref IR.IORef where
    type RefM IR.IORef = IO 
    newRef  a = IR.newIORef a 
    {-# INLINE newRef #-}
    newRef' !a = IR.newIORef $! a 
    {-# INLINE newRef' #-}
    readRef  a  = IR.readIORef a
    {-#  INLINE readRef #-}
    readRef' !r = IR.readIORef  $! r 
    {-# INLINE readRef' #-}
    writeRef r a = IR.writeIORef r a 
    {-# INLINE writeRef #-} 
    writeRef' !r !a =IR.writeIORef  r  $! a 
    {-# INLINE writeRef' #-}
    modifyRef r f  = IR.modifyIORef r f 
    {-# INLINE modifyRef #-}
    modifyRef' r f = IR.modifyIORef' r f 
    {-# INLINE modifyRef' #-}


instance  Ref (SR.STRef s) where
    type RefM (SR.STRef s) = ST s 
    newRef  a = SR.newSTRef a 
    {-# INLINE newRef #-}
    newRef' !a = SR.newSTRef $! a 
    {-# INLINE newRef' #-}
    readRef  a  = SR.readSTRef a
    {-#  INLINE readRef #-}
    readRef' !r = SR.readSTRef  $! r 
    {-# INLINE readRef' #-}
    writeRef r a = SR.writeSTRef r a 
    {-# INLINE writeRef #-} 
    writeRef' !r !a =SR.writeSTRef  r  $! a 
    {-# INLINE writeRef' #-}
    modifyRef r f  = SR.modifySTRef r f 
    {-# INLINE modifyRef #-}
    modifyRef' r f = SR.modifySTRef' r f 
    {-# INLINE modifyRef' #-}