Re: Caching Actions

I believe the intention is to ensure that two threads do not perform the action at the same time. If you look at the implementation of mfix for IO[1], it's using an MVar. I'm not 100% certain, but I think the `cache` function above could be rewritten to use MVars explicitly and avoid both monadic do and unsafe IO functions.

I'm trying to understand the reasom behind the use of mdo. Can't it be like this:

do a <- m liftIO $ writeIORef key (Just a) return a

Removing the need for a recursive definition?

Yotam

_______________________________________________

I ran into a need for something similar for FRP myself. I agree that one probably has to be careful about duplicate/concurrent evaluation. My solution at the time was an action, which returns an action that is performed only once:

lazyIO :: IO a -> IO (IO a)

lazyIO action = do

box <- newMVar Nothing

return $ modifyMVar box storeResultOnce

where

storeResultOnce m@(Just result) = return (m, result)

storeResultOnce _ = action >>= \r -> return (Just r, r)

{-# RULES "optimize lazyIO" lazyIO = unsafeInterleaveIO . (pure <$>)

#-}

I think you need to change the type of the cache function if you want to avoid unsafe IO functions at all:

cache :: (MonadFix m, MonadIO m) => m a -> m (Cached m a)

unsafePerformIO [1] already prevents duplicate/concurrent evaluation of its argument. And if you're using unsafe IO already, why not simplify it to just using unsafeInterleaveIO? It has the same guarantees about no duplication according to its Haskell source.