问题
I made the following function which is specific for the IO monad:
memoIO :: MonadIO m => m a -> IO (m a)
memoIO action = do
ref <- newMVar Nothing
return $ do
x <- maybe action return =<< liftIO (takeMVar ref)
liftIO . putMVar ref $ Just x
return x
Example usage:
main :: IO ()
main = do
p <- memoIO $ putStrLn "hello"
p
p
Prints "hello
" once.
I would like (a pet peeve) to make it work for as many cases as possible (not just in IO).
I found stateref on hackage and with it my code looks like this:
{-# LANGUAGE FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, Rank2Types, UndecidableInstances #-}
import Data.MRef
class (NewMRef r m a, DefaultMRef r m a, PutMRef r m a, TakeMRef r m a) => MRef r m a
instance (NewMRef r m a, DefaultMRef r m a, PutMRef r m a, TakeMRef r m a) => MRef r m a
memo :: (MRef r m (Maybe a), Monad s) => (forall x. m x -> s x) -> s a -> m (s a)
memo liftFunc action = do
ref <- newDefaultMRef Nothing
return $ do
x <- maybe action return =<< liftFunc (takeDefaultMRef ref)
liftFunc . putDefaultMRef ref $ Just x
return x
Is there an alternative for stateref or a better way to use it than I did?
回答1:
I've rewritten a cheesy little MonadRef
class on a few separate occasions for my own personal use and someone probably has one on Hackage, but I can't find one that is unencumbered with other baggage.
class Monad m => MonadRef m where
type Ref m :: * -> *
newRef :: a -> Ref m a
writeRef :: Ref m a -> -> m ()
readRef :: Ref m a -> m a
instance MonadRef IO where
type Ref IO = IORef
newRef = newIORef
writeRef = writeIORef
readRef = writeIORef
instance MonadRef STM where
type Ref STM = TVar
...
instance MonadRef (ST s) where
type Ref (ST s) = STRef s
...
Then it is easy to abstract away your memoization routine (though you probably want to replace IORef
in this context with an MVar
.)
[Edit: clarified verbage]
来源:https://stackoverflow.com/questions/1091629/haskell-generic-ioref-mvar