我正在玩leveldb绑定。
我想知道是否可以采用像
这样的功能MonadResource m => a -> m b
并将其转换为
MonadResource m => m (a -> IO b))
答案 0 :(得分:3)
绝对可以做到,但这很危险。让我们首先通过提取ResourceT
的内部状态来说明如何:
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource
import Control.Monad.Trans.Resource.Internal
data Foo = Foo Int
deriving Show
getFoo :: MonadResource m => Int -> m Foo
getFoo i = fmap snd $ allocate
(do
putStrLn $ "allocating Foo with " ++ show i
return $ Foo i)
(\(Foo x) -> putStrLn $ "Freeing Foo " ++ show x)
stripLayer :: MonadResource m => (a -> ResourceT IO b) -> m (a -> IO b)
stripLayer f = do
is <- liftResourceT getInternalState
return $ \a -> runInternalState (f a) is
main :: IO ()
main = do
getFoo' <- runResourceT $ stripLayer $ getFoo
getFoo' 42 >>= print
不幸的是,这个输出并不是我们所希望的:
allocating Foo with 42
Foo 42
注意&#34; Freeing&#34;从来没有调用过。这是因为,当我们使用getFoo'
时,runResourceT
调用已经退出,这就是我们保证释放所有资源的方式。你可以安全地逃脱这个伎俩,如果你有纪律,并确保所有内容都在runResourceT
电话中,但类型系统不会帮助你。要看看它会是什么样子:
main :: IO ()
main = runResourceT $ do
getFoo' <- stripLayer $ getFoo
liftIO $ getFoo' 42 >>= print