Is there the concept of a Future
that cannot fail in Scala?
I'm transforming a Future[Result]
, which may fail—therefore I handle both a Failure
and a Success
—into a Future[Option[String]]
, carrying an optional error message derived from the failure or success states. So far, so good.
Thing is now, I would like to formally (i.e., with the help of the type system) remember that this future will always hold a Success
and that I won't need to handle the failure case in the future.
Is there a smart way to do this?
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.
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.
NOTE: With Future. onComplete() we are no longer blocking for the result from the Future but instead we will receive a callback for either a Success or a Failure.
You cannot do that "with the help of type system" because there is no way for the type system to guarantee a Future
will not fail, even if you promise that it won't.
Consider this:
Future { doStuff(); }
.recover { case _ => "Failed!" } // Now it always succeeds
.map { _ => Seq.empty[String].head } // Now it does not.
Even if you were going to make any further transformations impossible, once the Future
has been declared to always succeed, that still does not help, because the exception handler (or whatever you do to convert the original future to the "always succeeding one") could throw.
Update: as pointed out in a comment below, the code snippet above is incorrect: the result of .map
is not the same Future
as the result of .recover
. The point stands however. Here is the correct illustration:
Future { doStuff }
.recover { case _ => Seq.empty[String].head }
Isn't this what type-tagging is for?
scala> type Tagged[U] = { type Tag = U }
defined type alias Tagged
scala> type @@[T, U] = T with Tagged[U]
defined type alias $at$at
scala> trait OK ; trait Uncertain
defined trait OK
defined trait Uncertain
scala> type Sure[A] = Future[A] @@ OK
defined type alias Sure
scala> type Unsure[A] = Future[A] @@ Uncertain
defined type alias Unsure
scala> val f = Future.successful(42).asInstanceOf[Sure[Int]]
f: Sure[Int] = Future(Success(42))
then
scala> object X { def p(f: Sure[_]) = "sure" ; def p(f: Unsure[_])(implicit d: DummyImplicit) = "unsure" }
defined object X
scala> X.p(f)
res1: String = sure
It doesn't remain sure under map, of course.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With