Search code examples
haskellhappstackhdbc

How to pass HTTP request parameter to quickQuery?


I'm using Happstack to receive some parameters from an HTTP request then pass these parameters to a function that will retrieve data from the database and return this data in the HTTP response as follow:

myFunc :: IO String
myFunc = do r <- look "personId"
            conn <- connectODBC "... my connection string ...";
            vals <- quickQuery conn ("SELECT Name FROM Person where Id = ?") [(toSql r)];
            return (processData vals)

handlers :: ServerPartT IO Response
handlers = do
       x <- liftIO (myFunc);
       decodeBody (defaultBodyPolicy "/tmp/" 0 1000 1000)
       msum [ 
              dir "getData" $ ok $ toResponse x
            , ... other handlers ...
            ]

mainFunc = simpleHTTP nullConf handlers

But when I build the above code I get the following error:

No instance for (HasRqData IO) arising from a use of `look' In a stmt of a 'do' block: r <- look "personId"

After reading questions on similar issues (like this one) I think I have to include HasRqData constraint somewhere, but I couldn't learn where and how.


Solution

  • As you may have guessed, this is too an issue with monads. There are a handful of them in happstack (HasRqData, among others), so you may well consider it a complicated case.

    Let us begin with the innocent-looking look function.

    look :: (Functor m, Monad m, HasRqData m) => String -> m String
    

    Indeed, there is a non-trivial constraint HasRqData. Let us ask ourselves: what monads HaveRqData? (It so happens that IO has not!)

    class HasRqData m where
    ...
    
    Instances
        HasRqData RqData         
        (MonadIO m, MonadPlus m) => HasRqData (ServerPartT m)
        ...
    

    The other instances are derivative of these first two, so, it looks like we have to consider these two options first.

    • The RqData has limited effects — you can only do look and its derivatives, extracting information from the request at hand. As we want to also have other effects — querying the database, for one, — this is not enough for us.
    • The ServerPartT m is the general form of the same old friend of ours, ServerPart ≡ ServerPartT IO. It happens that it also HasRqData. This is not a coincidence, but rather an implication of the design of happstack — looks like the authors meant us to use this single monad everywhere, unless we need particular granularity. So, let's give it a try.

     

    myFunc :: ServerPart String
    myFunc = do r <- look "personId"
                return undefined
    

    This compiles.

    Now, we don't even need to lift myFunc in handlers — our previous predicament solved itself. We will need to lift our access to the database though, by the same jar logic we discussed before.

    I believe you can figure the details by yourself. In any case, let me know how it works out!