6

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

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

这是我为 IO 类型找到的答案:Doobie and DB access composition within 1 transaction

4

3 回答 3

8

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 上的上下文边界(同步?))

于 2020-04-24T10:38:42.363 回答
3

钱宁答案的变体,

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]支持采用F[A]to an IO[A]via toIO,并LiftIO[G]支持采用IO[A]to a G[A]via to[G]

于 2020-04-26T17:38:06.970 回答
3

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

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

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

def fooCIO: ConnectionIO[String] = foo[ConnectionIO]
于 2020-01-13T18:16:30.580 回答