2016-05-26 5 views
5
object Main extends App { 
    val p1 = Promise[Option[String]]() 
    val p2 = Promise[Option[String]]() 
    val f1 = p1.future 
    val f2 = p2.future 

    val res = (for{ 
    file1Opt <- f1 
    file2Opt <- f2 
    file1 <- file1Opt 
    file2 <- file2Opt 
    } yield { 
    combineFiles(file1, file2) 
    }).fallbackTo(Future.successful("Files not found")) 

    Thread.sleep(2000) 
    println("XXXXXXXXXXXXXXXXXXX") 

    p1.success(Some("file one")) 
    p2.success(Some("file two")) 

    val finalData = res.map(s => 
    s + " " + "add more data to the file" 
) 

    finalData.map(println(_)) 

    def combineFiles(f1: String, f2: String): String = { 
    f1 + " " + f2 
    } 
} 

У меня есть две функции, которые возвращают Future[Option[String]], и мне нужно объединить две строки в одну строку.Scala для понимания с будущим и опциями

Я хочу, чтобы результат был либо комбинацией двух строк, либо нижнего колонтитула: «файл один файл два добавить больше данных в файл» или по умолчанию, когда один или оба из Future s возвращают None: «Файлы не найдены, добавьте больше данных для файла ".

Как это можно достичь?

Compiler ошибка:

Error:(16, 11) type mismatch; 
found : Option[String] 
required: scala.concurrent.Future[?] 
file1 <- file1Opt 
    ^

ответ

6

Ну, не делая ничего необычного, как трансформаторы монады или прочее, можно просто вложить for соображений. Это будет более многословным, но никаких дополнительных зависимостей.

val res = (for{ 
    file1Opt <- f1 
    file2Opt <- f2 
} yield for { 
    file1 <- file1Opt 
    file2 <- file2Opt 
} yield combineFiles(file1, file2)) 
.fallbackTo(Future.successful(Some("Files not found"))) 
//or, alternatively, .fallbackTo(Future.successful(None)) 

В конечном счете, проблема здесь заключается в том, что вы пытаетесь сочетать Future и Option в одном for понимании. Это просто не работает по причинам, о которых говорили другие респонденты. Однако вложение прекрасно работает.

Недостатком гнездования является то, что вы получаете очень сложные структуры данных, которые могут быть нелегкими в использовании в вашей программе. Вы должны подумать о том, как вы их сглаживаете, то есть от Future[Option[String]] до Future[String]. Ваш конкретный случай вы можете сделать примерно так: res.map(_.getOrElse("")).

Хорошо, возможно, 2 уровня гнездования прекрасны, но вы больше всего гнездились, подумайте о сглаживании этой иерархии, прежде чем позволить своим коллегам справиться с этим. :)

1

Я думаю, что именно эта проблема рассматривается в this 47deg blog post, а также в this one: монады не сочиняю, так что вам нужен трансформатор с одной монады на другую, так как нет flatMap операция, которая (квартира) отобразит Future в Option.

4

alf, упомянутый в его ответе, вы можете использовать трансформаторы монады для этого, в данном случае OptionT.

Пример использование cats:

import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 
import cats.data.OptionT 
import cats.implicits._ 

val file1: Future[Option[String]] = Future.successful(Some("file1")) 
val file2: Future[Option[String]] = Future.successful(Some("file2")) 

val combinedOT: OptionT[Future, String] = 
    for { 
    f1 <- OptionT(file1) 
    f2 <- OptionT(file2) 
    } yield s"$f1 $f2" 

val combinedFO: Future[Option[String]] = combinedOT.value 
val combinedF: Future[String] = combinedOT.getOrElse("Files not found") 

Обратите внимание, что если вы используете кошка, вы можете заменить на понимание в combinedOT2 с использованием декартова строителя (|@|), потому что file2 не зависит от file1:

val combinedOT2: Future[Option[String]] = 
    (OptionT(file1) |@| OptionT(file2)).map(_ + " " + _).value 

Вы все еще можете использовать fallbackTo если «комбинированный» Future не удается, Eventhough это, вероятно, лучше использовать recover или recoverWith, чтобы проверить, какие из Throwable вы хотите восстановить.