Как написать транзакцию Дуби, когда IO застрял в середине
Я хочу написать базовую транзакцию чтения / записи Дуби, но главное, что есть 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
.) Имеет смысл? Любая идея, если это возможно осуществить?
1 ответ
Вы можете теоретически использовать LiftIO
Класс типов предоставлен cats-effect
и реализовано doobie следующим образом:
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]
}