我想编写一个基本的doobie读/写事务,但是最重要的是中间有一个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
。)有意义吗?知道这是否可行吗?
答案 0 :(得分:2)
理论上,您可以使用LiftIO
提供并由doobie实现的cats-effect
类型类,
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]
}