Home > Software design >  Doobie - lifting arbitrary effect into ConnectionIO CE3
Doobie - lifting arbitrary effect into ConnectionIO CE3

Time:02-25

I am trying to migrate project from cats-effect 2 to cats-effect 3, i am using doobie for interacting with database. Previously i could lift ConnectionIO to IO as it was described, but with the upgrade i didn't find any implementation of LiftIO[ConnectionIO], how can be same achieved with CE3?

CodePudding user response:

There's two approaches here. Broadly, there's the "don't do that" option (recommended), and the other way would be to use WeakAsync

"Don't do that"

Generally, it's not recommended to interleave arbitrary IO into a ConnectionIO program, because ConnectionIO is transactional, and you cannot roll back an IO. A ConnectionIO might run multiple times, for example to retry a recoverable failure.

When possible, it's a good idea to refactor the code to not do the non-transactional operation inside a transaction, but instead to perform it afterwards

However, when that's not practical, ConnectionIO provides a Sync instance, and so there's a number of things you can do with it easily that don't require lifting from IO in the first place:

  • printing to the console can be done by summoning Console[ConnectionIO]
  • Getting the current time can be done with Clock[ConnectionIO]
  • You can create a log4cats Logger[ConnectionIO] as needed using the appropriate factory for your backend
  • Generating a UUID value can be done with UUIDGen[ConnectionIO]

Using WeakAsync

Doobie's WeakAsync provides a way to get a Resource[F, F ~> ConnectionIO]

Note that because this is a resource, you must take care to complete the transaction inside of use - the lifecycle of the FunctionK from the resource will be shut down once use completes.

Usually that means something like this:

def doStuff(rows: List[Int]): F[Unit] = ???

WeakAsync.liftK[F, ConnectionIO].use { fk =>
  val transaction = for {
    rows <- sql"select 1".query[Int].to[List]
    _ <- fk(doStuff(rows))
  } yield ()

  transaction.transact(xa)
}

CodePudding user response:

I found the way to achieve it by

def f()(implicit liftToConnectionIO: FunctionK[IO, ConnectionIO]): IO[Unit] = IO.unit
implicit val liftToConnectionIO = WeakAsync.liftK[IO, ConnectionIO]
liftToConnectionIO.use(implicit lift => f())
  • Related