[英]Using pipes/proxies with concurrent MVars
在pipes-3.1.0
包的Control.Proxy
教程中,作者提供了這個函數:
cache :: (Proxy p, Ord key) => key -> p key val key val IO r
cache = runIdentityK (loop M.empty) where
loop _map key = case M.lookup key _map of
Nothing -> do
val <- request key
key2 <- respond val
loop (M.insert key val _map) key2
Just val -> do
lift $ putStrLn "Used cache!"
key2 <- respond val
loop _map key2
因為我想要並發應用程序緩存請求,所以我有以下數據類型
newtype Cache kv = Cache (MVar (M.Map kv))
現在我想要一個帶簽名的新cache
功能
cache :: (Proxy p, Ord k) => Cache k v -> k -> p k v k v IO r
cache (Cache c) k = readMVar c >>= \m -> runIdentityK $ loop m k
where loop m key = case M.lookup key m of
Nothing -> do
val <- request key
respond val >>= loop (M.insert key val m)
Just val -> respond val >>= loop m
但是,由於readMVar
位於IO
monad中,因此無法進行類型readMVar
,而runIdentityK
位於Proxy p => pkvkv IO r
monad中。 當然,我可以將readMVar
提升到這個代理monad中,因為它是IO
上的轉換器,但我找不到合適的組合器。
解決方案是簡單的lift
。 我曾經想過要用它,但顯然沒有用力。 這是我所需cache
的粗略的類型檢查版本
cache = runIdentityK . loop
where loop (Cache c) key = lift (takeMVar c) >>= \m -> case M.lookup key m of
Nothing -> do
val <- request key
lift . putMVar c $ M.insert key val m
respond val >>= loop (Cache c)
Just val -> do
lift $ putMVar c m
respond val >>= loop (Cache c)
它就像添加lift
一樣簡單。 但是,您的實現似乎沒有按照您的意圖執行。 您只在開始時讀取了一次MVar
,然后再也不再使用它,只需在循環中傳遞更新的地圖。 如果不同的線程應該通過MVar
看到更改,您還必須更新它。 一個建議(編譯,但我還沒有測試它是如何工作的):
cache :: (Proxy p, Ord k) => Cache k v -> k -> p k v k v IO r
cache (Cache c) k = runIdentityK loop k
where
loop key = do
m <- lift (readMVar c)
case M.lookup key m of
Nothing -> do
val <- request key
lift $ modifyMVar_ c (return . M.insert key val)
respond val >>= loop
Just val -> respond val >>= loop
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.