Автоматическое объявление обработчиков yesod с помощью Template Haskell

Например, у меня есть следующий тип объекта в модели

User json
    username Text

и следующие типы Haskell:

Entity User

Удалить обработчик для пользователя:

Файл маршрутов:

/users/#UserId UserR DELETE

Объявление обработчика:

deleteUserR :: UserId -> Handler Value
deleteUserR uid = do
    runDB $ delete uid
    sendResponseStatus status200 ("DELETED" :: Text)

Я хочу написать функцию шаблона примерно так:

mkDeleteHandler :: String -> Q [Dec]
mkDeleteHandler name = do
    [d|hname idname = do
        runDB $ delete idname
        sendResponseStatus status200 ("DELETED" :: Text)|]
        where hname  = mkName ("delete" ++ name ++ "R")
              idname = mkName ("i" ++ name)

В моем модуле Handler.User я пишу

mkDeleteHandler "User"

Но это не работает. Компилятор пишет следующие предупреждения:

Внимание: определено, но не используется: hname

Внимание: определено, но не используется: idname

И ошибка:

Не входит в область: deleteUserR


person Bet    schedule 17.01.2015    source источник


Ответы (1)


РЕДАКТИРОВАТЬ: добавлен пример параметризации подписи простого типа.

Я думаю, что есть проблема с привязкой имени функции в вашем mkDeleteHandler. Я бы попробовал что-то вроде этого, например (упрощенный пример):

mkHandler :: String -> String -> Q [Dec]
mkHandler typeName funcName = do
  funDecl <- [d| funcName :: String -> IO()
                 funcName var1 = do
                 print var1 |]

  let [SigD _ (AppT _ t0), FunD _ funBody] = funDecl
      sigBody' = (AppT (AppT ArrowT (ConT tname)) t0)

  return $ [SigD hname sigBody', FunD hname funBody]

  where
    hname  = mkName funcName
    tname = mkName typeName

И тогда вы можете соединить его примерно так:

data Foo = Foo Int
     deriving Show

$(mkHandler "Int" "handlerInt")
$(mkHandler "String" "handlerString")
$(mkHandler "Foo" "handlerFoo")

main = do
  handlerInt 5
  handlerString "Hello World"
  handlerFoo $ Foo 5

Обратите внимание, что mkHandler должен быть определен в отдельном модуле и импортирован перед его использованием.

Ваша проблема заключалась в том, что hname вообще не использовался, как правильно предупредил компилятор. Исправление заключается в том, что мы генерируем «шаблонное» объявление функции, используя кавычки, а затем заменяем сгенерированное имя функции нашим собственным hname.

ИМХО, лучший учебник для изучения Template Haskell - это это один.

person bmk    schedule 18.01.2015
comment
Объявление функции deleteUserR теперь существует в модуле Handler.User, но когда исполнитель Yesod пытается вызвать deleteUserR из модуля Application, компилятор записывает специфичную для Yesod ошибку: нет экземпляра для (ToTypedContent res0), возникающего из-за использования yesodRunner' The type variable res0', является неоднозначным - person Bet; 18.01.2015
comment
Привет. Возможно, нам следует добавить сигнатуру типа к сгенерированному объявлению функции. Я обновил пример кода в своем ответе. Попробуйте еще раз с подписью правильного типа, в вашем случае: funcName :: UserId -> Handler Value - person bmk; 18.01.2015
comment
Проблема: я не знаю сигнатуру типа для funcName У него тип примерно такой: Key User -> Handler Value но тип User является аргументом mkDeleteHandler, где Key User = UserId - person Bet; 18.01.2015
comment
Хм, я понятия не имею, что именно вы пытаетесь сделать, не видя полного кода... Можете ли вы обновить свой вопрос, указав полный код? - person bmk; 18.01.2015
comment
У вас установлены пакеты yesod? Например, сайта строительных лесов Yesod по умолчанию достаточно, чтобы получить ошибку - person Bet; 18.01.2015
comment
Вы также получаете сообщение об ошибке, если определяете функцию deleteUserR вручную, как в своем вопросе? - person bmk; 18.01.2015
comment
Давайте продолжим обсуждение в чате. - person bmk; 18.01.2015
comment
Я обновил ответ примером параметризации простого типа. Это должно быть прямо вперед, чтобы адаптировать пример для вашего случая. - person bmk; 18.01.2015
comment
Очень хороший пример. Это действительно решает проблему. Могу ли я написать код с TH что-то вроде этого funcName :: $(mytype) -> IO(), где $(mytype) - тип оценки времени компиляции? - person Bet; 18.01.2015