I have a repository:
trait CustomerRepo[F[_]] {
def get(id: Identifiable[Customer]): F[Option[CustomerWithId]]
def get(): F[List[CustomerWithId]]
}
I have an implementation for my database which uses Cats IO, so I have a CustomerRepoPostgres[IO].
class CustomerRepoPostgres(xa: Transactor[IO]) extends CustomerRepo[IO] {
import doobie.implicits._
val makeId = IO {
Identifiable[Customer](UUID.randomUUID())
}
override def get(id: Identifiable[Customer]): IO[Option[CustomerWithId]] =
sql"select id, name, active from customer where id = $id"
.query[CustomerWithId].option.transact(xa)
override def get(): IO[List[CustomerWithId]] =
sql"select id, name, active from customer"
.query[CustomerWithId].to[List].transact(xa)
}
Now, I want to use a library which cannot deal with arbitrary holder types (it only supports Future). So I need a CustomerRepoPostgres[Future].
I thought to write some bridge code which can convert my CustomerRepoPostgres[IO] to CustomerRepoPostgres[Future]:
class RepoBridge[F[_]](repo: CustomerRepo[F])
(implicit convertList: F[List[CustomerWithId]] => Future[List[CustomerWithId]],
convertOption: F[Option[CustomerWithId]] => Future[Option[CustomerWithId]]) {
def get(id: Identifiable[Customer]): Future[Option[CustomerWithId]] = repo.get(id)
def get(): Future[List[CustomerWithId]] = repo.get()
}
I don't like that this approach requires implicit converters for every type used in the repository. Is there a better way to do this?
This is exactly what the tagless final approach is for, to abstract over
Fby requiring it to follow some specific type constraints. For example, let's create a custom implementation which requiresFto be anApplicative:This way, no matter the concrete type of
F, if it has an instance ofApplicative[F]then you'll be good to go, with no need to define any transformers.The way we do this is just put the relevant constraints on
Faccording to the processing we need to do. If we need a sequential computation, we can use aMonad[F]and thenflatMapthe results. If no sequentiality is needed,Applicative[F]might be strong enough for this.