I'm writing a toy example to learn Haskell database access with the Persistent library. To play around, I want to see what's in the DB (SQLite in Memory):
import qualified Database.Persist.Sql as PSQL
import qualified Data.Conduit.List as CL
import Data.Conduit ( ($$) )
import Control.Monad.IO.Class (liftIO)
dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery "select * from " <> tableName [] $$ CL.mapM_ (liftIO . print)
(Taken from the School of Haskell)
Because I want to use the RIO libraries for my applications, the above does not work: I need to use one of the RIO logging functions instead of print, and the function must run in the RIO monad. Here is my attempt to do so:
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
[..]
import RIO
import qualified Database.Persist.Sql as PSQL
import Data.Conduit ( ($$) )
import qualified Data.Conduit.List as CL
dumpTable :: (HasLogFunc env) => Text -> RIO env ()
dumpTable tableName =
let query = "select * from " <> tableName
in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
However, this code does not type check. I get the following error:
• Could not deduce (PSQL.BackendCompatible PSQL.SqlBackend env)
arising from a use of ‘PSQL.rawQuery’
from the context: HasLogFunc env
bound by the type signature for:
dumpTable :: forall env. HasLogFunc env => Text -> RIO env ()
at src/Persistence/DbInspect.hs:13:1-51
• In the first argument of ‘($$)’, namely ‘PSQL.rawQuery query []’
In the expression:
PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
In the expression:
let query = "select * from " <> tableName
in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
|
16 | in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
| ^^^^^^^^^^^^^^^^^^^^^^
I do not understand what this error means. It would be great if someone could give me some hints on how to go ahead and analyse this error, thereby improving my understanding of the involved typeclasses and monads.
Firstly instead of
dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery "select * from <> tableName" [] $$ CL.mapM_ (liftIO . print)
you probably want this
dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery ("select * from " <> tableName) [] $$ CL.mapM_ (liftIO . print)
Now assuming this version, what you have done here is you have selected a concrete type IO
for dumpTable
which should not type check.
The function should be written like this
dumpTable
:: (MonadResource m, MonadReader env m,
BackendCompatible SqlBackend env) =>
Text -> m ()
dumpTable tableName = PSQL.rawQuery ("select * from " <> tableName) [] $$ CL.mapM_ (liftIO . print)
I don't know which specific example you might be referring to but a simple example for runQuery
would look something like this
main :: IO ()
main = runSqlite ":memory:" $ do
buildDb
dumpTable
buildDb
:: ReaderT SqlBackend (NoLoggingT (ResourceT IO)) (Key Tutorial)
buildDb = do
runMigrationSilent migrateTables
insert $ Tutorial "Basic Haskell" "https://fpcomplete.com/school/basic-haskell-1" True
insert $ Tutorial "A monad tutorial" "https://fpcomplete.com/user/anne/monads" False
insert $ Tutorial "Yesod usage" "https://fpcomplete.com/school/basic-yesod" True
insert $ Tutorial "Putting the FUN in functors" "https://fpcomplete.com/user/anne/functors" False
insert $ Tutorial "Basic Haskell" "https://fpcomplete/user/anne/basics" False
dumpTable
:: ReaderT SqlBackend (NoLoggingT (ResourceT IO)) ()
dumpTable = rawQuery "select * from Tutorial" [] $$ CL.mapM_ (liftIO . print)
Above example is from Dumping a table
Without going into too much details, the way to satisfy these constraints ReaderT SqlBackend (NoLoggingT (ResourceT IO))
is by using each monad transformers' run
functions. For ReaderT
that would be runReaderT
i.e. runReaderT configData $ monadReaderConstraiendFunction
.
Anyways, you might want to take a look at how Monad Transformers
work before diving into this library. It won't take too long, and once you get the gist of it you'll be able to debug any further problems.
Speaking of which, now lets take a look at this part of the error message
• Could not deduce (PSQL.BackendCompatible PSQL.SqlBackend env)
arising from a use of ‘PSQL.rawQuery’
from the context: HasLogFunc env
and your function type
dumpTable :: (HasLogFunc env) => Text -> RIO env ()
The monad env
is constrained on HasLogFunc
but the function rawQuery
requires several other contexts to work in as we saw above.
You can see that from rawQuery
's function type (MonadResource m, MonadReader env m, BackendCompatible SqlBackend env)
.
Basically we need to help GHC out by explicitly defining those in its type signature. I don't know how you're interacting with the RIO
monad, but the most general case should look like this
dumpTable :: (HasLogFunc env
, PSQL.BackendCompatible PSQL.SqlBackend env
, MonadResource (RIO env)
)
=> Text
-> RIO env ()
dumpTable tableName =
let query = "select * from " <> tableName
in PSQL.rawQuery query [] $$ CL.mapM_ (logInfo . displayShow)
Now this will type check.