haskellsqlitehdbcscotty

Scotty Parameters to feed in Sqlite3


I'm trying to create a web site that will get information via URL routes and then pass this information into a HDBC SQLITE3 database. I have figured out how to get the information via parameters using Scotty as well as how to create a database using HDBC. But I'm having trouble passing the parameter information to the database. I'm getting this error: (sorry for such a beginner problem, I've only been using Haskell for about three weeks)

Controllers/Home.hs:51:48:
No instance for (Convertible a0 SqlValue)
  arising from a use of `toSql'
Possible fix:
  add an instance declaration for (Convertible a0 SqlValue)
In the expression: toSql
In the expression: toSql $ userId
In the third argument of `run', namely
  `[toSql $ userId, toSql $ name]'

Controllers/Home.hs:51:64:
No instance for (Convertible a1 SqlValue)
  arising from a use of `toSql'
Possible fix:
  add an instance declaration for (Convertible a1 SqlValue)
In the expression: toSql
In the expression: toSql $ name
In the third argument of `run', namely
  `[toSql $ userId, toSql $ name]'

Here is the code I'm trying to run:

import Control.Monad
import Web.Scotty (ScottyM, ActionM, get, html, param)
import Data.Monoid (mconcat)
import Database.HDBC
import Database.HDBC.Sqlite3
import Control.Monad.Trans ( MonadIO(liftIO) )
import Data.Convertible
createUser ::  ScottyM()
createUser = get "/create/user/:userId/:name" $ do
  name <- param "name"
  userId <- param "userId"
  liftIO $ createUserDB name userId
  html $ mconcat ["<p>/create/user/" , userId , "/" , name ,"</p>"]
createUserDB :: a1 -> a0 -> IO()
createUserDB name userId = do
  conn <- connectSqlite3 databaseFilePath
  run conn "INSERT INTO users VALUES (? , ?)" [toSql $ userId, toSql $ name]
  commit conn
  disconnect conn
databaseFilePath = "data/mydb.db"

If someone could provide how to fix this, as well as why their fix works that'd be really helpful!


Solution

  • I think the compiler is complaining because the code requires that the arguments to createUserDB satisfy the constraint in the error message (i.e. that they are convertible to Sqlvalue), but the signature you have given doesn't provide that guarantee (a0 and a1 can be any type).

    The simplest option is to remove the type signature for createUserDB and just let the compiler infer it. Alternatively you can restrict it to Text values (since name and userId are of type Text and Database.HDBC.SqlValue defines an instance for Convertible Text SqlValue):

    createUserDB :: Text -> Text -> IO ()
    

    In general you can also use a context in the function signature to restrict the types:

    createUserDB :: (Convertible a0 SqlValue, Convertible a1 SqlValue) => a1 -> a0 -> IO ()
    

    but that's probably not a great idea here where the types are clearly SQL strings and probably mapped to varchars.