将 F[A] 转换为 Future[A]
Transform F[A] to Future[A]
我有一个存储库:
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()
}
我不喜欢这种方法需要为存储库中使用的每种类型提供隐式转换器。有更好的方法吗?
这正是无标记最终方法的目的,通过要求它遵循一些特定的类型约束来抽象 F
。例如,让我们创建一个自定义实现,它要求 F
为 Applicative
:
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]
可能就足够了。
我有一个存储库:
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()
}
我不喜欢这种方法需要为存储库中使用的每种类型提供隐式转换器。有更好的方法吗?
这正是无标记最终方法的目的,通过要求它遵循一些特定的类型约束来抽象 F
。例如,让我们创建一个自定义实现,它要求 F
为 Applicative
:
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]
可能就足够了。