Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 371
Leonti Avatar asked Jan 09 '20 04:01

Leonti


2 Answers

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)

like image 191
Channing Walton Avatar answered Sep 21 '22 13:09

Channing Walton


Yes, you can easily instantiate your F[String] into ConnectionIO[String]. Given a function like:

def foo[F[_]: Async]: F[String] = ...

To instantiate in to ConnectionIO you can simply do this:

def fooCIO: ConnectionIO[String] = foo[ConnectionIO]
like image 38
Luka Jacobowitz Avatar answered Sep 19 '22 13:09

Luka Jacobowitz