haskell - 如何修复 MonadReader 和 MonadIO 上约束的函数缺少的 IO 实例?

标签 haskell monad-transformers haskell-persistent

我一直在努力深入了解 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(简单的情况)以及 IdentityReader 上的类似 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/

相关文章:

haskell - 在变压器堆栈的底部插入 ErrorT

haskell - MaybeT 的直觉

database - haskell "persistent"模型 : How to correctly define cross-reference?

Haskell 包构建错误

haskell - HXT 的计数和过滤箭头

parsing - haskell /三连胜 : Parsing completely optional semicolons without polluting AST

haskell - 仆人处理程序中的任一计算

haskell - 持久内连接还是应该使用 esqueleto?

haskell - 持久的 selectList 导致错误 "Couldn' t 匹配类型 ‘BaseBackend backend0’ 与 ‘SqlBackend’"

haskell - 如何在 haskell 中做到这一点? [x^0,x^1,x^2,x^3 ...]