2016-10-12 5 views
0

Распространенной ситуация, которую я нашел myselfe с использованием Scala является следующим потоком:Закономерность в типизированной функциональном программировании Опции [Future [Int]] преобразована в будущее [Опция [Int]]

  1. Прочитайте некоторый реквизит которым возвращает Option
  2. Использование реквизита в Future
  3. сделать некоторые преобразования по результатам в будущем, которое возвращает Option

Это дает типа, такие как Option[Future[Option[_]]] лучшим решением будет использовать такое преобразование, как Future[Option[Option[_]]], например, используя что-то вроде ниже:

def transform[A](o: Option[Future[A]]): Future[Option[A]] = 
    o.map(f => f.map(Option(_))).getOrElse(Future.successful(None)) 

(код украденного из here)

Тогда я мог бы работать с произвольным числом Options внутри Future с использованием flatmap.

Это похоже на такой общий шаблон, что я уверен, что он доступен в Scala каким-то идеоматическим способом, без необходимости повторять метод преобразования снова и снова.

Так что мой вопрос: какой самый идеоматичный способ превратить Option[Future[Option[_]]] снаружи, как в приведенном выше примере?

+1

Монографический трансформатор может быть тем, что вы ищете. – mfirry

+0

Вопрос говорит, что вы хотите «Option [Future [A]] => Future [Option [A]]', но детали, похоже, говорят, что вы хотите «Option [Option [... Option [A]]] => Option [ A] '? Итак, что это? Можете ли вы привести пример? – Cubic

ответ

0
val a: Option[Future[Option[T]]] = ... 

val b: Future[Option[T]] = a.getOrElse(Future(None)) 
+1

Глядя на «Будущее» [docs] (http://www.scala-lang.org/api/current/#scala.concurrent.Future$), я бы выбрал 'Future.successful' над' Future.apply' , т.е. 'Future (...)'. Первая заметка: «Создает уже завершенное Будущее с указанным результатом», в то время как последние говорят: «Начинает асинхронное вычисление и возвращает объект Future с результатом этого вычисления». Примечание. Я не уверен в фактической разнице; просто указывая на документы. –

1

Traverse В этом разделе может быть полезная модельная табличка в библиотеке кошек. Он может позаботиться о шаблоне преобразования Option[Future[Something]] в Future[Option[Something]].

пример использования аммонит REPL:

$ amm 
Loading... 
Welcome to the Ammonite Repl 0.7.7 
(Scala 2.11.8 Java 1.8.0_101) 
@ import $ivy.`org.typelevel::cats-core:0.7.2` 
import $ivy.$ 

Первые несколько импорт ...

@ import cats.Traverse 
import cats.Traverse 
@ import cats.implicits._ 
import cats.implicits._ 
@ import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.ExecutionContext.Implicits.global 
@ import scala.concurrent.Future 
import scala.concurrent.Future 

Вот что мы хотим преобразовать:

@ val optionOfFuture: Option[Future[String]] = Some(Future.successful("hello")) 
optionOfFuture: Option[Future[String]] = Some([email protected]) 

Мы используем Traverse ' s sequence метод для переключения Option и Future:

@ val futureOfOption: Future[Option[String]] = Traverse[Option].sequence(optionOfFuture) 
futureOfOption: Future[Option[String]] = Success(Some(hello)) 

Или, если вы предпочитаете синтаксис-засахаренные версия:

@ import cats.syntax.traverse._ 
import cats.syntax.traverse._ 
@ val futureOfOption2: Future[Option[String]] = optionOfFuture.sequence 
futureOfOption2: Future[Option[String]] = Success(Some(hello)) 

Для получения дополнительной информации о том, что еще Traverse можно сделать, посмотрите на cats documentation.

1

Я думаю, ваша проблема с №3 в вашем вопросе: почему «некоторые преобразования на результат», которые управляют Future, возвращают Option? Это плохо пахнет. Просто заставьте их вернуть Future в первую очередь, и у вас не будет этой проблемы.

И, между прочим, я не уверен, что именно вы подразумеваете под «возможностью иметь дело с произвольным числом опций с использованием flatMap», но это почти наверняка неправильно: flatMap поможет вам избавиться от дополнительный уровень опций на момент составления карты: Some(Some("foo")).flatMap(x => Some(s)) - Some(Some("foo")). .flatten делает то же самое: Some(Some(Some("foo"))).flatten дает Some(Some("foo")), а не Some("foo") или "foo" как и ожидалось.

Обратите внимание, что в каждом случае существует только один уровень параметров, с которым выполняется сплющивание, а не «произвольное число». Обычный подход заключается в том, чтобы избавиться от дополнительных опций, когда вы их встретите (спрячьте сразу, чтобы всегда иметь Future[Option[T]], а не Future[Option[Option[T]]]).

Смежные вопросы