{-# LANGUAGE TypeFamilies, RankNTypes, FlexibleInstances #-}

-- |
-- Module     : Simulation.Aivika.Trans.ProtoRef
-- Copyright  : Copyright (c) 2009-2014, David Sorokin <david.sorokin@gmail.com>
-- License    : BSD3
-- Maintainer : David Sorokin <david.sorokin@gmail.com>
-- Stability  : experimental
-- Tested with: GHC 7.8.3
--
-- It defines a prototype of mutable references.
--
module Simulation.Aivika.Trans.ProtoRef
       (ProtoRefMonad(..),
        ProtoRef(..)) where

import Data.IORef

import Simulation.Aivika.Trans.Session

-- | A monad within which computation we can create and work with
-- the prototype of mutable reference.
class (Functor m, Monad m) => ProtoRefMonad m where
  
  -- | A prototype of mutable reference.
  data ProtoRef m :: * -> *

  -- | Create a new ptototype of mutable reference by the specified session and initial value.
  newProtoRef :: Session m -> a -> m (ProtoRef m a)

  -- | Read the contents of the prototype of mutable reference.
  readProtoRef :: ProtoRef m a -> m a

  -- | Write a new value in the prototype of mutable reference.
  writeProtoRef :: ProtoRef m a -> a -> m ()

  -- | Modify a value stored in the prototype of mutable reference.
  modifyProtoRef :: ProtoRef m a -> (a -> a) -> m ()

  -- | A strict version of 'modifyProtoRef'.
  modifyProtoRef' :: ProtoRef m a -> (a -> a) -> m ()

instance ProtoRefMonad IO where

  newtype ProtoRef IO a = ProtoRef (IORef a)

  {-# SPECIALIZE INLINE newProtoRef :: Session IO -> a -> IO (ProtoRef IO a) #-}
  newProtoRef session = fmap ProtoRef . newIORef

  {-# SPECIALIZE INLINE readProtoRef :: ProtoRef IO a -> IO a #-}
  readProtoRef (ProtoRef x) = readIORef x

  {-# SPECIALIZE INLINE writeProtoRef :: ProtoRef IO a -> a -> IO () #-}
  writeProtoRef (ProtoRef x) = writeIORef x

  {-# SPECIALIZE INLINE modifyProtoRef :: ProtoRef IO a -> (a -> a) -> IO () #-}
  modifyProtoRef (ProtoRef x) = modifyIORef x

  {-# SPECIALIZE INLINE modifyProtoRef' :: ProtoRef IO a -> (a -> a) -> IO () #-}
  modifyProtoRef' (ProtoRef x) = modifyIORef' x