Reputation: 550
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
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 functiona(x)
, this results in aFuture
, when this future is completed, take resulty
and apply functionb(y)
, this results in a newFuture
. This is the result of functionc
.
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