Closed iximeow closed 9 years ago
@a-wortman Thanks for submitting! I'm rushing around this week so it might take a few days before I can have a look at this, but I'll try to get round to this as soon as I can.
Thanks for your patience!
Hi Andy
What do you think about the following formulation, which stays pretty close to your version, but makes the Exception vs. Error choice a bit more obvious:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
val f = Future({ throw new Error("bang!") }) recoverWith {
case ex: Exception => Future.successful("Not so bad: " + ex.getMessage)
case err: Error => Future.failed(err)
}
f onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
with possible candidate answers:
Or the following one, which is more compact but obscures the exception vs. error distinction:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
Future({ throw new Error("bang!") }) onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
with candidate answers potentially being:
Yay: java.lang.Error: bang!
Oops: bang!
Oops: Boxed Error
I like your first suggestion quite a bit! I realized when I had to drop in Thread.sleep(100)
that there was some way to make the sample a little more elegant, glad to see it's there.
I prefer the first version to the second because the crux of the puzzle is that the Error
is magically boxed into an Exception
. The second version has that, of course, but seems a stretch to reach back to Boxed Error
. The first version is a more "honest" puzzle in a way, I think.
The second version has that, of course, but seems a stretch to reach back to Boxed Error.
...which may make it a bit more puzzling, depending on how you look at it ;-) But I agree that, in the second version, the only thing that's really puzzling is the message, not the execution path that the code takes.
Actually, looking at the first version again, the "throws an exception" candidate answer doesn't seem very plausible since we're explicitly catching Error
. That would be more plausible as an option in the second case, actually: "Hm, I wonder if onComplete
actually handles Error
? Perhaps it just throws it?"
Bearing the above in mind, perhaps a hybrid would work best?
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
val f = Future({ throw new Error("bang!") }) recoverWith {
case ex: Exception => Future.successful("Not so bad: " + ex.getMessage)
}
f onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
with candidate answers:
Failure(msg)
Leaving case err: Error => ...
out does make "throws an exception" a more plausible answer, yeah. That last revision is also very similar to the initial submission, but with onComplete
instead of a sleep and attempt to mach f.value
, so of course I like it ;)
One of the parens or curlies on the fifth line isn't necessary though, just to be a stickler I prefer
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
val f = Future { throw new Error("bang!") } recoverWith {
case ex: Exception => Future.successful("Not so bad: " + ex.getMessage)
}
f onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
(curlies for the Future
rather than parens)
There's also the option to have a more generic recoverWith
:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
val f = Future { throw new Error("bang!") } recoverWith {
case t: Throwable => Future.successful("Not so bad: " + t.getMessage)
}
f onComplete {
case Success(res) => println("Yay: " + res)
case Failure(e) => println("Oops: " + e.getMessage)
}
with
as answers. This makes the first answer far more plausible, but does obscure the execution path point.
@a-wortman: I'll close this one for now - let's continue the discussion over at #129...?
Future
does interesting things when failed with something other than an Exception! Tested this on 2.11.4 and 2.11.7, probably still happens on master - seems like intentional decisions inscala.concurrent.impl.Promise
.