Изящная альтернатива для карты будущего [Вариант [...]] - PullRequest
4 голосов
/ 29 октября 2019

У меня есть 2 метода, которые возвращают Future[Option[String]], и мне нужно вызвать второй метод, если первый метод возвращает Future[None]. Ниже приведен код, который у меня есть:

def method1(): Future[Option[String]] = Future(None)
def method2(): Future[Option[String]] = Future(Some("test"))

val resultFuture = method1().flatMap(option =>
if (option.isDefined) { // Alternatives?
  Future(option)
} else {
  method2()
})

val durationSeconds = {
  import scala.concurrent.duration._
  import scala.language.postfixOps
  20 seconds
}
val result = Await.result(resultFuture, durationSeconds)
println(result) // Prints Some(test) as expected

Я думаю, что это решение недостаточно, потому что

  1. Отображается значение успеха (часть if), хотя в действительности нетпреобразование значения
  2. Ненужное создание второго Future (в блоке if), чтобы компилятор был доволен.

Я пытался

  1. method1().flatMap(option => option.fold(method2)(value => Future(Option(value)))). Но это имеет ту же проблему.
  2. option.orElse(...), но он должен возвращать Option[B] не Future[Option[B]].

Оценить любые указатели наотображение Future[None] без явного отображения / передачи Future[Some[String]].

Ответы [ 2 ]

3 голосов
/ 29 октября 2019

Future.apply создает новое будущее и планирует его запуск в контексте выполнения, что действительно дорого, так что вы правы, это не лучшее решение.

Вместо Future.apply вы можете простоиспользуйте Future.sucessful, который создает уже разрешенную Future и является очень дешевой операцией. Вы также можете сделать свой код (возможно) более читабельным с помощью сопоставления с образцом и для понимания:

for{
    m1 <- method1()
    result <- m1 match {
      case x @ Some(_) => Future.successful(x) //just lifts value into Future context
      case None => method2()
   }
} yield result

Вы также можете использовать плоскую карту с сопоставлением с образцом:

method1().flatMap{
   case x @ Some(_) => Future.successful(x)
   case None => method2()
}

Другое решениевы могли бы рассмотреть использование монадного преобразователя OptionT, который упрощает операции со вложенным стеком монад, например Future[Option[T]]. И scalaz , и cats предоставляют свои собственные реализации OptionT. Ниже приведен пример с cats:

import cats.data.OptionT
import cats.implicits._

OptionT(method1()).orElseF(method2()).value
2 голосов
/ 29 октября 2019

Вот одна альтернатива.

val resultFuture :Future[Option[String]] = method1().flatMap(
  _.fold(method2())(str => Future.successful(Some(str))))

Она по-прежнему переупаковывает заполненный Option, но затрачивается меньше как Future.successful().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...