Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional Future in Scala

Tags:

scala

Given these two futures, I need to run the first one only if a condition is true (see if y>2). But I get an exception Future.filter predicate is not satisfied. What does this mean and how to fix the example?

object TestFutures extends App {

  val f1 = Future {
    1
  }

  val f2 = Future {
    2
  }

  val y = 1

  val x = for {
    x1 <- f1 if y>2
    x2 <- f2
  }  yield x1 + x2


  Thread.sleep(5000)
  println(x)
}
like image 875
ps0604 Avatar asked Oct 24 '25 14:10

ps0604


2 Answers

The question contains some terminology issues.

Given the requirement, "I need to run the first (future) one only if a condition is true", then one possible implementation of the requirement would be:

val f1 = if (cond) Some(Future(op)) else None

This is because a Future will start its execution once it's defined.

Going back to the expression in the question:

val x = for {
  x1 <- f1 if y>2
  x2 <- f2
}  yield x1 + x2

This is saying "I want the result of f1 if(cond)" and not "I want to execute f1 if(cond)".

This would be a way: (note how the futures are defined within the for-comprehension, and the condition is outside):

val x = if (y > 2) {
  for {
      x1 <- Future(op1)
      x2 <- Future(op2)
  }  yield x1 + x2
} else ???

The proper use of guards in for-comprehensions is to evaluate an expression against values coming from the computation expressed by the for-comprehension. For example:

"I want to execute f2 only if the result of f1 is greater than y"

val x = for {
  x1 <- f1
  x2 <- Future(op) if x1 > y
}  yield x1 + x2

Note how the condition here involves an intermediate result of the computation (x1 in this case)


One side note: to wait for the result of a future, use Await.result(fut, duration) instead of Thread.sleep(duration)

like image 174
maasg Avatar answered Oct 27 '25 12:10

maasg


filter is not really something you should be able to do on a Future - what would a Future that didn't pass the condition return? From your example: we still need to have a value for x1 (even if it fails the if) to use in the yield x1 + x2.

Therefore, the filter method on Future is designed to fail hard when the predicate evaluates to false. It is an "assert" of sorts. You probably would prefer something like this (that provides a default value for x1 if the condition fails):

val x = for {
  x1 <- if (y > 2) f1 else Future.successful(/* some-default-value-for-x1 */)
  x2 <- f2
}  yield x1 + x2
like image 21
Alec Avatar answered Oct 27 '25 12:10

Alec