TomasMikula/libretto

Reference implementation

TomasMikula opened this issue · 0 comments

The problem with the current Future-based implementation

Futures don't support removing listeners. This leads to a couple of problems.

No support for cancellation

There is no way to let a Future know that we are no longer interested in its result, not to mention to cascade that information to upstream Futures.

Resource leaks

Some recursive programs may lead to arbitrarily long chains of Promises.

To illustrate the problem, let's try to define our own flatMap method on Future:

def flatMap[A, B](fa: Future[A])(f: A => Future[B]): Future[B] = {
  val pb = Promise[B]()
  fa.onComplete {
    case Success(a) => pb.completeWith(f(a))
    case Failure(e) => pb.failure(e)
  }
  pb.future
}

Now imagine an infinite chain of such flatMaps, as in

def loop(): Future[Unit] = {
  // using *our* flatMap defined above
  flatMap(step()) { _ =>
    loop()
  }
}

def step(): Future[Unit] = ???

loop() will create an infinite chain of Promises, thus leaking resources.

This problem does not occur with Future's flatMap method, because it has a specialized implementation that avoids building up a chain of promises by re-linking the listeners. That's something not available to client code like the Libretto implementation.

Now, Libretto uses constructs with Promises like the one in the above implementation of flatMap, but not exactly flatMaps.

Suggestion for a correct reference implementation

Avoid Scala Futures altogether. Instead, introduce our own primitive that gives control over listeners. Also, it need not be as general purpose as Future, but can exploit Libretto specifics, such as having exactly one listener.