Doobie - 将任意效果提升到 ConnectionIO

Doobie - lifting arbitrary effect into ConnectionIO

我正在尝试在使用 Doobie 将用户插入数据库的同一事务中发送电子邮件。
我知道我可以通过使用 Async[ConnectionIO].liftIO(catsIO) where catsIO: IO[String]
IO 提升到 ConnectionIO 但是在我的代码中我不对 IO 进行操作,我使用 F 有约束,例如 F[_]: Async 这样我就可以用我自己的 monad 替换 F 进行测试。

是否可以在不直接使用 IO 类型的情况下以某种方式将 F[String] 提升为 ConnectionIO[String]

这是我为 IO 类型找到的答案:

是的,您可以轻松地将 F[String] 实例化为 ConnectionIO[String]。 给定一个函数,如:

def foo[F[_]: Async]: F[String] = ...

要实例化到 ConnectionIO 你可以简单地这样做:

def fooCIO: ConnectionIO[String] = foo[ConnectionIO]

Cats 有一个叫做 FunctionK 的东西,它是一种自然变换。

我这样做了:

在世界之巅,一切都在这里建造,你将需要这个

val liftToConnIO: FunctionK[IO, ConnectionIO] = LiftIO.liftK[ConnectionIO]

在class中需要从F[String]转为G[String](F会是IO,G会是ConnectionIO当你构造一切)你可以通过liftToConnIO并使用它在需要的地方将 F[A] 转换为 G[A]。

不想抽象过IO和ConnectionIO的class可以通过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 上的上下文边界(同步?))

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] 支持通过 toIOF[A] 转换为 IO[A]LiftIO[G] 支持将 IO[A] 转换为 G[A] 通过 to[G].