当 IO 卡在中间时如何编写 doobie 事务
How to write a doobie transaction when there's an IO stuck in the middle
我想写一个基本的 doobie read/write 交易,但更重要的是中间有一个 IO
返回调用。我想做这样的事情:
abstract class MyDAO {
def readSomething(id: String): ConnectionIO[Option[Something]]
def writeSomething(something: Something): ConnectionIO[Unit]
}
class MyService {
def getNewSomething: IO[Something] = ???
}
class MyProgram(myDAO: MyDAO, myService: MyService, xa: DataSourceTransactor[IO]) {
val transaction = myDAO.readSomething("xyz").flatMap {
case Some(thing) => IO.pure(thing).pure[ConnectionIO] //ConnectionIO[IO[Something]]
case None => myService.getNewSomething.map { newSomething =>
myDAO.writeSomething(newSomething).map(_ => newSomething)
}.sequence[ConnectionIO, Something] //can't sequence an IO! So I'm stuck with IO[ConnectionIO[Something]]
}
transaction.transact(xa)
}
但我无法在 IO
上排序。因此,在第二种情况下,我会被 IO[ConnectionIO[Something]]
困住,这意味着我的交易最终会像 ConnectionIO[IO[ConnectionIO[Something]]
一样。
我想要的是一个 ConnectionIO[IO[Something]]
,然后我可以在单个事务中 运行 产生 IO[IO[Something]]
然后我可以轻松地压平它。 (我不想运行 IO
。)有道理吗?知道这是否有可能实现吗?
理论上您可以使用 cats-effect
提供并由 doobie 实现的 LiftIO
类型类,如下所示:
import cats.effect._
import doobie._
import doobie.implicits._
def read: ConnectionIO[Int] = ???
def write(s: String): ConnectionIO[Unit] = ???
def transform(i: Int): IO[String] = ???
val transaction: ConnectionIO[Unit] = for {
i <- read
s <- transform(i).to[ConnectionIO]
_ <- write(s)
} yield ()
transaction.transact(xa)
注意 to[ConnectionIO]
。它需要一个 LiftIO
类型的隐式参数,它看起来像这样并且几乎可以做你想做的事情(将 IO
提升为 F
):
trait LiftIO[F[_]] {
def liftIO[A](ioa: IO[A]): F[A]
}
我想写一个基本的 doobie read/write 交易,但更重要的是中间有一个 IO
返回调用。我想做这样的事情:
abstract class MyDAO {
def readSomething(id: String): ConnectionIO[Option[Something]]
def writeSomething(something: Something): ConnectionIO[Unit]
}
class MyService {
def getNewSomething: IO[Something] = ???
}
class MyProgram(myDAO: MyDAO, myService: MyService, xa: DataSourceTransactor[IO]) {
val transaction = myDAO.readSomething("xyz").flatMap {
case Some(thing) => IO.pure(thing).pure[ConnectionIO] //ConnectionIO[IO[Something]]
case None => myService.getNewSomething.map { newSomething =>
myDAO.writeSomething(newSomething).map(_ => newSomething)
}.sequence[ConnectionIO, Something] //can't sequence an IO! So I'm stuck with IO[ConnectionIO[Something]]
}
transaction.transact(xa)
}
但我无法在 IO
上排序。因此,在第二种情况下,我会被 IO[ConnectionIO[Something]]
困住,这意味着我的交易最终会像 ConnectionIO[IO[ConnectionIO[Something]]
一样。
我想要的是一个 ConnectionIO[IO[Something]]
,然后我可以在单个事务中 运行 产生 IO[IO[Something]]
然后我可以轻松地压平它。 (我不想运行 IO
。)有道理吗?知道这是否有可能实现吗?
理论上您可以使用 cats-effect
提供并由 doobie 实现的 LiftIO
类型类,如下所示:
import cats.effect._
import doobie._
import doobie.implicits._
def read: ConnectionIO[Int] = ???
def write(s: String): ConnectionIO[Unit] = ???
def transform(i: Int): IO[String] = ???
val transaction: ConnectionIO[Unit] = for {
i <- read
s <- transform(i).to[ConnectionIO]
_ <- write(s)
} yield ()
transaction.transact(xa)
注意 to[ConnectionIO]
。它需要一个 LiftIO
类型的隐式参数,它看起来像这样并且几乎可以做你想做的事情(将 IO
提升为 F
):
trait LiftIO[F[_]] {
def liftIO[A](ioa: IO[A]): F[A]
}