haskell - 在 Haskell 中内存 IO 计算

标签 haskell concurrency memoization

我有一个功能

f :: MonadIO m => a -> m b

它接受一些输入并返回将产生输出的 IO 计算。我想“记住”f所以我只对每个输入执行一次这些计算。例如,如果
f :: String -> IO String
f s = putStrLn ("hello " ++ s) >> return s

那么我想要一个函数 memoize以至于
do
  mf <- memoize f
  s <- mf "world"
  t <- mf "world"
  return (s,t)

版画 "hello world"恰好一次并返回 ("world", "world") .我正在编写的程序是多线程的,因此即使不同的线程都在调用 mf,该属性也应该保持不变。 .

以下是我到目前为止提出的(可怕的)解决方案。我的问题是是否以及如何改进。
memoize :: (MonadIO m, Ord a) => (a -> m b) -> m (a -> m b)
memoize f = do
  cache <- liftIO $ newTVarIO Map.empty
  return $ \a -> do
              v <- liftIO $ atomically $ lookupInsert cache a
              b <- maybe (f a) return =<< liftIO (atomically $ takeTMVar v)
              liftIO $ atomically $ putTMVar v $ Just b
              return b
    where
      lookupInsert :: Ord a => TVar (Map a (TMVar (Maybe b))) -> a -> STM (TMVar (Maybe b))
      lookupInsert cache a = do
                         mv <- Map.lookup a <$> readTVar cache
                         case mv of
                           Just v -> return v
                           Nothing -> do
                                   v <- newTMVar Nothing
                                   modifyTVar cache (Map.insert a v)
                                   return v

这里发生了一些事情:

1) cache有类型 TVar (Map a (TMVar (Maybe b))) .它将输入映射到 TMVar包含计算值或 Nothing (这表示尚未计算值)。函数lookupInsert检查 cache ,并插入一个新的 TMVar初始化为 Nothing如果已经不存在。

2)返回的action首先获得v :: TMVar (Maybe b)关联到 a ,然后获取它,或者执行计算 f a获取结果或返回存储在 Maybe 中的值如果可用。此 takeput模式使得两个不同的线程不会同时运行计算 f a在看到它尚未运行后。

最佳答案

我以为你想要的是不可能的,但事实证明它是。

https://stackoverflow.com/a/9458721/1798971

我仍然无法弄清楚为什么会这样!

关于haskell - 在 Haskell 中内存 IO 计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15911084/

相关文章:

haskell - 如何在 IHP 表单中传递 List 参数?

java - volatile 实际上是如何工作的?

java - 波动发生在澄清/误解之前?

javascript - 将参数内存为键

haskell - parsec 如何递归解析简单表达式?

haskell - 如何从 STDIN 读取 1 个字节?

kotlin - GlobalScope 与 LifecycleOwner :CoroutineScope

python - 如何为 functools.lru_cache 创建别名以进行内存?

python - Pandas Series 参数函数内存

haskell - 发布 Haskell 库时如何确定合理的包依赖范围?