我目前正在和Bryan O'Sullivan的Mouseover to select item in listbox in WPF图书馆一起玩,并且有关于扩展withResource
功能的问题。
我想将withResource
函数的签名从(MonadBaseControl IO m) => Pool a -> (a -> m b) -> m b
更改为(MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b
我想要实现的是,操作应该返回(Bool, b)
元组,其中布尔值指示借用的资源是否应该
被放回游泳池或被摧毁。
现在我的当前实现如下:
withResource :: forall m a b. (MonadBaseControl IO m) => Pool a -> (a -> m (Bool, b)) -> m b
{-# SPECIALIZE withResource :: Pool a -> (a -> IO (Bool,b)) -> IO b #-}
withResource pool act = fmap snd result
where
result :: m (Bool, b)
result = control $ \runInIO -> mask $ \restore -> do
resource <- takeResource pool
ret <- restore (runInIO (act resource)) `onException`
destroyResource pool resource
void . runInIO $ do
(keep, _) <- restoreM ret :: m (Bool, b)
if keep
then liftBaseWith . const $ putResource pool resource
else liftBaseWith . const $ destroyResource pool resource
return ret
我有一种感觉,这不是它看起来的样子......
也许我没有使用MonadBaseControl
API权限。
你们怎么看待这个?我怎样才能把它改进为更惯用的?
答案 0 :(得分:2)
我觉得这种方法存在根本问题。对于StM M a
与a
相等/同构的monad,它将起作用。但对于其他monad将会出现问题。我们来考虑MaybeT IO
。类型a -> MaybeT IO (Bool, b)
的操作可能会失败,因此不会生成Bool
值。
void . runInIO $ do
(keep, _) <- restoreM ret :: m (Bool, b)
...
不会被执行,控制流将停在restoreM
。对于ListT IO
而言,它会更糟,因为putResource
和destroyResource
会被执行多次。考虑这个示例程序,它是函数的简化版本:
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, RankNTypes, TupleSections #-}
import Control.Monad
import Control.Monad.Trans.Control
import Control.Monad.Trans.List
foo :: forall m b . (MonadBaseControl IO m) => m (Bool, b) -> m b
foo act = fmap snd result
where
result :: m (Bool, b)
result = control $ \runInIO -> do
ret <- runInIO act
void . runInIO $ do
(keep, _) <- restoreM ret :: m (Bool, b)
if keep
then liftBaseWith . const $ putStrLn "return"
else liftBaseWith . const $ putStrLn "destroy"
return ret
main :: IO ()
main = void . runListT $ foo f
where
f = msum $ map (return . (, ())) [ False, True, False, True ]
它打印
destroy
return
destroy
return
对于一个空列表,没有任何内容被打印出来,这意味着你的函数不会调用任何清理工作。
我不得不说我不确定如何以更好的方式实现目标。我试着向签名方向探索
withResource :: forall m a b. (MonadBaseControl IO m)
=> Pool a -> (a -> IO () -> m b) -> m b
其中IO ()
参数是一个函数,在执行时,会使当前资源无效并将其标记为要销毁。 (或者,为了更方便,请将IO ()
替换为提升m ()
)。然后在内部,以IO
为基础,我只需创建一个帮助MVar
,然后通过调用重置
函数,最后,根据值,返回或销毁资源。