使用模板 Haskell 自动声明 yesod 处理程序

Automatically declaration yesod handlers using 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

编辑:添加了简单类型签名参数化示例

我认为您的 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,正如编译器已正确警告的那样。解决方法是我们使用引号括号生成一个 "template" 函数声明,然后将生成的函数名称替换为我们自己的 hname.

恕我直言,学习 Template Haskell 的最佳教程是 this one