During work, I have some problem handling three future at the same time. First future return a long value and second and third future receive the parameter from the first return and return a tuple.

The code is like this.

for { long <- futureLong() int <- futureInt(long) char <- futureChar(long) } yield (int, char)

Of course for comprehension operates step by step, so, futureInt is called after futureLong is finished and futureChar is called after futureInt is finished. So, It takes too much time to get a final return value.

So, I changed codes like this.

val longVal: Future[Long] = futureLong() val intVal: Future[Int] = for { long <- futureLong() int <- futureInt(long) } yield int val charVal: Future[Char] = for { long <- futureLong() char <- futureChar(long) } yield char val forComp: (Int, Char) = Await.result(for { int <- intVal char <- charVal } yield (int, char), 5 second)

Yes, It works as I want. But, there is duplicated codes like, ‘long <- futureLong()’. During thinking a long time, my coworker, Liam, recommends using Cartesian Product.

### What is Cartesian Product?

Cartesian Product is very simple. It’s just a product of two Sets. For example, if there are two sets A = {1,2,3} and B = {‘a’, ‘b’, ‘c’}, Cartesian Product of A X B is

{(1, ‘a’), (1, ‘b’), (1, ‘c’), (2, ‘a’), (2, ‘b’), (2, ‘c’), (3, ‘a’), (3, ‘b’), (3, ‘c’)}

Yes. that’s all. Of course Cartesian Product didn’t hold commutative law. For example Cartesian Product of B X A is

{(‘a’, 1), (‘a’, 2), (‘a’, 3), (‘b’, 1), (‘b’, 2), (‘b’, 3), (‘c’, 1), (‘c’, 2), (‘c’, 3)}

And different from A X B.

### Cartesian Product in cats

Above example can be implemented in Scala.

import cats.instances.list._ import cats.syntax.all._ val ints: List[Int] = List(1, 2, 3, 4) val chars: List[Char] = List('a', 'b', 'c') (ints |@| chars).tupled // res0: List[(Int, Char)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c), (4,a), (4,b), (4,c)) (chars |@| ints).tupled // res1: List[(Char, Int)] = List((a,1), (a,2), (a,3), (a,4), (b,1), (b,2), (b,3), (b,4), (c,1), (c,2), (c,3), (c,4)) (ints |@| chars).map(_ + _.toString) // res2: List[String] = List(1a, 1b, 1c, 2a, 2b, 2c, 3a, 3b, 3c, 4a, 4b, 4c)

In Cats Cartesian Product Syntax is ‘|@|’.(Some people call it Oink, and others call Pizza box) If you call tuple, it will return List Tuple. You can also map it.

### Cartesian Product in Future

Ok, let’s return to the previous problem. I need to call futureInt and futureChar at a same time. And using three for comprehension is too dirty. So, how and I solve this with Cartesian Product? It’s very simple.

for { long <- futureLong() (int, char) <- (futureInt(long) |@| futureChar(long)).tupled } yield (int, char)

Very simple right? You can test whether this expression really operates at the same time.

In this test, the first test occurs TimeoutException and second and third pass test well. Then, we can make a conclusion that, last expression also run at the same time.

Nice post. Thanks.

LikeLike