Hi Martin, *Unsurprisingly*, I think it is a bad idea to pollute something which was created as a non-blocking superset intended to provide maximum utility with minimum of sharp edges.
However, I think you have a point in that toCompletableFuture throwing UOE is rather unhelpful, and if some people are huge fans of parking threads then let's see if we can come to terms on a solution which doesn't compromise the solution for everyone else. The subject of this thread, "We need to add blocking methods to CompletionStage!", gave me a few questions: "who is 'we'?", "when do we need blocking methods?" and "Why is CompletionStage the right place to add this?" I think it's great that you point out that Scala's Future (full disclosure: I am a co-designer of that) extends the Awaitable[1] trait (think interface). It is worth pointing out that those methods are not callable on the instance directly (notice the implicit evidence type) so all invocations need to go through an external construct, Await, which makes it abundantly clear that something different is going to happen. I wrote a long explanation of the design process (at least parts of it) here[2], but I'm including the relevant section on Await below: There’s an underlying question that I think is worth answering: “Why does Await.result exist, and why doesn’t it exist *on* Future?” When we designed Scala’s Future one thing we based it on was the experience with Akka Future ( http://doc.akka.io/api/akka/1.3.1/?_ga=1.21043707.1579561034.1353497989#akka.dispatch.Future ) Note how it had `get` and `await` methods, which do similar things as the C# `Result` method — block the currently executing Thread from progressing until the value is available. Such a method is the counterpart to asynchronous, it is synchronizing the current Thread with the Thread which executes the Future — i.e. an API for synchronous programming. Not only did we find that methods like that introduce performance problems due to blocking Threads (delay until the value is available but also due to Thread scheduler wakeup lag), but these methods also produce programs that are difficult to reason about since they can deadlock and become non-deterministic in their runtime behavior as things like GC-pauses etc may cause either spurious failures (where timeouts are supplied) or prolonged resource starvation due to unavailability of Threads to execute other logic. Having a single method for performing these potentially dangerous operations and putting it outside of the Future API itself meant that it is easy to spot where blocking is performed as well as easy to outlaw it (by disallowing it to be present in source code). But we also took it a step further, by creating the BlockContext mechanism we also made it possible to the runtime to perform evasive manoeuvres in the presence of blocking. ( http://www.scala-lang.org/api/current/index.html#scala.concurrent.BlockContext ) *Option 1:* Now, if Java's type system was just a tad more powerful, it would support intersection types and APIs expressing the problem of wanting to expose more than CompletionStage but less than the concrete CompletableFuture type could very easily return `*CompletionStage[T] with Future[T]*`. Now we don't really have that, so it needs to be poorly emulated using compound interfaces, becoming something like `*FutureCompletionStage[T]*` which would have the `join`-method on Future. And that may actually not be a bad idea at all. So let's call that *Option 1.* *Option 2:* Add a `toFuture` method to CompletionStage such that if you want to go and use the blocking methods of Future, it is both shorter and does not expose a concrete type in the signature. *Futher proposals:* * Adding a constructor to `CompletableFuture` which takes a `CompletionStage` as parameter would make the boilerplate discussed earlier[3] obsolete. * I'd like to see if we could make the `toCompletableFuture` be a default method which delegates to `new CompletableFuture<>(this)`, meaning that the UOE problem would sort of solvable. *Summary:* * I think there is already a great place to host blocking methods: j.u.c.Future * We can get around the UOE by introducing the constructor on CompletableFuture both as a fallback to things which do throw UOE on toCompletableFuture, but also provides a terrific default implementation for toCompletableFuture. * We can introduce a toFuture-method with a default implementation which calls `new CompletableFuture<>(this)` Have a great weekend! *Footnotes:* 1: http://www.scala-lang.org/files/archive/api/2.11.8/index.html#scala.concurrent.Awaitable 2: https://medium.com/@viktorklang/hi-eef4acf316a8#.uesy1fqgo 3: static <T> CompletableFuture<T> toCompletableFuture(CompletionStage<T> stage) { … } PS. As a sidenote, Martin, and in all friendliness, "actor purist API"? C'mon, I know you're better than that! CompletionStage's design has nothing to do with Actors and if Single Responsibility Principle is considered purism then I'm not sure why we don't have a single interface with all methods in it. Let's try to keep things friendly. PPS: A misunderstanding is that CompletionStage represents a running task of sorts, one that can be cancelled etc. This is not the case. PPPS: Adding blocking methods without mandatory timeouts has in practice proven to be a recipe for disaster. PPPPS: "I think it's unreasonable to not provide this for users (especially when we can do so more efficiently)." <- If efficiency is desired then blocking is definitely not the right solution. PPPPPS: I'm currently moving cross-country so there will be delays of any responses from me On Wed, Sep 21, 2016 at 10:43 PM, Martin Buchholz <marti...@google.com> wrote: > (Sorry to re-open this discussion) > > The separation of a read-only CompletionStage from CompletableFuture is > great. I'm a fan of the scala style Promise/Future split as described in > http://docs.scala-lang.org/overviews/core/futures.html, but: we need to > re-add (safe, read-only) blocking methods like join. Java is not Node.js, > where there are no threads but there is a universal event loop. Java > programmers are used to Future, where the *only* way to use a future's > value is to block waiting for it. The existing CompletionStage methods are > a better scaling alternative to blocking all the time, but blocking is > almost always eventually necessary in Java. For example, junit test > methods that start any asynchronous computation need to block until the > computation is done, before returning. > > As Viktor has pointed out, users can always implement blocking themselves > by writing > > static <T> CompletableFuture<T> toCompletableFuture(CompletionStage<T> > stage) { > CompletableFuture<T> f = new CompletableFuture<>(); > stage.handle((T t, Throwable ex) -> { > if (ex != null) f.completeExceptionally(ex); > else f.complete(t); > return null; > }); > return f; > } > > static <T> T join(CompletionStage<T> stage) { > return toCompletableFuture(stage).join(); > } > > but unlike Viktor, I think it's unreasonable to not provide this for users > (especially when we can do so more efficiently). What is happening instead > is API providers not using CompletionStage as return values in public APIs > because of the lack of convenient blocking, and instead returning > CompletableFuture, which is a tragic software engineering failure. > > Re-adding join is easy. We discourage CompletionStage.toCompletableFuture > from throwing UnsupportedOperationException, and implement join as: > > public default T join() { return toCompletableFuture().join(); } > > There is a risk of multiple-inheritance conflict with Future if we add > e.g. isDone(), but there are no current plans to turn those Future methods > into default methods, and even if we did in some future release, it would > be only a source, not binary incompatibility, so far less serious. > -- Cheers, √