Link Multiple Either's - PullRequest
       53

Link Multiple Either's

2 голосов
/ 01 апреля 2020

Рассмотрим сценарий

trait Checker {
  def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]]
} 

Эта черта реализована различными классами.

Допустим,

    class CheckerImpl1 extends Checker {
          override def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]] = ???
        }
    class CheckerImpl2 extends Checker {
          override def check()(implicit ec: ExecutionContext): Future[Either[String, Unit]] = ???
        } 

Теперь мне нужно определить новую функцию, которая вызовет функцию check() для каждого из этих классов реализации в последовательности (последовательность не имеет значения) и возвратит новую либо, например, Future[Either[String, Unit]], где String здесь - объединенная строка left для результата реализации check().

Так что, если CheckerImpl1.check() возвращает Left("error1") и CheckerImpl2.check() возвращает Left("error2"), то результат новой функции вернет Left("error1&error2") (и просто разделить две строки).

ИЛИ

Так что если CheckerImpl1.check() возвращает Right(()) и CheckerImpl2.check() возвращает Left("error2"), то результат новой функции вернет Left("error2").

ИЛИ

Так что если CheckerImpl1.check() вернет Right(()) и CheckerImpl2.check() возвращает Right(()), тогда результат новой функции вернет Right(()).

То, что я сейчас сделал, это

(CheckerImpl1.check(), CheckerImpl2.check())
      .mapN {
        case (Right(_), Right(_))     => Right(())
        case (Left(err), Right(_))    => Left(err)
        case (Right(_), Left(err))    => Left(err)
        case (Left(err1), Left(err2)) => Left(err1 ++ "&" ++ err2)))
      }

Но это не идеальное решение, потому что, если я добавлю больше реализации, то мне нужно будет добавить больше этих состояний дела Мент.

Есть ли лучший способ сделать это?

Ответы [ 2 ]

3 голосов
/ 01 апреля 2020

Итак, у вас есть List из Future Either с.

val lfe :List[Future[Either[String,Unit]]] = ???

Чтобы собрать все строки Left в одну Future[String], вы можете сделать это ...

val res :Future[String] =
  Future.sequence(lfe)
        .map(_.flatMap(_.fold(Some(_),_ => None)).mkString(" & "))
2 голосов
/ 01 апреля 2020

Если я правильно понимаю, вы в конечном итоге захотите получить тип Future[Either[String, Unit]]. Почему бы не просто .sequence фьючерсы и .fold результаты?

val checkers: List[Checker] = ???

Future.sequence(checkers.map(_.check()))
  .map { results => results.foldLeft(Right(()): Either[String, Unit]) {
    case (Left(acc), Left(err)) => Left(s"$acc&$err")
    case (Right(_), Left(err)) => Left(err)
    case (acc, Right(_)) => acc
  }}

Единственное изменение кода, которое вам нужно сейчас, - это добавить checkers список.


В некотором смысле подробнее элегантно, используя cats (если вы не знакомы с добрым плагином для проектора - вот откуда взялся *).

import cats.implicilts._

checkers.map(_.check()).sequence
  .map { results =>
    results.map(_.toValidatedNec)
      .sequence[ValidatedNec[String, *], Unit]
      .leftMap(_.toList.mkString("&"))
      .map(_ => ())
      .toEither
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...