Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala for comprehension with 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 that return Future[Option[String]] and I need to combine the two strings into one string.

I want the output to be either combination of two strings and footer: "file one file two add more data to the file" or default when one or both of the Futures return None: "Files not found add more data to file".

How can this be achieved?

Compiler error:

Error:(16, 11) type mismatch;
found   : Option[String]
required: scala.concurrent.Future[?]
file1 <- file1Opt
      ^ 
like image 774
An Illusion Avatar asked May 26 '16 22:05

An Illusion


People also ask

What is Future option in Scala?

The contract on Future#filter is thus: If the current future contains a value which satisfies the predicate, the new future will also hold that value. Otherwise, the resulting future will fail with a NoSuchElementException. But wait: scala> None.get java.util. NoSuchElementException: None.get.

What is for comprehension in Scala?

Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.

How do you use Future in Scala?

Future. Future represents a result of an asynchronous computation that may or may not be available yet. When we create a new Future, Scala spawns a new thread and executes its code. Once the execution is finished, the result of the computation (value or exception) will be assigned to the Future.

What is Future and promise in Scala?

The Promise is a writable, single-assignment container that completes a Future. The Promise is similar to the Future. However, the Future is about the read-side of an asynchronous operation, while the Promise is about the write-side.


1 Answers

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

An 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 the for comprehension in combinedOT2 by using a cartesian builder (the |@|), because file2 doesn't depend on file1 :

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

You can still use fallbackTo if the "combined" Future fails, eventhough it is probably better to use recover or recoverWith to actually check which Throwables you want to recover from.

like image 120
Peter Neyens Avatar answered Sep 21 '22 12:09

Peter Neyens