Переменная неоднозначного типа в функциях 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,

Другие вопросы по тегам