我一直在努力深入了解 mtl通过将其与 persistent 结合使用来构建项目.
该项目的一个模块有一个使用 insertMany_ 的函数
service
:: (MonadReader ApplicationConfig m, MonadIO m) =>
ReaderT SqlBackend (ExceptT ApplicationError m) ()
service = insertMany_ =<< lift talkToAPI
此处 talkToAPI
可能会失败,因此响应被包装在 ExceptT
中,其类型为
ExceptT ApplicationError m [Example]
简而言之,服务
的工作是与 API 对话、解析响应并使用 insertMany_
将该响应存储到数据库中。
实际的存储操作由withPostgresqlConn处理。
withPostgresqlConn
:: (MonadUnliftIO m, MonadLogger m) =>
ConnectionString -> (SqlBackend -> m a) -> m a
在我的 service
函数上使用 runReaderT
会产生
ghci> :t runReaderT service
ghci> (MonadReader ApplicationConfig m, MonadIO m) =>
SqlBackend -> ExceptT ApplicationError m ()
所以要处理这个我相信我需要像这样使用runExceptT
runService :: ConnectionString -> IO ()
runService connStr = either print return
=<< runStdLoggingT (runExceptT $ withPostgresqlConn connStr $ runReaderT service)
但是我收到这两个错误
• No instance for (MonadUnliftIO (ExceptT ApplicationError IO))
arising from a use of ‘withPostgresqlConn’
• No instance for (MonadReader ApplicationConfig IO)
arising from a use of ‘service’
这里可能出现什么问题?我可能有一个错误,但我不知道在哪里寻找。
最佳答案
一个问题是 ExceptT ApplicationError IO
没有——事实上也不可能——有 MonadUnliftIO
实例。很少有 monad 具有这样的实例:IO
(简单的情况)以及 Identity
和 Reader
上的类似 IO
的转换器.
解决方案是在将 service
传递给 withPostgresqlConn
之前,而不是之后,“剥离”ExceptT
构造函数。也就是说,传递 SqlBackend -> m (Either ApplicationError ())
值而不是 SqlBackend -> exceptT ApplicationError m ()
值。您可以通过使用 runExceptT
组合函数来实现这一点。 .
我们仍然需要为m
选择一个具体类型,以便它满足
service
所需的 MonadReader ApplicationConfig m、MonadIO m
约束以及 withPostgresqlConn
所需的 MonadUnliftIO m、MonadLogger m
约束>。 (实际上,我们可以忘记 MonadIO
,因为 MonadUnliftIO
无论如何都暗示了它)。
在您的代码中,您调用 runStdLoggingT
并期望进入 IO
。这意味着 m
预计为 LoggingT IO
。没关系,因为 LoggingT
有一个 MonadUnliftIO
实例,当然还有一个 MonadLogger
实例。但有一个问题:什么满足 MonadReader ApplicationConfig
约束?配置从哪里来?这就是第二个错误的原因。
解决方案是使m
类似于ReaderT ApplicationConfig (LoggingT IO)
。 runService
函数应采用额外的 ApplicationConfig
参数,并在调用 runStdLoggingT
之前使用配置调用 runReader
。 p>
更普遍的一点是,monad 转换器通常具有“直通”实例,这些实例表示“如果基础 monad 是类型类 C 的实例,那么转换后的 monad 也是 C 的实例”。例如 MonadLogger m => MonadLogger (ReaderT r m)
或 MonadUnliftIO m => MonadUnliftIO (ReaderT r m)
。但这样的实例并不总是存在于每个转换器类型类组合中。
关于haskell - 如何修复 MonadReader 和 MonadIO 上约束的函数缺少的 IO 实例?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62360268/