[Haskell-cafe] Re: Caching the Result of a Transaction?

Jake Mcarthur jake.mcarthur at gmail.com
Tue Apr 29 23:27:09 EDT 2008


Alright, I have tested it now. I still feel funny about most of the  
names I chose for the types and functions, and it's still very ugly,  
but the code appears to work correctly. In this version I have also  
added "retry" and "orElse" functions so that it can feel more like the  
STM monad. I think the biggest downside to this monad is the potential  
confusion about whether to use "could" or "must," but I have a feeling  
that better naming choices would reduce the ambiguity.

Thoughts?

> module CachedSTM where
>
> import Control.Applicative
> import Control.Concurrent.STM as S
> import Control.Monad
>
> data CachedSTM a = CSTM {
>       getMust :: STM (),
>       getCould :: STM a
>     }
>
> instance Functor CachedSTM where
>     f `fmap` (CSTM m s) = CSTM m $ f <$> s
>
> joinCSTM :: CachedSTM (CachedSTM a) -> CachedSTM a
> joinCSTM cstm = CSTM m s
>     where m = do cstm' <- getCould cstm
>                  getMust cstm' `S.orElse` return ()
>                  getMust cstm `S.orElse` return ()
>           s = getCould =<< getCould cstm
>
> instance Applicative CachedSTM where
>     pure = return
>     (<*>) = ap
>
> instance Monad CachedSTM where
>     return = CSTM (return ()) . return
>     x >>= f = joinCSTM $ f <$> x
>
> maybeAtomicallyC :: CachedSTM a -> IO (Maybe a)
> maybeAtomicallyC cstm = atomically $ do
>                           getMust cstm
>                           liftM Just (getCould cstm) `S.orElse`  
> return Nothing
>
> could :: STM a -> CachedSTM a
> could stm = CSTM (return ()) stm
>
> must :: STM () -> CachedSTM ()
> must stm = CSTM (stm `S.orElse` return ()) $ return ()
>
> retry :: CachedSTM a
> retry = could S.retry
>
> orElse :: CachedSTM a -> CachedSTM a -> CachedSTM a
> orElse a b = do must $ getMust a
>                 temp <- could newEmptyTMVar
>                 must $ (getCould a >>= putTMVar temp) `S.orElse`  
> getMust b
>                 could $ takeTMVar temp `S.orElse` getCould b

I don't think the IVar code has changed (no version control for this),  
but here it is again for quick reference:

> module IVal where
>
> import CachedSTM
> import Control.Applicative
> import Control.Concurrent.STM
> import Control.Monad
> import System.IO.Unsafe
>
> newtype IVal a = IVal (TVar (Either (CachedSTM a) a))
>
> newIVal :: CachedSTM a -> CachedSTM (IVal a)
> newIVal = fmap IVal . could . newTVar . Left
>
> newIValIO :: CachedSTM a -> IO (IVal a)
> newIValIO = fmap IVal . newTVarIO . Left
>
> cached :: CachedSTM a -> IVal a
> cached = unsafePerformIO . newIValIO
>
> force :: IVal a -> CachedSTM a
> force (IVal tv) = could (readTVar tv) >>= either compute return
>     where compute wait = do x <- wait
>                             must . writeTVar tv $ Right x
>                             return x
>
> instance Functor IVal where
>     f `fmap` x = cached $ f <$> force x
>
> instance Applicative IVal where
>     pure = return
>     (<*>) = ap
>
> instance Monad IVal where
>     return = cached . return
>     x >>= f = cached (force x >>= force . f)

- Jake
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/haskell-cafe/attachments/20080429/cad30905/attachment.htm


More information about the Haskell-Cafe mailing list