Вложенный фильтр в Scala Либо поверх будущего[Попробуйте[Int]]

#scala #async-await #future #either

Вопрос:

Я новичок в scala , я пытаюсь реализовать логику фильтрации поверх a Either . Теперь у меня есть функция getTaskId , которая возвращает Future[Try[Int]] , и моя filter logic функция основана на этом Int . Теперь , поскольку filter ожидает boolean , я не могу вернуть то же самое в приведенном ниже фрагменте кода .

 val records: List[CommittableRecord[Either[Throwable, MyEvent]]] = ???
records.filter { 
  (x: CommittableRecord[Either[Throwable,MyEvent]]) => 
    x.value match {
      case Right(event: MyEvent) =>
        getTaskId(event.get("task").get) filter {
            case Success(value)     => value > 1
            case Failure(exception) => false
          }
      case Left(_) => false
    }
}
 

Я получаю ошибку для filter функции on getTaskId , которая возвращает Future[Try[Int]]

 type mismatch;
 found   : scala.concurrent.Future[scala.util.Try[Int]]
 required: Boolean
 

Так что в основном filter поверх Future возвращения другого Future , но родитель filter ожидает boolean

Мы будем очень признательны за любую помощь.

Комментарии:

1. Вопрос недостаточно ясен, чтобы ответить на него. Вы фильтруете List[Either] информацию ?

2. Ваша функция , вероятно, должна возвращать a Future[Either[A, B]] вместо Either[A, B] , потому что ваша функция асинхронна.

3.@TausifSayyad да, я filtering List[Either]

4. @curiousguy что вы хотите получить в конце своей фильтрации? Какого типа?

5. @BorisAzanov это должно быть результатом моего фильтра CommittableRecord[Either[Throwable,MyEvent]]

Ответ №1:

Здесь вы столкнулись с двумя сложными функциями в scala:

  1. Много синтаксического сахара
  2. Лучшая практика в scala-не ждать Future результата внутри вас какой-то бизнес-логики с использованием: Await.result(future, timeout) . Вы должны использовать его только в конце вселенной (в большинстве случаев: в конце вашей программы).

Итак, я бы посоветовал переработать вашу текущую логику, отфильтровав List[CommittableRecord] результат, чтобы сделать результат неблокирующим- Future[List[CommittableRecord]] с отфильтрованным списком записей. Вы можете работать с этим будущим, как будто это просто еще один контейнер данных (например Option[T] ), и в конце операции блокировки вызовов вашей программы, например Await.result .

Пример кода:

 import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future.sequence
import scala.util.{Failure, Success, Try}

case class Task()

type MyEvent = Map[String, Task]
case class CommittableRecord(value: Either[Throwable, MyEvent])

def getTaskId(task: Task): Future[Try[Int]] = ???

val records: List[CommittableRecord] = List.empty[CommittableRecord]
val result: Future[List[CommittableRecord]] = sequence(
  records.map(
    x =>
      (x.value match {
        case Left(_) => Future(false)
        case Right(value) =>
          getTaskId(value.get("task").get)
            .map {
              case Failure(_) => false
              case Success(id) => id > 1
            }
      }).map(_ -> x)
  )
).map(
  idMoreThen1AndRecordList =>
    idMoreThen1AndRecordList.collect {
      case (true, record) => record
    }
)
 

или, после некоторого рефакторинга и замены лямбда-выражений на функции:

 def isTaskIdMoreThenOneAndRecord(record: CommittableRecord): Future[(Boolean, CommittableRecord)] =
  (record.value match {
    case Left(_) => Future(false)
    case Right(value) =>
      getTaskId(value.get("task").get)
        .map(tryId => tryId.fold(_ => false, _ > 1))
  }).map(_ -> record)

def filterRecordsWithTaskIdMoreThenOne(
    isMoreOneAndRecordList: List[(Boolean, CommittableRecord)]
): List[CommittableRecord] =
  isMoreOneAndRecordList.collect {
    case (true, record) => record
  }

val result: Future[List[CommittableRecord]] =
  sequence(records.map(isTaskIdMoreThenOneAndRecord))
    .map(filterRecordsWithTaskIdMoreThenOne)
 

Таким образом, в результате у вас будет Future[List[CommittableRecord]] и вы сможете работать с отфильтрованными записями с помощью map функции:

 result.map((filteredRecords: List[CommittableRecord]) => *do something with filtered records*)
 

или вы можете создать две неблокирующие операции (например, свой список и другую неблокирующую функцию) с помощью flatMap .

Полезные ссылки: