Преобразование F [A] в будущее [A] - PullRequest
0 голосов
/ 02 июня 2018

У меня есть хранилище:

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 голосов
/ 02 июня 2018

Это именно то, для чего нужен конечный подход без тегов, чтобы абстрагироваться от 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] может быть достаточно сильным для этого.

...