AcidState 函数中的不明确类型变量



我在Haskell网络项目中遇到过错误Ambiguous type variable的情况。

相关代码是

--- Other import statements
import qualified Model as Model
---------- HTTP Handlers
needItem db user itemName = do
  Model.changeItemStatus db user itemName Model.Need
  listItems db user
gotItem db user itemName = do
  Model.changeItemStatus db user itemName Model.Got
  listItems db user
newItem db user itemName comment count = do
  Model.insertItem db user itemName comment count
  listItems db user
listItems db user = do
  lst <- Model.listItems db user
  resOk lst
--- rest of the program

这些错误专门抱怨insertItemchangeItemStatus,两者都是acid-state查询函数

insertItem db name itemName comment count = withAccount db name newItem
  where item = Item { itemName = itemName, itemComment = comment, itemCount = count, itemStatus = Need } 
        newItem account = update' db $ NewItem account item
-- other stuff
changeItemStatus db name itemName status = withAccount db name cItem
  where cItem account = withItem account itemName
                        (i -> update' db $ ChangeItem account $ i { itemStatus = status})

withAccountwithItem 是帮助程序函数,可帮助我处理数据库中Maybe返回值。它们被定义为

withAccount :: MonadIO m => AcidState GoGetDB -> String -> (Account -> a) -> m (Maybe a)
withAccount db name fn = do
  maybeAccount <- query' db $ AccountByName name
  return $ do acct <- maybeAccount
              return $ fn acct
withItem :: Account -> String -> (Item -> a) -> Maybe a
withItem account itemName fn = do
  item <- getOne $ (accountItems account) @= itemName
  return $ fn item

好吧,现在。

我已经多次阅读了Happstack速成课程中的AcidState文档,但它没有多大帮助;他们直接在响应生成函数中使用查询

  • 我已经尝试并得到了相同的Ambiguous type variable错误,除了指向query'调用iteslf,
  • 真的不想这样做,因为这会迫使我混合我的模型/控制器代码
  • 对我的特定情况没有帮助,因为它没有向我显示调用query'update'的函数的具体返回类型是什么(它们的函数都是AcidState DBName -> ServerPart Response的,因为它们直接生成响应(。

我试图通过在表达式的各个部分上使用:t来将insertItemchangeItemStatus的类型签名放在一起,但每次尝试都给了我我认为是No instance for (MonadIO m1)更糟糕的错误。

我还不是一个特别熟练的哈斯凯勒,所以我觉得我唯一可以尝试的另一件事就是在这个地方撒上随机return $,但这听起来不像是真正解决问题的好机会,或者教我任何东西。

我尝试实现的一般概念是:"对数据库进行此更改,然后为当前用户返回(可能更改的(相关元素列表"。

关于我接下来应该尝试什么,或者我具体出错的地方有什么提示吗?我是否以完全错误的方式思考这个问题?关于该主题,我还有其他文件可以参考吗?

附言。我已经包含了我认为是上面的所有相关代码,但如果你想查看完整的源代码,它在这里和这里。

编辑:完整类型错误

/home/inaimathi/projects/goget/goget.hs:31:3:
    Ambiguous type variable `m2' in the constraint:
      (Control.Monad.IO.Class.MonadIO m2)
        arising from a use of `Model.changeItemStatus'
    Probable fix: add a type signature that fixes these type variable(s)
    In a stmt of a 'do' block:
      Model.changeItemStatus db user itemName Model.Need
    In the expression:
      do { Model.changeItemStatus db user itemName Model.Need;
           listItems db user }
    In an equation for `needItem':
        needItem db user itemName
          = do { Model.changeItemStatus db user itemName Model.Need;
                 listItems db user }
/home/inaimathi/projects/goget/goget.hs:35:3:
    Ambiguous type variable `m1' in the constraint:
      (Control.Monad.IO.Class.MonadIO m1)
        arising from a use of `Model.changeItemStatus'
    Probable fix: add a type signature that fixes these type variable(s)
    In a stmt of a 'do' block:
      Model.changeItemStatus db user itemName Model.Got
    In the expression:
      do { Model.changeItemStatus db user itemName Model.Got;
           listItems db user }
    In an equation for `gotItem':
        gotItem db user itemName
          = do { Model.changeItemStatus db user itemName Model.Got;
                 listItems db user }
/home/inaimathi/projects/goget/goget.hs:39:3:
    Ambiguous type variable `m0' in the constraint:
      (Control.Monad.IO.Class.MonadIO m0)
        arising from a use of `Model.insertItem'
    Probable fix: add a type signature that fixes these type variable(s)
    In a stmt of a 'do' block:
      Model.insertItem db user itemName comment count
    In the expression:
      do { Model.insertItem db user itemName comment count;
           listItems db user }
    In an equation for `newItem':
        newItem db user itemName comment count
          = do { Model.insertItem db user itemName comment count;
                 listItems db user }

为函数编写类型签名以捕获它们应该执行的操作。 在编写实现之前编写它们,然后您可以使用类型签名,以及从编译器获得的更好的错误消息(如果它知道你想要什么类型(,以检查实现是否执行[或至少有机会执行]您想要的操作。

让我们看看有问题的孩子,以及他们实际上做了什么:

从我们使用acid-state

update' :: (UpdateEvent event, MonadIO m) => AcidState (EventState event) -> event -> m (EventResult event)

insertItem db name itemName comment count = withAccount db name newItem
  where item = Item { itemName = itemName, itemComment = comment, itemCount = count, itemStatus = Need } 
        newItem account = update' db $ NewItem account item

现在让我们看看会产生什么样的类型。从使用

withAccount :: MonadIO m => AcidState GoGetDB -> String -> (Account -> a) -> m (Maybe a)

我们看到结果具有类型

m (Maybe a)

对于某些MonadIO m,其中anewItem的结果类型。现在

newItem account = update' db something

所以

newItem :: MonadIO mio => Account -> mio (EventResult type_of_something)

因此insertItem的结果类型将是

m (Maybe (mio (EventResult type_of_something)))

和在

newItem db user itemName comment count = do
  Model.insertItem db user itemName comment count
  listItems db user

编译器不知道应该在第二行中使用哪个MonadIO mio。因此,类型变量mio是不明确的。

请注意,在那里指定类型变量仍然不会做你想要的。我希望您实际上想要执行update',而不仅仅是计算在执行时更新数据库的操作。

对于insertItem,如果它确实应该更新数据库,withAccount现状是没有用的。你也许可以使用类似的东西

withAccountM :: MonadIO m => AcidState GoGetDB -> String -> (Account -> m a) -> m (Maybe a)
withAccountM db name fn = do
  maybeAccount <- query' db $ AccountByName name
  case maybeAccount of
    Nothing -> return Nothing
    Just acct -> do
         result <- fn acct
         return $ Just result

(未经测试,仍然可能完全错误(实际执行update'.

问题和可能的修复程序与changItemStatus相似。

相关内容

  • 没有找到相关文章