Abhay Kumar
Abhay Kumar

Reputation: 550

How reduceLeft works on sequence of Functions returning Future

This might be a naive question and I am sorry for that. I am studying Scala Futures and stumbled on below code:

object Main extends App {
    def await[T](f: Future[T]) = Await.result(f, 10.seconds)

    def f(n: Int): Future[Int] = Future {n + 1}
    def g(n: Int): Future[Int] = Future {n * 2}
    def h(n: Int): Future[Int] = Future {n - 1}

    def doAllInOrder[T](f: (T => Future[T])*): T => Future[T] = {
      f.reduceLeft((a,b) => x => a(x).flatMap(y => b(y)))
    }

    println(await(doAllInOrder(f, g, h)(10))) // 21
  }

I know how reduceLeft works when it is applied on Collections. But in the above example, as I understand, in the first pass of reduceLeft value x i.e. 10 is applied to Function a and the result is applied to Function b using flatMap which eventually return Future[Int] (say, I call it result). In the next pass of reduceLeft the result and Function h has to be used, but here is I am troubled.

The result is actually an already executed Future, but the reduceLeft next pass expects a Function which returns a Future[Int].Then how it is working?

Another thing I am not able to understand how each pass sending its result to next pass of reduceLeft, i.e. how x is getting it's value in subsequent passes.

Though both of my confusions are interrelated and a good explanation may help clear my doubt.

Thanks in advance.

Upvotes: 1

Views: 107

Answers (1)

Ivan Stanislavciuc
Ivan Stanislavciuc

Reputation: 7275

You have to think about reduceLeft to be independent from Future execution. reduceLeft creates a new function by combining two given ones and that's it.

reduceLeft is applied to a Seq of T => Future[T]. So, it's just simple iteration from left to right over sequence of functions taking first and second elements and reducing it to one single value, reducing this value with the 3rd element and so on. Eventually, having just two element left that are reduced to a single one.

The result of reduceLeft has to be of the same type as the type of elements in the collection. In your case, it's function T => Future[T].

Let's understand what (a,b) => x => a(x).flatMap(y => b(y)) is doing

This means the following. Given functions a and b, create a function that combines function a with b. Mathematically it's c(x)=b(a(x)).

Now, a and b are functions returning futures. And futures can be chained with the help of map/flatMap methods.

You should read x => a(x).flatMap(y => b(y)) as

Given an input x, apply function a(x), this results in a Future, when this future is completed, take result y and apply function b(y), this results in a new Future. This is the result of function c.

Note: value x is Int at all times. It is the input parameter for your new reduced function.

If it's still not clear, let's address the points of confusions

The result is actually an already executed Future.

No future is guaranteed to be executed at any point here. map and flatMap are non blocking operation and it applies functions to a Future. The result of this application is still a Future.

Another thing I am not able to understand how each pass sending its result to next pass of reduceLeft, i.e. how x is getting it's value in subsequent passes.

This is easier to understand when just having integers in a collection. Given following code

Seq(1, 2, 5, 10).reduceLeft(_ - _)

It will take 1, 2 and apply - function, this will result in -1. It will then combine -1 and 5 resulting in -6. And finally, -6 with 10 resulting in -16.

Upvotes: 1

Related Questions