IO OutputType 这可能是一些简单的事情,例如 DNS 查找,或者针对时不变数据的某些 Web 服务-6ren">
gpt4 book ai didi

haskell - "referentially transparent"IO调用的可重入缓存

转载 作者:行者123 更新时间:2023-12-02 14:09:47 25 4
gpt4 key购买 nike

假设我们有一个 IO 操作,例如

lookupStuff :: InputType -> IO OutputType

这可能是一些简单的事情,例如 DNS 查找,或者针对时不变数据的某些 Web 服务调用。

我们假设:

  1. 该操作永远不会引发任何异常和/或永远不会发散

  2. 如果没有 IO monad,该函数将是纯函数,即对于相同的输入参数,结果始终相同

  3. 该操作是可重入的,即可以同时从多个线程安全地调用它。

  4. lookupStuff 操作的(时间)成本相当高。

我面临的问题是如何正确(并且不使用任何unsafe*IO*作弊)实现可从多个线程调用的可重入缓存,并合并多个查询将相同的输入参数放入单个请求中。

我想我正在寻找类似于 GHC 纯计算黑洞概念的东西,但在 IO“计算”上下文中。

针对上述问题的惯用 Haskell/GHC 解决方案是什么?

最佳答案

是的,基本上重新实现了逻辑。虽然这看起来与 GHC 已经在做的事情相似,但这是 GHC 的选择。 Haskell 可以在工作方式非常不同的虚拟机上实现,因此从这个意义上说,它还没有为您完成。

但是,是的,只需使用 MVar (Map InputType OutputType) 甚至 IORef (Map InputType OutputType) (确保使用 atomicModifyIORef 进行修改) code>),然后将缓存存储在那里。如果这个命令式解决方案看起来是错误的,那就是“如果没有 IO,这个函数将是纯函数”约束。如果它只是一个任意的 IO 操作,那么您必须保持状态才能知道执行或不执行什么的想法似乎是非常自然的。问题是 Haskell 没有“纯 IO”类型(如果它依赖于数据库,那么它只是在某些条件下表现为纯 IO,这与遗传纯 IO 不同)。

import qualified Data.Map as Map
import Control.Concurrent.MVar

-- takes an IO function and returns a cached version
cache :: (Ord a) => (a -> IO b) -> IO (a -> IO b)
cache f = do
r <- newMVar Map.empty
return $ \x -> do
cacheMap <- takeMVar r
case Map.lookup x cacheMap of
Just y -> do
putMVar r cacheMap
return y
Nothing -> do
y <- f x
putMVar (Map.insert x y cacheMap)
return y

是的,里面很丑。但从外面看,你看!它就像纯内存函数的类型,只不过它上面沾满了IO

关于haskell - "referentially transparent"IO调用的可重入缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5484847/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com