{-# LANGUAGE CPP, FunctionalDependencies, MultiParamTypeClasses #-}
{- |
   Module     : Data.Ref.Universal
   Copyright  : Copyright (C) 2006 Bulat Ziganshin
   License    : BSD3

   Maintainer : Bulat Ziganshin <Bulat.Ziganshin@gmail.com>
   Stability  : experimental
   Portability: Hugs/GHC

Monad-independent interfaces for boxed and unboxed references

-}

module Data.Ref.Universal (
           -- * Monad-independent interface for boxed references
           Ref(..),
           modifyRef,
           modifyRefM,
           -- * Monad-independent interface for unboxed references
           URef(..),
           modifyURef,
           modifyURefM,
       )
where

import Control.Monad.ST (ST)
import Data.IORef
import Data.STRef

import Data.Unboxed
import Data.Ref.Unboxed

-- -----------------------------------------------------------------------------
-- | This class allows to create new boxed reference in monad-independent way
--     (suitable for writing code that will work in IO, ST and other monads)

class (Monad m) => Ref m r | m->r, r->m where
    -- | Create a new 'Ref' with given initial value
    newRef :: a -> m (r a)
    -- | Read the value of an 'Ref'
    readRef   :: r a -> m a
    -- | Write new value into an 'Ref'
    writeRef  :: r a -> a -> m ()

instance Ref IO IORef where
    newRef = newIORef
    readRef = readIORef
    writeRef = writeIORef
instance Ref (ST s) (STRef s) where
    newRef = newSTRef
    readRef = readSTRef
    writeRef = writeSTRef

-- | Modify the contents of an 'Ref' by applying pure function to it
{-# INLINE modifyRef #-}
modifyRef :: (Ref m r) => r a -> (a -> a) -> m ()
modifyRef  ref f  =  readRef ref >>= writeRef ref . f

-- | Modify the contents of an 'Ref' by applying monadic computation to it
{-# INLINE modifyRefM #-}
modifyRefM :: (Ref m r) => r a -> (a -> m a) -> m ()
modifyRefM ref f  =  readRef ref >>= f >>= writeRef ref

-- -----------------------------------------------------------------------------
-- | This class allows to create new unboxed reference in monad-independent way
--     (suitable for writing code that will work in IO, ST and other monads)

class (Monad m) => URef m r | m->r, r->m where
    -- | Create a new 'URef' with given initial value
    newURef    :: (Unboxed a) => a -> m (r a)
    -- | Read the value of an 'URef'
    readURef   :: (Unboxed a) => r a -> m a
    -- | Write new value into an 'URef'
    writeURef  :: (Unboxed a) => r a -> a -> m ()

instance URef IO IOURef where
    newURef = newIOURef
    readURef = readIOURef
    writeURef = writeIOURef
instance URef (ST s) (STURef s) where
    newURef = newSTURef
    readURef = readSTURef
    writeURef = writeSTURef

-- | Modify the contents of an 'URef' by applying pure function to it
{-# INLINE modifyURef #-}
modifyURef :: (Unboxed a, URef m r) => r a -> (a -> a) -> m ()
modifyURef  ref f =  readURef ref >>= writeURef ref . f

-- | Modify the contents of an 'URef' by applying monadic computation to it
{-# INLINE modifyURefM #-}
modifyURefM :: (Unboxed a, URef m r) => r a -> (a -> m a) -> m ()
modifyURefM ref f  =  readURef ref >>= f >>= writeURef ref