scalascala-catscats-effectdoobie

Doobie - lifting arbitrary effect into ConnectionIO


I'm trying to send an email in the same transaction as inserting user into a database with Doobie.
I know that I can lift IO into ConnectionIO by using Async[ConnectionIO].liftIO(catsIO) where catsIO: IO[String]
But in my code I don't operate on IO, I use F with constraints, for example F[_]: Async So then I can replace F with my own monad for testing.

Is it possible to somehow lift an F[String] into ConnectionIO[String] without using IO type directly?

Here is an answer I found for IO type: Doobie and DB access composition within 1 transaction


Solution

  • Cats has something called FunctionK which is a natural transformation.

    I did this:

    At the top of the world, where everything is built, you will need this

    val liftToConnIO: FunctionK[IO, ConnectionIO] = LiftIO.liftK[ConnectionIO]
    

    In the class needing to transform from F[String] to G[String] (F will be IO, G will be ConnectionIO when you construct everything) you can pass liftToConnIO and use it to transform F[A]to G[A] where needed.

    The class that doesn't wants to abstract over IO and ConnectionIO can be passed the FunctionK to do the lifting:

    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 ()
    
    }
    

    (You might need context bounds (Sync?) on F and G)