Scala for understanding the future and options

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 } } 

I have two functions returning Future[Option[String]] , and I need to combine two strings into one string.

I want the output to be either a combination of two lines or a footer: “file one file two adds more data to the file” or by default when one or both of Future return None : “Files no more data found in file”.

How can this be achieved?

Compiler Error:

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

Well, without doing anything like monad transformers or anything, you can just put in an understanding. This will be more verbose, but no additional dependencies.

 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)) 

Ultimately, the problem is that you are trying to combine Future and Option in the same for sense. It just doesn't work for the reasons that other respondents talked about. However, the attachment works great.

The disadvantage of nesting is that you get very complex data structures, which can be difficult to use elsewhere in your program. You should think about how you smooth them out, i.e. From Future[Option[String]] to all Future[String] . In your specific case, you can do something like this: res.map(_.getOrElse("")) .

Ok, maybe 2 levels of nesting are great, but you nested the most, think about smoothing this hierarchy before letting your colleagues handle it. :)

+7
source

Like alf mentioned in his answer, you can use monad transceivers for this, in this case OptionT .

Example using 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") 

Note that if you use cats, you can replace them for understanding in combinedOT2 with the help of the Cartesian builder ( |@| ), because file2 does not depend on file1 :

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

You can use fallbackTo if the "combined" Future failure fails, but it is probably best to use recover or recoverWith to check which Throwable you want to recover.

+4
source

I think this particular problem is described in this 47deg blog post and also in this one : monads do not compose, so you need a transformer from one monad to another, since there is no flatMap operation that (flat) displays a Future in Option .

+1
source

All Articles