我正在写一个玩具示例来学习使用持久库访问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
来说,这将是runReaderT
即runReaderT 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 ()
monadenv
受HasLogFunc
约束,但函数rawQuery
需要其他几个上下文才能工作,如上所示。
从rawQuery
的函数类型(MonadResource m, MonadReader env m, BackendCompatible SqlBackend env)
可以看出。
基本上,我们需要通过在其类型签名中显式定义这些来帮助GHC。我不知道你是如何与RIO
monad互动的,但最常见的情况应该是这样的
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)
现在这将键入检查。