将F [A]转换为未来[A]

时间:2018-06-02 15:46:45

标签: scala scala-cats tagless-final

我有一个存储库:

trait CustomerRepo[F[_]] {

  def get(id: Identifiable[Customer]): F[Option[CustomerWithId]]
  def get(): F[List[CustomerWithId]]

}

我有一个使用Cats IO的数据库实现,所以我有一个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)

}

现在,我想使用一个不能处理任意持有者类型的库(它只支持Future)。所以我需要一个CustomerRepoPostgres[Future]

我想写一些可以将CustomerRepoPostgres[IO]转换为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()

}

我不喜欢这种方法需要为存储库中使用的每种类型使用隐式转换器。有更好的方法吗?

1 个答案:

答案 0 :(得分:4)

这正是无标记最终方法的用途,通过要求它遵循某些特定类型约束来抽象F。例如,让我们创建一个自定义实现,该实现需要FApplicative

trait CustomerRepo[F[_]] {
  def get(id: Identifiable[Customer]): F[Option[CustomerWithId]]
  def get(): F[List[CustomerWithId]]
}

class CustorRepoImpl[F[_]](implicit A: Applicative[F]) extends CustomerRepo[F] {
  def get(id: Identifiable[Customer]): F[Option[CustomerWithId]] {
    A.pure(???)
  }

  def get(): F[List[CustomerWithId]] = {
    A.pure(???)
  }
}

这样,无论F的具体类型如何,如果它具有Applicative[F]的实例,那么您将会很高兴,无需定义任何变换器。

我们这样做的方法是根据我们需要做的处理,将相关约束放在F上。如果我们需要顺序计算,我们可以使用Monad[F]然后使用flatMap结果。如果不需要顺序性,Applicative[F]可能足够强大。