[Haskell-cafe] Re: Caching the Result of a Transaction?
ChrisK
haskell at list.mightyreason.com
Mon Apr 28 10:40:01 EDT 2008
The garbage collector never gets to collect either the action used to populate
the cached value, or the private TMVar used to hold the cached value.
A better type for TIVal is given below. It is a newtype of a TVal. The
contents are either a delayed computation or the previously forced value.
Thew newTIVal(IO) functions immediately specify the delayed action.
The newEmptyTIVal(IO) functions create a private TMVar that allows the delayed
action to be specified once later. Note the use of tryPutTMVar to return a Bool
instead of failing, in the event that the user tries to store more that one action.
When force is called, the previous action (and any private TMVar) are forgotten.
The garbage collector might then be free to collect them.
--
Chris
> -- By Chris Kuklewicz (April 2008), public domain
> module TIVal(TIVal,newTIVal,newTIValIO,force,cached) where
>
> import Control.Applicative(Applicative(..))
> import Control.Concurrent.STM(STM,TVar,newTVar,newTVarIO,readTVar,writeTVar
> ,TMVar,newEmptyTMVar,newEmptyTMVarIO,tryPutTMVar,readTMVar)
> import Control.Monad(Monad(..),join,liftM2)
> import System.IO.Unsafe(unsafePerformIO)
>
> newtype TIVal a = TIVal (TVar (Either (STM a) a))
>
> -- the non-empty versions take a computation to delay
>
> newTIVal :: STM a -> STM (TIVal a)
> newTIVal = fmap TIVal . newTVar . Left
>
> newTIValIO :: STM a -> IO (TIVal a)
> newTIValIO = fmap TIVal . newTVarIO . Left
>
> -- The empty versions stage things with a TMVar, note the use of join
> -- Plain values 'a' can be stored with (return a)
>
> newEmptyTIVal :: STM ( TIVal a, STM a -> STM Bool)
> newEmptyTIVal = do
> private <- newEmptyTMVar
> tv <- newTVar (Left (join $ readTMVar private))
> return (TIVal tv, tryPutTMVar private)
>
> newEmptyTIValIO :: IO ( TIVal a, STM a -> STM Bool )
> newEmptyTIValIO = do
> private <- newEmptyTMVarIO
> tv <- newTVarIO (Left (join $ readTMVar private))
> return (TIVal tv, tryPutTMVar private)
>
> -- force will clearly let go of the computation (and any private TMVar)
>
> force :: TIVal a -> STM a
> force (TIVal tv) = do
> v <- readTVar tv
> case v of
> Right a -> return a
> Left wait -> do a <- wait
> writeTVar tv (Right a)
> return a
>
> -- Conal's "cached" function. This is actually safe.
>
> cached :: STM a -> TIVal a
> cached = unsafePerformIO . newTIValIO
>
> -- The instances
>
> instance Functor TIVal where
> f `fmap` tiv = cached (f `fmap` force tiv)
>
> instance Applicative TIVal where
> pure x = cached (pure x)
> ivf <*> ivx = cached (force ivf <*> force ivx)
>
> instance Monad TIVal where
> return x = cached (return x)
> tiv >>= k = cached (force tiv >>= force . k)
>
> instance Applicative STM where
> pure x = return x
> ivf <*> ivx = liftM2 ($) ivf ivx
More information about the Haskell-Cafe
mailing list