我在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
这些错误专门抱怨insertItem
和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
的,因为它们直接生成响应(。
我试图通过在表达式的各个部分上使用:t
来将insertItem
和changeItemStatus
的类型签名放在一起,但每次尝试都给了我我认为是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
,其中a
是newItem
的结果类型。现在
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
相似。