onComplete handlers on Future and if statements

131 views
Skip to first unread message

Eric Fredericks

unread,
Mar 10, 2016, 5:53:39 PM3/10/16
to scala-user
Hi,

I am seeing some weird behavior with onComplete() not executing on a future when the future is the result of the execution of the 'if' case of an if/else statement, and the future is not assigned to a val.


  if (true) {
    Future.successful(1)
  } else {
    Future.successful(0)
  } onComplete {
    case Success(x: Int) => println(s"CASE 3: NoAssign, if-case, onComplete: success ${x}")
    case Failure(x: Throwable) => println(s"CASE 3: NoAssign,if-case, onComplete: failure: ${x.toString}")
  }

The onComplete() handler is never executing.  If I assign the result of the if/else statement to a val (of type Future[Int]), onComplete executes.  Or if I change the condition to 'false', the else case executes, and the onComplete handler executes.

See the attached program, which runs a test in a main program of four cases:

Case 1: With assignment, if case
Case 2: With assignment, else case
Case 3: Without assignment, if case // Does not execute onComplete()!
Case 4: Without assignment, else case

I tested against 2.11.8 (just downloaded now from scala-lang.org).  I originally saw this in 2.11.7.

I did a quick search but not sure if I'm missing something. We're a bit stumped.  Maybe others have seen this?  

Thanks,
Eric
WeirdFuture.scala

Viktor Klang

unread,
Mar 10, 2016, 6:10:39 PM3/10/16
to Eric Fredericks, scala-user

Hi Eric, if you add a 'case _ => println("ohnoes")' as a catch-all in the onComplete callback, is it called?

--
Cheers,

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Stephen Link

unread,
Mar 10, 2016, 7:04:05 PM3/10/16
to scala-user, eric.fr...@gmail.com
Hi Eric,

Your onComplete is being called on block that the second future is being created within, not the result of the if/else expression. Assigning to a val fixes this.

To fix inline you could could use the following

(if (false) {
  Future.successful(1)
} else {
  Future.successful(0)
}) onComplete {
  case Success(x: Int) => println(s"CASE 4: NoAssign, else-case, onComplete: success ${x}")
  case Failure(x: Throwable) => println(s"CASE 4: NoAssign,else-case, onComplete: failure: ${x.toString}")
}

Eric Fredericks

unread,
Mar 10, 2016, 9:42:18 PM3/10/16
to scala-user, eric.fr...@gmail.com
Hi Victor,

I added the catch-all case you suggested, but it is not called. Incidentally, we saw this behavior in production code (the types and bodies of if/else were different), and we had two cases: Success(_) and Failure(_), which I think should cover everything.

The fundamental question in my mind is: what is the type of

  if(...) { expr } else { expr }

... and if my call to onComplete() after the closing brace of the else compiles, then why is it applying only to the case where the 'else' executes?

Many people (all of whom are colleagues at work :) ) are pointing out that I can surround the if/else with braces or parenthesis and the onComplete() handler is then always called. OK, that's fine...but that does not satisfy my understanding of the way I think the semantics are supposed to work.  I am starting with the possibility that my understanding is wrong before asserting that the compiler is broken.

I'm a bit surprised that the compiler does not infer the if/else I have above as some type (in this case, Future[Int]) and then call onComplete on the object, of type Future[Int], regardless of which of the if or else cases executes. It would seem that the behavior I'm seeing is that the onComplete() handler is only registered on the future returned by the expression after the 'else.'

Am I misunderstanding the semantics?

Eric

On Thursday, March 10, 2016 at 5:10:39 PM UTC-6, √iktor Klang wrote:

Eric Fredericks

unread,
Mar 10, 2016, 10:02:12 PM3/10/16
to scala-user
Apologies, Viktor, for misspelling your name.
--
Eric Fredericks
Computer Scientist / Software Architect
41° 56' 2" N,  °87 41' 25" W

Viktor Klang

unread,
Mar 11, 2016, 12:39:01 AM3/11/16
to Stephen Link, Eric Fredericks, scala-user

Ouch, nice catch, Stephen!

--
Cheers,

Eric Fredericks

unread,
Mar 11, 2016, 2:45:04 AM3/11/16
to Viktor Klang, Stephen Link, scala-user
So that means I got the semantics wrong.  The code after the else, { ... } onComplete { ... } is treated as a single expression (of type Unit).

And furthermore, since the type of the expression after the 'if' (before the 'else') can be assigned to a variable of type Unit, the type of the whole expression ends up being Unit.  Which is slightly odd to me, since Unit is not a supertype of Future[Int], and yet I can still assign the former to a variable of type Unit. It seems to be boxing (or casting? ew) somehow.

As my CS professor in college often declared: "Subtle!"

Thanks all.

Viktor Klang

unread,
Mar 11, 2016, 4:37:58 AM3/11/16
to Eric Fredericks, scala-user
Hi Eric,

On Fri, Mar 11, 2016 at 3:42 AM, Eric Fredericks <eric.fr...@gmail.com> wrote:
Hi Victor,

I added the catch-all case you suggested, but it is not called. Incidentally, we saw this behavior in production code (the types and bodies of if/else were different), and we had two cases: Success(_) and Failure(_), which I think should cover everything.

The fundamental question in my mind is: what is the type of

  if(...) { expr } else { expr }

... and if my call to onComplete() after the closing brace of the else compiles, then why is it applying only to the case where the 'else' executes?

Many people (all of whom are colleagues at work :) ) are pointing out that I can surround the if/else with braces or parenthesis and the onComplete() handler is then always called. OK, that's fine...but that does not satisfy my understanding of the way I think the semantics are supposed to work.  I am starting with the possibility that my understanding is wrong before asserting that the compiler is broken.

I'm a bit surprised that the compiler does not infer the if/else I have above as some type (in this case, Future[Int]) and then call onComplete on the object, of type Future[Int], regardless of which of the if or else cases executes. It would seem that the behavior I'm seeing is that the onComplete() handler is only registered on the future returned by the expression after the 'else.'

I don't have time to check the Scala Language Specifcation right now, but I agree that from an ocular inspection of the code, it is not immediately evident what will happen.



--
Cheers,

som-snytt

unread,
Mar 11, 2016, 2:05:00 PM3/11/16
to scala-user, eric.fr...@gmail.com

We need Abide/linter rules for this case and similar cases with match, which would have to be syntax-aware. There are SO questions that could be avoided.

som-snytt

unread,
Mar 11, 2016, 2:08:25 PM3/11/16
to scala-user

That would be Herr Klangmeister to the rest of us. As of today, anyway.
Reply all
Reply to author
Forward
0 new messages