2

我在 Haskell Web 项目中遇到了错误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

这些错误专门抱怨insertItemand changeItemStatus,这两个都是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})

withAccount并且withItem是帮助我处理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,因为它们直接生成响应)。

我试图通过使用表达式的片段来组合类型签名insertItem,但每次尝试都给了我我认为更糟糕的错误。changeItemStatus:tNo instance for (MonadIO m1)

我还不是一个特别熟练的 Haskeller,所以我觉得我唯一可以尝试的另一件事就是return $在这个地方撒上随机 s,但这听起来不太可能真正解决问题或教学我什么。

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

关于我接下来应该尝试什么,或者我具体哪里出错的任何提示?我是否以完全错误的方式思考这个问题?我是否可以就该主题查阅其他文件?

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

编辑:完整类型错误

/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 }
4

1 回答 1

2

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

让我们看看问题儿童,以及他们实际做了什么:

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.

于 2013-02-06T20:28:30.523 回答