Bet
Bet

Reputation: 404

Automatically declaration yesod handlers using Template Haskell

For example, I have next entity type in Model

User json
    username Text

and following Haskell types:

Entity User

Delete handler for User:

Routes file:

/users/#UserId UserR DELETE

Handler declaration:

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

I want to write template function something like this:

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)

In my Handler.User module I write

mkDeleteHandler "User"

But it is not working. Compiler write next warnings:

Warning: Defined but not used: hname

Warning: Defined but not used: idname

And Error:

Not in scope: deleteUserR

Upvotes: 1

Views: 123

Answers (1)

bmk
bmk

Reputation: 1568

EDIT: Simple type signature parameterization example added

I think there is a problem with function name binding in your mkDeleteHandler. I would try something like this for example (example simplified):

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

And then you can splice it something like this:

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

Note that the mkHandler should be defined in separate module and imported first before using it.

Your problem was that the hname was not used at all, like the compiler has correctly warned. The fix is that we generate a "template" function declaration using quotation brackets and then replace the generated function name with our own hname.

IMHO the best tutorial for learning Template Haskell is this one.

Upvotes: 1

Related Questions