Переменная неоднозначного типа в функциях 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
На ошибки конкретно жалуются 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
Хорошо, теперь тогда.
Я несколько раз перечитывал документацию по AcidState в крушительном курсе Happstack, но это мало помогало; они используют запросы непосредственно в своих функциях генерации ответов, которые
- Я попробовал и получил то же самое
Ambiguous type variable
ошибка, кроме указания наquery'
позвони себе, - Я не очень хочу этого делать, так как это заставило бы меня смешать код модели / контроллера и
- не помогает моей конкретной ситуации, так как она не показывает мне, какой конкретный тип возвращаемой функции вызывает
query'
или жеupdate'
будет (их функции всеAcidState DBName -> ServerPart Response
, поскольку они генерируют ответ напрямую).
Я пытался собрать подписи типа для insertItem
а также changeItemStatus
используя :t
на части выражения, но каждая попытка дала мне, что я предполагаю, является худшей ошибкой No instance for (MonadIO m1)
вместо.
Я пока не особенно знаток Хаскеллера, поэтому я чувствую, что единственное, что я могу попробовать, - это разбрызгивать случайно return $
Вокруг этого места, но это не похоже на то, что у него был бы хороший шанс на самом деле решить проблему или научить меня чему-нибудь.
Общая концепция, которую я пытаюсь реализовать: "Внесите это изменение в БД, а затем верните (потенциально измененный) список релевантных элементов для текущего пользователя".
Любые намеки на то, что я должен попробовать дальше, или где я специально иду не так? Думаю ли я об этом совершенно неправильно? Есть ли другая документация, которую я мог бы проконсультировать по этому вопросу?
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 }
1 ответ
Напишите сигнатуры типов для ваших функций, чтобы понять, что они должны делать. Напишите их, прежде чем писать реализацию, затем вы можете использовать сигнатуры типов и лучшие сообщения об ошибках, которые вы получите от компилятора, если он знает, какой тип вы хотите иметь, чтобы проверить, что реализация делает [или, по крайней мере, имеет шанс делай что хочешь.
Давайте посмотрим на проблему детей, и что они на самом деле делают:
От 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
,