#1299 Nested Futures

vkuzkokov Mon 8 Nov 2010

I dug a little bit into concurrency in functional languages. Apparently, almost everything is here in Fantom to use FP-like concurrency. The only thing missing to achieve this is a method to unwrap nested Futures. That's how it should work:

using concurrent
const class Worker : Actor {
  new make(ActorPool pool) : super(pool) {}
  override Obj? receive(Obj? o) {
    Func#call.callList(o)
  }
  private static Obj? doubleGet(Future f) {
    ((Future)f.get).get
  }
  Future join(Future f) {
    sendWhenDone(f,[#doubleGet.func,f].toImmutable)
  }
}

The meaning is: Future of Future is still just Future. The bad thing is that second get in doubleGet is blocking (on Actor's thread).

Does anyone has a use-case for that feature or its value is purely scientific?

JohnDG Tue 9 Nov 2010

When using Future as a monad, it's quite common to deal with "nested futures", to avoid them "bind" or "flatMap is used.

For example, if you're doing an HTTP get from one resource, which you'll use to do additional HTTP gets, then unless you flatten the result you could end up with a future of future of the result.

Instead of using blocking techniques like the one you propose, I recommend bind/flatMap and map be added to Future.

vkuzkokov Tue 9 Nov 2010

What I meant to propose to make this work in a non-blocking way. I'm not sure what flatMap is but bind is definitely preferable.

Semantics of bind in their "sequential form" (or at least how I got them) would be:

Sometimes, instead of returning an object Actor might decide to postpone or to delegate to another actor. And all that transparently for a client (holder of a Future instance).

In Actor it can be done with:

virtual Future receiveOrPostpone(Obj? o) {
  Future.make(receive(o)) //will create Future as a simple wrapper
}

So, more sophisticated Actors would be allowed to postpone execution (by returning Future of postponed operation) without blocking thread or to end execution normally (with use of Future.make).

qualidafial Wed 10 Nov 2010

Couldn't the Future returned from Actor.sendWhenDone be considered a nested Future? e.g.

futureA := actorA.send(message)
futureB := actorB.sendWhenDone(futureA)
futureC := actorC.sendWhenDone(futureB)
result := futureC.get

The benefit to doing it this way is that your actors don't have to know about each other. actorA has a discrete task, as do actorB and actorC. It's some other class's job to string them together into a pipeline. This is nice because now actors a, b and c can be easily re-used.

Contrast this with "nested" futures, where each actor has to know which actor is next in the pipeline, resulting in a highly coupled design and parts that are difficult to re-use.

vkuzkokov Wed 10 Nov 2010

@qualidafial

With sendWhenDone you create a pipeline where steps don't know about each other. With "nested futures" it's the other way around.

  1. The client is not necessarily aware that the pipelineing takes place at all (kind of encapsulation).
  2. We define what's the next step after the previous one is completed (so the pipeline is more flexible).

Login or Signup to reply.