将持久与 RIO 日志记录相结合以转储表



我正在写一个玩具示例来学习使用持久库访问Haskell数据库。为了玩一玩,我想看看数据库中有什么(内存中的SQLite(:

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)

(摘自哈斯克尔学校(

因为我想为我的应用程序使用 RIO 库,所以上述方法不起作用:我需要使用其中一个 RIO 日志记录函数而不是打印,并且该函数必须在 RIO monad 中运行。这是我这样做的尝试:

{-# 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)

但是,此代码不键入检查。我收到以下错误:

• 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)
|         ^^^^^^^^^^^^^^^^^^^^^^

我不明白这个错误是什么意思。如果有人能给我一些关于如何继续分析这个错误的提示,从而提高我对所涉及的类型类和 monads 的理解,那就太好了。

首先而不是

dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery "select * from <> tableName" [] $$ CL.mapM_ (liftIO . print)

你可能想要这个

dumpTable :: Text -> IO ()
dumpTable tableName = PSQL.rawQuery ("select * from " <> tableName) [] $$ CL.mapM_ (liftIO . print)

现在假设这个版本,您在这里所做的是,您为dumpTable选择一个不应进行类型检查的具体类型IO

函数应该这样写

dumpTable
:: (MonadResource m, MonadReader env m,
BackendCompatible SqlBackend env) =>
Text -> m ()
dumpTable tableName = PSQL.rawQuery ("select * from " <> tableName)  [] $$ CL.mapM_ (liftIO . print)

我不知道您可能指的是哪个具体示例,但是runQuery一个简单的示例看起来像这样

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)

上面的例子来自 转储表

在不赘述太多细节的情况下,满足这些约束ReaderT SqlBackend (NoLoggingT (ResourceT IO))方法是使用每个单元变压器的run函数。对于ReaderT来说,这将是runReaderTrunReaderT configData $ monadReaderConstraiendFunction.

无论如何,在进入这个库之前,您可能想看看Monad Transformers是如何工作的。这不会花费太长时间,一旦你掌握了它的要点,你就可以调试任何进一步的问题。

说到这里,现在让我们看一下错误消息的这一部分

• Could not deduce (PSQL.BackendCompatible PSQL.SqlBackend env)
arising from a use of ‘PSQL.rawQuery’
from the context: HasLogFunc env

和您的函数类型

dumpTable :: (HasLogFunc env) => Text -> RIO env ()

monadenvHasLogFunc约束,但函数rawQuery需要其他几个上下文才能工作,如上所示。

rawQuery的函数类型(MonadResource m, MonadReader env m, BackendCompatible SqlBackend env)可以看出。

基本上,我们需要通过在其类型签名中显式定义这些来帮助GHC。我不知道你是如何与RIOmonad互动的,但最常见的情况应该是这样的

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)

现在这将键入检查。

最新更新