Если у меня есть функция проверки:
def validateOne(a: A): Try[A]
И теперь я хочу проверить коллекцию A с помощью функции validateOne
def validateAll(all: List[A]): Try[List[A]]
Есть ли хороший способ вернуть Failure
, как только первый элемент окажется недействительным?
Я делаю это сейчас, вызывая get
после проверки каждого элемента. Для первого элемента, где validateOne
возвращает Failure
, get
выдает обернутое исключение... которое я перехватываю повторно:
def validateAll(all: List[A]): Try[List[A]] = try {
all.map(a => validateOne(a).get)
} catch {
case e: MyValidationException => Failure(e)
}
Вы можете легко преобразовать List[Future[A]]
в Future[List[A]]
с помощью Future.sequence. К сожалению, стандартная библиотека не предоставляет аналогичный метод для Try
.
Но вы можете создать свою собственную хвостовую рекурсию, не выполняющую быструю функцию для перебора результатов:
def validateAll[A](all: List[A]): Try[List[A]] = {
//additional param acc(accumulator) is to allow function to be tail-recursive
@tailrec
def go(all: List[A], acc: List[A]): Try[List[A]] =
all match {
case x :: xs =>
validateOne(x) match {
case Success(a) => go(xs, a :: acc)
case Failure(t) => Failure(t)
}
case Nil => Success(acc)
}
go(all, Nil)
}
Если вы используете котов в своем стеке, вы также можете использовать обход (переход — это карта + последовательность):
import cats.implicits._
def validateAll2[A](all: List[A]): Try[List[A]] = all.traverse(validateOne)
Try(all.map(a => validateOne(a).get))
Тоже получится.