Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine Futures of different types into a single Future without using zip()

I want to create a Future of type Future[(Class1,Class2,Class3)] from below code. However the only way I have found to do this is by using zip(). I find the solution ugly and properly not optimal. Can anybody enlightened me.

val v = for (     a <- {         val f0:Future[Class1] = process1         val f1:Future[Class2] = process2         val f2:Future[Class3] = process3         f0.zip(f1).zip(f2).map(x => (x._1._1,x._1._2,x._2))     } yield a  // Future[(Class1,Class2,Class3)] 

I have also tried to use Future.sequence(List(f0, f1, f2)) but this will not work as the new Future will have type of Future[List[U]] where U is the lub of Class1/2/3 whereas I want a 3-tuple preserving the original types

like image 465
Peter Lerche Avatar asked Jun 22 '12 13:06

Peter Lerche


People also ask

How do you complete a Future in Scala?

The most general form of registering a callback is by using the onComplete method, which takes a callback function of type Try[T] => U . The callback is applied to the value of type Success[T] if the future completes successfully, or to a value of type Failure[T] otherwise.

Is Future blocking onComplete?

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.

What is Future sequence?

sequence takes a list of futures and transforms it into a single future of list in an asynchronous manner. For instance, assume that you have a list of independent jobs to be run simultaneously. In such a case, the list of futures can be composed into a single future of list using Future. sequence.

How does Scala Future work?

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.


2 Answers

val result: Future[(Class1, Class2, Class3)] = for {   _ <- Future.unit   val f1 = process1   val f2 = process2   val f3 = process3   v1 <- f1   v2 <- f2   v3 <- f3 } yield (v1, v2, v3) 
like image 183
Viktor Klang Avatar answered Oct 11 '22 16:10

Viktor Klang


Applicative Functors

What you are asking for is an applicative functor for a future. See scalaz Applicative Builder pattern. It should be rather trivial to roll your own on the back of zip

(f0 |@| f1 |@| f2)(g) //g is function (Class1, Class2, Class3) => Z 

This is equivalent to the direct applicative:

(f0 <***> (f1, f2))(g) 

Scalaz ships with a banana braces method which forms a tuple from the target and the arguments (i.e. what you asked for). So your solution will be:

f0 <|**|> (f1, f2) //that. is. all. 

You get all this simply by defining a typeclass instance for the following typeclass:

trait Apply[Z[_]] {   def apply[A, B](f: Z[A => B], a: Z[A]): Z[B] } 

So for future this looks like:

implicit val FutureApply = new Apply[Future] {   def apply[A, B](f: Future[A => B], a: Future[A]): Future[B] =      (f zip a) map { case (fn, a1) => fn(a1) }   } } 

(Actually you'd need Pure and Functor as well. Might as well implement Bind whilst you're at it - see appendix)

The great thing about this pattern is that you will start to see it everywhere (e.g. in Option, in Validation, in List etc). For example, the cartesian product of 2 streams is:

s1 <|*|> s2 

Notes

All the above assuming scalaz 6, doubtless scalaz 7 for 2.10 will ship with these typeclasses by default. Pure has been renamed Pointed in scalaz7.


Appendix

Other type class instances for future:

implicit val FuturePure = new Pure[Future] {   def pure[A](a: =>A): Future[A] = Future { a } } implicit val FutureBind = new Bind[Future] {   def bind[A, B](a: Future[A], f: A => Future[B]): Future[B] = a flatMap f } implicit val FutureFunctor = new Functor[Future] {   def map[A, B](a: Future[A], f: A => B): Future[B] = a map f } 
like image 44
oxbow_lakes Avatar answered Oct 11 '22 18:10

oxbow_lakes