1

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

Bet
  • 389
  • 4
  • 13

1 Answers1

1

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.

bmk
  • 1,548
  • 9
  • 17
  • Function declaration deleteUserR now exists in module Handler.User, but when Yesod runner try call deleteUserR from module Application, compiler write Yesod specific Error: No instance for (ToTypedContent res0) arising from a use of `yesodRunner' The type variable `res0' is ambiguous – Bet Jan 18 '15 at 12:34
  • Hi, Maybe we should add type signature to the generated function declaration. I have updated the code example in my answer. Try it again with right type signature, in your case: `funcName :: UserId -> Handler Value` – bmk Jan 18 '15 at 12:49
  • Problem: I don't know type signature for `funcName` It have type something like this: `Key User -> Handler Value` but type User is argument of mkDeleteHandler, where `Key User = UserId` – Bet Jan 18 '15 at 13:07
  • Hmm, I have no idea what you are exactly trying to do without seeing the full code... Can you update your question with full code? – bmk Jan 18 '15 at 13:17
  • Do you have yesod packages installed? For example, default Yesod scaffolding site enough to get error – Bet Jan 18 '15 at 13:33
  • Do you get an error too, if you define the function `deleteUserR` "manually" like you did in your question? – bmk Jan 18 '15 at 13:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69062/discussion-between-bmk-and-bet). – bmk Jan 18 '15 at 13:42
  • I've updated the answer with a simple type parametrization example. It should be straight forward to adapt the example for your case. – bmk Jan 18 '15 at 18:03
  • Very good example. It is really solve problem. Can I write code with TH something like this `funcName :: $(mytype) -> IO()`, where $(mytype) - compile time evaluation type? – Bet Jan 18 '15 at 18:39