Doing for comprehensions right?

Hi all, so I hadn’t needed to do a lot of for comprehensions until I started using Kalix, and I have been doing calls to my entities in my controllers like this:

for {
r1 ← components.somecomponent.somemethod.execute()
r2 ← components.somecomponent.somemethod.execute()
_ ← components.somecomponent.somemethod.execute()
} yield (r1, r2)

but according to the SO post below that is not optimal and should be done like this?

future1 = components.somecomponent.somemethod.execute()
future2 = components.somecomponent.somemethod.execute()
future3 = components.somecomponent.somemethod.execute()

for {
r1 ← future1
r2 ← future2
_ ← future3
} yield (r1, r2)

I’m thinking that even though the comprehension does indeed call each component sequentially, the call returns immediately with the future, so ‘sequential’ here doesn’t really mean much. Is that correct? or should I change my approach (which is fine just a more verbose and unwieldy).

thanks,

-m

Hi @maristi,

It’s not true that the call returns immediately.
The second call is only executed once the first completes.

Consider the following case:

for {
r1 ← components.somecomponent.somemethod.execute()
r2 ← components.somecomponent.someOtherMethod(r1).execute()
} yield (r1, r2)

It’s possible to write the above code. That means that the second call can only be executed when the result r1 is ready. This is the same as calling:

components.somecomponent.somemethod.execute().flatMap { r1 =>
  components.somecomponent.someOtherMethod(r1).execute()
}

You execute somemethod and when its result is ready, the someOtherMethod is executed.

For comprehension is just syntax sugar for a sequence of flatMaps. So in the end, they are effectively chained.

If the different calls are not dependent on each other, it’s indeed better to call them independently and not use the for comprehension only to collect the results.

1 Like

@octonato might be good to update the documentation to reflect the more efficient pattern:

In that example there is an ordering requirement, even if the second call does not contain anything from the response of the first call, it is not possible to add something to a cart before it has been successfully created, so they need to be sequential.

If the two calls were in parallel the addItem call could end up happening before create even though they were in the right order in the code, so it is not only about efficiency but also about ordering guarantees.

We could maybe also cover a separate scenario in the docs where the ordering does not make sense and two tasks can be executed in parallel with a single composed outcome once both are done though.

Right. But yeah would be good to document this pattern and make it more obvious to us Kalix newbies. Thanks.