-- |This module provides classes and instances for mutable state
-- references.  Various implementation exist in common usage, but
-- no way (until now ;-) to define functions using state references
-- which don't depend on the specific monad or reference type in use.
-- 
-- These modules use several language extensions, including multi-parameter
-- type classes and functional dependencies.
module Data.StateRef
    ( module Data.StateRef
    , module Data.StateRef.Types
    , module Data.StateRef.Instances
    , module Data.Accessor
    ) where

import Data.StateRef.Types
import Data.StateRef.Instances
import Data.Accessor

-- |Read a 'Ref'.  See 'readReference'.
readRef :: Ref m a -> m a
readRef = readReference

-- |Write a 'Ref'.  See 'writeReference'
writeRef :: Ref m a -> a -> m ()
writeRef = writeReference

-- |Modify a 'Ref'.  See 'modifyReference'.
atomicModifyRef :: Ref m a -> (a -> (a,b)) -> m b
atomicModifyRef = atomicModifyReference


-- |Modify a 'Ref'.  See 'modifyReference'.
modifyRef :: Ref m a -> (a -> a) -> m ()
modifyRef = modifyReference


-- |Essentially the same concept as 'Control.Monad.State.gets',
-- 'Control.Monad.State.asks', et al. Typically useful to read a field of
-- a referenced ADT by passing a record selector as the second argument.
readsRef :: (ReadRef sr m a,
             Monad m) =>
            sr -> (a -> b) -> m b
readsRef r f = do
    x <- readReference r
    return (f x)

-- |Construct a counter - a monadic value which, each time it is
-- evaluated, returns the 'succ' of the previous value returned.
newCounter :: (HasRef m, Monad m, Enum a) => a -> m (m a)
newCounter n = do
    c <- newRef n
    return $ do
        x <- readRef c
        writeRef c (succ x)
        return x

-- |Create a \"lapse reader\" (suggestions for better terminology are more 
-- than welcome), a sort of a time-lapse of the variable.  The first 
-- motivating instance for this operation was a clock in a simple simulation
-- application.  Given a 'TVar' 'Double' called \"clock\", a useful
-- value \"dT\" is yielded by the expression: 'mkLapseReader' clock (-)
mkLapseReader
  :: (ReadRef sr m a, HasRef m, Monad m) =>
     sr -> (a -> a -> b) -> m (m b)
mkLapseReader var f = do
    startVal <- readReference var
    prevRef <- newRef startVal
    
    return $ do
        newVal <- readReference var
        prevVal <- readRef prevRef
        
        writeReference prevRef newVal
        
        return (f newVal prevVal)