haskell - 如何为受 MonadReader 和 MonadIO 约束的函数修复缺少的 IO 实例?
问题描述
我一直在尝试通过将mtl 与persistent结合使用来构建项目,从而对mtl有一个很好的理解。
该项目的一个模块具有使用insertMany_的功能
service
:: (MonadReader ApplicationConfig m, MonadIO m) =>
ReaderT SqlBackend (ExceptT ApplicationError m) ()
service = insertMany_ =<< lift talkToAPI
这里talkToAPI
可能会失败,因此响应包含在 中ExceptT
,它的类型是
ExceptT ApplicationError m [Example]
简而言之,service
的工作是与 API 对话,解析响应并将insertMany_
响应存储到数据库中。
实际的存储操作由withPostgresqlConn处理
withPostgresqlConn
:: (MonadUnliftIO m, MonadLogger m) =>
ConnectionString -> (SqlBackend -> m a) -> m a
runReaderT
在我的函数上使用service
会产生
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
类似的转换器 over IO
.
解决方案是在传递给之前“剥离”ExceptT
构造函数,而不是之后。也就是说,传递一个值而不是一个值。您可以通过将函数与.service
withPostgresqlConn
SqlBackend -> m (Either ApplicationError ())
SqlBackend -> ExceptT ApplicationError m ()
runExceptT
我们仍然必须选择一个具体的类型,m
以便它满足所需的MonadReader ApplicationConfig m, MonadIO m
约束service
以及MonadUnliftIO m, MonadLogger m
所需的约束withPostgresqlConn
。(实际上,我们可以忘记MonadIO
因为MonadUnliftIO
无论如何都暗示它)。
在您的代码中,您调用runStdLoggingT
并期望得到IO
. 这意味着m
预计将是LoggingT IO
. 这很好,因为LoggingT
有一个MonadUnliftIO
实例,当然还有MonadLogger
一个。但是有一个问题:什么满足MonadReader ApplicationConfig
约束?哪里来的配置?这就是第二个错误的原因。
解决方案是制作m
类似ReaderT ApplicationConfig (LoggingT IO)
. 该runService
函数应该接受一个额外的ApplicationConfig
参数,并runReader
在调用之前使用配置调用runStdLoggingT
。
更一般的一点是,monad 转换器通常具有“直通”实例,例如“如果基本 monad 是类型类 C 的实例,则转换后的 monad 也是 C 的实例”。例如。MonadLogger m => MonadLogger (ReaderT r m)
_ MonadUnliftIO m => MonadUnliftIO (ReaderT r m)
但是这种实例并不总是存在于每个转换器类型类组合中。
推荐阅读
- node.js - danfojs - `danfojs` 中是否有类似 pandas.DataFrame.to_html 的 to_html 方法?
- javascript - 出现带有图标的悬停时弹出图像
- postgresql - 如何在不丢失/截断数据的情况下将 postgres“双精度”转换为“数字”
- shader - 使用表面着色器修改器时,有没有办法跨像素传递值?
- groovy - 验证 JSON 响应中是否存在某些值的 Groovy 脚本断言
- sql - SQL alchemy ORM 括号 And 语句与 OR 语句
- javascript - Nuxt + Vuex:getter 看不到状态变量
- c# - 如何调用由其他函数创建的类?
- google-apps-script - 谷歌表格脚本查找范围内的单元格错误
- cmake - 依赖的 FetchContent 项目