从MonadResource

时间:2016-04-12 15:13:27

标签: haskell

我正在玩leveldb绑定。

我想知道是否可以采用像

这样的功能
MonadResource m => a -> m b

并将其转换为

MonadResource m => m (a -> IO b))

1 个答案:

答案 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