【问题标题】:Doobie - lifting arbitrary effect into ConnectionIODoobie - 将任意效果提升到 ConnectionIO
【发布时间】:2020-04-27 03:50:56
【问题描述】:
我正在尝试在与使用 Doobie 将用户插入数据库的同一事务中发送电子邮件。
我知道我可以使用Async[ConnectionIO].liftIO(catsIO) 将IO 提升为ConnectionIO,其中catsIO: IO[String]
但是在我的代码中我不对IO 进行操作,我使用F 和约束,例如F[_]: Async
那么我可以用我自己的 monad 替换 F 进行测试。
是否可以在不直接使用IO 类型的情况下以某种方式将F[String] 提升为ConnectionIO[String]?
这是我为 IO 类型找到的答案:Doobie and DB access composition within 1 transaction
【问题讨论】:
标签:
scala
scala-cats
cats-effect
doobie
【解决方案1】:
Cats 有一个叫做 FunctionK 的东西,它是一种自然变换。
我这样做了:
在世界之巅,一切都在这里建造,您将需要这个
val liftToConnIO: FunctionK[IO, ConnectionIO] = LiftIO.liftK[ConnectionIO]
在需要从 F[String] 转换为 G[String] 的类中(F 将是 IO,G 将是 ConnectionIO 当您构造所有内容时)您可以传递liftToConnIO 并使用它来将 F[A] 转换为G[A] 在需要的地方。
不想在IO和ConnectionIO上抽象的类可以通过FunctionK来做提升:
class Stuff[F[_], G[_]](emailer: Emailer[F], store: Store[G], liftToG: FunctionK[F, G]) {
def sendEmail: G[Unit] =
for {
_ <- doDatabaseThingsReturnStuffInG
_ <- liftToG(emailer.sendEmail)
_ <- doMoreDatabaseThingsReturnStuffInG
} yield ()
}
(您可能需要 F 和 G 上的上下文边界(同步?))
【解决方案2】:
是的,您可以轻松地将您的 F[String] 实例化为 ConnectionIO[String]。
给定一个类似的函数:
def foo[F[_]: Async]: F[String] = ...
要实例化到ConnectionIO,您只需这样做:
def fooCIO: ConnectionIO[String] = foo[ConnectionIO]
【解决方案3】:
Channing 答案的变体,
class Stuff[F[_] : Effect, G[_] : LiftIO](emailer: Emailer[F], store: Store[G]) {
def sendEmail: G[Unit] =
for {
_ <- doDatabaseThingsReturnStuffInG
_ <- emailer.sendEmail.toIO.to[G]
_ <- doMoreDatabaseThingsReturnStuffInG
} yield ()
}
Effect[F] 支持通过toIO 将F[A] 转为IO[A],LiftIO[G] 支持通过to[G] 将IO[A] 转为G[A]。