2014-11-03 46 views
1

我是Haskell的新手(请耐心等待),我正在尝试编写一个使用MongoDB的简单程序。如何使用haskell包中的liftDB函数mongodb

我对包装文档中编写的example没有任何问题。

然后我在文档中找到函数liftDB,我无法弄清楚它是如何工作的。

这是我的尝试:

{-# LANGUAGE OverloadedStrings #-} 

module Main where 

import Database.MongoDB 
import Control.Monad 
import Control.Monad.Reader 

data MongoConfig = MongoConfig{ mContext :: MongoContext } 
saveOnDB doc = do 
    _ <- liftDB $ insert "myDocs" doc 
    return() 

instance HasMongoContext MongoConfig where 
    mongoContext config = mContext config 

main = do 
    saveOnDB [] 
    putStrLn "hello everybody" 

这给我的错误:

No instance for (MonadReader env0 IO) 
    arising from a use of `saveOnDB' 
Possible fix: add an instance declaration for (MonadReader env0 IO) 
In a stmt of a 'do' block: saveOnDB [] 
In the expression: 
    do { saveOnDB []; 
     putStrLn "hello everybody" } 
In an equation for `main': 
    main 
     = do { saveOnDB []; 
      putStrLn "hello everybody" } 

我was't期待它,因为我从来没有提供MongoContext工作,但这里的问题:

  • MongoContext的数据构造函数未被库所公开,所以我想我不应该直接创建它。
  • 在我看来,它期望在MonadReader实例中找到MongoContext

这是函数的类型签名:

saveOnDB :: (HasMongoContext env, MonadIO m, MonadReader env m) 
     => [Field] -> m() 

我怎样才能使它发挥作用?

+1

看起来你需要,使用T他使用从'connect $ host'hostname“'返回的'Pipe'的'access'函数,像'pipe < - connection $ host”hostname“'; '访问管道主“dbName”(saveOnDB [])'。我从来没有使用MongoDB或这个库,所以我不是100%确定的,但类型似乎排队,我很奇怪尝试将某些东西提交到数据库而没有打开它或告诉它的连接要使用哪个数据库。 – bheklilr 2014-11-03 15:59:57

+0

感谢您的回复。是的,你可以这样做。但它真的是我想要了解的函数'liftDB'。所有关于数据库连接的相关信息都存储在MongoContext中(我没有在示例中构造这些信息,因为我不知道构造函数是如何暴露的)。 – 2014-11-05 23:00:02

+0

确实如此,你不应该自己构造一个'MongoContext',构造函数不会被导出,所以库可以确保所有的上下文都是有效的,就像你不能访问'Data.Map的构造函数一样.Map'或'Data.Sequence.Seq',或'base'中的任意数量的其他类型。 'MonadReader'约束来自'liftDB'函数,它的类型为'(MonadReader env m,HasMongoContext env,MonadIO m)=> Action IO a - > m a'。由于'IO'是'MonadIO'的一个实例,所以这个约束被满足,但是'MonadReader env'约束不是。 – bheklilr 2014-11-05 23:05:05

回答

1

基于问题下方的进一步的评论,你似乎更感兴趣的是如何理解liftDB的类型,以及如何使用它,如果你手头MongoConfig,不是如何获得MongoConfig,所以让我们来看看。

类型的liftDB

liftDB :: (HasMongoContext env, MonadIO m, MonadReader env m) 
     => Action IO a -> m a 

这意味着你的saveOnDB函数的类型

saveOnDB :: (HasMongoContext env, MonadIO m, MonadReader env m) 
     => Document -> m() 

这种类型背后的直觉是,liftDB/saveOnDB可以在任何单子m只要运行它为您提供了一种在m(约定MonadIO m)中执行IO并访问MongoContext在任何时候(基本上都是要求在幕后传递;这就是MonadReader MongoContext m的意思)。

为了使事情变得更好用,以防万一您已经拥有一些其他数据,无论如何您都可以传递,您不必明确传递MonadContext;任何事情我们可以从中提取MonadContext会做(这是HasMongoContext env约束的意思)

你所看到的问题,那么,就是你要设定m ~ IO(和,我想,在你真正的应用程序,env ~ MongoConfig),但没有MonadReader MongoConfig IO实例。

由现在的解决办法应该是显而易见的:在ReaderT单子转换“隧道”的基本单子的MonadIO实例(所以如果MonadIO m,然后MonadIO (ReaderT env m),同时提供一些环境env接入所以如果你选择env ~ MongoConfigm ~ ReaderT MongoConfig IO, 。那么你得到的东西,将工作完全按预期

在代码方面,你main应该是这样的:

main = do 
    ctx <- error "This is the part we don't care in this answer" 
    let cfg = MongoConfig ctx 
    flip runReaderT cfg $ saveOnDB [] 
    putStrLn "hello everybody"