On Apr 28, 2008, at 10:01 PM, Ryan Ingram wrote:
The problem I have with all of these STM-based solutions to this
problem is that they don't actually cache until the action fully
executes successfully.
I just hacked together a new monad that I think might solve this, at
least with a little extra work. I haven't tested it yet though because
I have to do some studying now. I just want to go ahead and put it up
for review and see if you guys think this is a good approach.
To use it you use the "could" and "must" functions to specify which
STM actions may be rolled back and which ones must be permanent. When
you apply maybeAtomicallyC to a CachedSTM action, all the "must"
actions are performed individually, where any that fail do not affect
any of the others. Once the "must" actions are done, the "could"
actions are performed, returning Just the result. If that fails then
the whole thing simply returns Nothing, but the "must" actions are
still committed.
At least, I _hope_ the above is what it actually does!
module CachedSTM where
import Control.Applicative
import Control.Concurrent.STM
import Control.Monad
data CachedSTM a = CSTM {
getMust :: STM (),
getShould :: 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' <- getShould cstm
getMust cstm' `orElse` return ()
getMust cstm `orElse` return ()
s = getShould =<< getShould 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 (getShould cstm) `orElse`
return Nothing
could :: STM a -> CachedSTM a
could stm = CSTM (return ()) stm
must :: STM () -> CachedSTM ()
must stm = CSTM stm $ return ()
Now the IVal stuff might look something like:
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
_______________________________________________
Haskell-Cafe mailing list
Haskell-Cafe@haskell.org
http://www.haskell.org/mailman/listinfo/haskell-cafe