Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any reason to make API of type Future[Try[A]] instead of Future[A]?

Tags:

monads

scala

Taking in account that any pattern matching on Future result matches Success[A] and Failure[A] (onComplete(), andThen()) (because they expect Try[A] as an argument, directly or through partial function), could there be a case when I would want to say explicitly that a function is of type Future[Try[A]]?

like image 257
eugen-fried Avatar asked Dec 12 '22 01:12

eugen-fried


1 Answers

There are various constructs in Scala which carry failure case in them. There are Option, Either, Try and Future. (Futures main point is to abstract asynchronous operations, an error handling is there for convinience). Scalaz have even more: Validation (Disjunction and Maybe are better Either and Option).

They all have a bit different treatment of erroneous values. Yet Try and Future have very similar, both wrap Throwable. So IMHO, Future[Try[A]] doesn't add much information (about the error). Compare to having Future[Future[A]] or Try[Try[A]]. OTOH Future[Option[A]] or Future[Either[MyError, A]] make sense to me.


There might be sitatuon where you have for example potentially failing f: A => B and g: B => C and you'd like to avoid creating too much tasks in the ExecutionContext:

val a: Future[A] = ???
val c: Future[C] = a.map(f).map(g) // two tasks, not good
val c2: Future[Try[C]] = a.map(x => Try { f(x) } map g ) // one task, maybe better

// get will throw, but exception will be catched by Future's map
// Almost no information is lost compared to `c2`
// You cannot anymore distinguish between possible error in `a`
// and exceptions thrown by `f` or `g`.
val c3: Future[C] = a.map(x => Try { f (x) }.map(g).get)

In this case, I'd rather refactor f and g to have better types, at least: f: A => Option[B] and g: B => Option[C] then, ending up with Future[Option[C]].

like image 176
phadej Avatar answered Apr 13 '23 00:04

phadej