0

我正在同时使用 LevelDB 库和 Snap 框架。我有:

main :: IO ()
main = runResourceT $ do
    db <- open "thedb" defaultOptions { createIfMissing = True }
    liftIO $ serveSnaplet defaultConfig $ initWeb db

现在在我的处理程序中,我不确定如何返回MonadResource IO以查询数据库:

handleWords :: Handler App App ()
handleWords = do
    words <- uses thedb $ \db -> $ get db def "words"
    writeBS $ pack $ show words

这给了我一个:No instance for (MonadResource IO) arising from a use of 'get'

有任何想法吗?我觉得我错过了有关如何正确创建单子“堆栈”的内容。谢谢

4

2 回答 2

3

MonadResource/ResourceT是一种获取稀缺资源的方式,以保证在异常情况下资源将被释放。另一种方法是模式bracket Snap 通过函数支持。您可以使用它来创建LevelDB 所需的上下文:bracketSnapResourceT

import qualified Control.Monad.Trans.Resource as Res
bracketSnap Res.createInternalState Res.closeInternalState $ \resState -> do
    let openAction = open "thedb" defaultOptions { createIfMissing = True }
    db <- Res.runInternalState openAction resState

这可以通过对 Snap 和 leveldb 进行一些更改来简化:

  • 不仅提供open假定MonadResource上下文的函数,还可以提供返回Resource的函数。我正在为 2.0 版本持续进行此调整。
  • MonadResourceSnap 可以为其中一个或monad提供支持Resource(不幸的是,这两个独立的概念具有相似的名称)。
于 2014-03-04T13:13:06.893 回答
0

Snap 不需要支持 MonadResource 或 Resource 来执行此操作。你在错误的方向上做单子变压器组合。看看类型会有所帮助。

serveSnaplet :: Config Snap AppConfig -> SnapletInit b b -> IO ()
runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a

因此,您尝试将 IO 放在预期 ResourceT 的位置。你应该反过来处理这个问题。使用 liftIO将您的open "thedb" ...调用放在应用程序的 Initializer 中。Butopen是一个 MonadResource,所以你需要使用 ResourceT 实例将它放入一个 IO。它看起来像这样:

app = makeSnaplet "app" "An snaplet example application." Nothing $ do
    ...
    db <- liftIO $ runResourceT $ open "thedb" defaultOptions

然后将数据库句柄存储在您的 App 状态中,稍后您可以使用HandlerMonadReader 或 MonadState 实例检索它。

于 2014-03-04T17:06:38.513 回答