-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
io.vertx.core.Future does not use variance in its declarations preventing reuse or forcing to introduce adapter functions, e.g. public void flatMap(Future<String> fut, Function<CharSequence, Future<Integer>> fn) { fut.flatMap(fn::apply); // produce a new function adapting "fn", it could be "fn" instead } public void onComplete(Future<String> fut, Promise<CharSequence> promise) { // That fut.onComplete(event -> promise.handle(event.map(s -> s))); // we would like to use "promise" instead // Or fut.map(s -> (CharSequence)s).onComplete(promise); } Changes: Three changes are necessary to solve this problem at the expense of small breaking changes 1. Use variance on existing methods // After <U> flatMap(Function<T, Future<U>> fn); // After <U> flatMap(Function<? super T, Future<U>> fn); This changes remains source compatible (for user), there are breaking changes but those are for implementation of the `Future` type, which are acceptable. It does not apply to Handler<AsyncResult<T>> arguments. In practice Handler<AsyncResult<T>> is solvable but not realistic, it should be Handler<? extends AsyncResult<? super T>> but this one exhibit issues with lambdas, e.g. future.onComplete(ar -> ar.result() /* Object and not T */). 2. Overloading handler of async results methods Use a functionnal interface accepting two arguments so a lambda will get the value and the error instead of the combined async result, pretty much like CompletionStage#whenComplete(BiConsumer<? super T, ? super Throwable>). Future<T> onComplete(Handler<AsyncResult<T>> handler); Future<T> onComplete(Completable<? super T> handler); // Overload Likewise <U> Future<U> transform(Function<AsyncResult<T>, Future<U>> fn); <U> Future<U> transform(BiFunction<? super T, ? super Throwable, Future<U>> fn); // Overload with @FunctionalInterface public interface Completable<T> { default void succeed(T value) { complete(value, null); } default void fail(Throwable cause) { complete(null, cause); } void complete(T value, Throwable err); } There are a few breaking changes though, when null is an argument, the compiler cannot decide with overload to use. We consider those are marginal, we found some of these in the vertx test suite to check that null arguments produces errors, e.g. `future.onComplete(null)`. 3. Retrofit Promise as Completable instead of async result handler Since now we have variant part with a Completable, we need Promise to extend Completable instead of Handler<AsyncResult<T>> Promise<T> extends Completable<T> { ... } This creates breaking changes when Promise<T> was used at the place of Handler<AsyncResult<T>>, e.g. internally in vertx we still have a few of those, and promise::handle should be used to fix the issues. Since Vert.x 5 does not anymore exhibit Handler<AsyncResult<T>> methods, this should not be an issue for users. This will require adaptation in Vert.x code base because there are many remaining usage of `Handler<AsyncResult<T>>` idiom: here is a recap of the necessary changes https://gist.github.com/vietj/02ce9dc89c8a1c11dabe8828f760f973 . This list is actually quite long, however it mostly solves internal issues of the vertx codebase still using Vert.x 3 idioms and was never migrated to use futures, e.g. recent vertx components like the new _vertx-grpc_ or _vertx-service-resolver_ do not need any changes.
- Loading branch information
Showing
38 changed files
with
497 additions
and
608 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* Copyright (c) 2011-2024 Contributors to the Eclipse Foundation | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 | ||
* which is available at https://www.apache.org/licenses/LICENSE-2.0. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 | ||
*/ | ||
package io.vertx.core; | ||
|
||
import io.vertx.core.impl.NoStackTraceThrowable; | ||
|
||
/** | ||
* A view of something that can be completed with a success or failure. | ||
* | ||
* @author <a href="mailto:julien@julienviet.com">Julien Viet</a> | ||
*/ | ||
@FunctionalInterface | ||
public interface Completable<T> { | ||
|
||
/** | ||
* Set the result. The instance will be marked as succeeded and completed. | ||
* <p/> | ||
* | ||
* @param result the result | ||
* @throws IllegalStateException when this instance is already completed or failed | ||
*/ | ||
default void succeed(T result) { | ||
complete(result, null); | ||
} | ||
|
||
/** | ||
* Shortcut for {@code succeed(null)} | ||
* | ||
* @throws IllegalStateException when this instance is already completed or failed | ||
*/ | ||
default void succeed() { | ||
complete(null, null); | ||
} | ||
|
||
/** | ||
* Set the failure. This instance will be marked as failed and completed. | ||
* | ||
* @param failure the failure | ||
* @throws IllegalStateException when this instance is already completed or failed | ||
*/ | ||
default void fail(Throwable failure) { | ||
complete(null, failure); | ||
} | ||
|
||
/** | ||
* Calls {@link #fail(Throwable)} with the {@code message}. | ||
* | ||
* @param message the failure message | ||
* @throws IllegalStateException when this instance is already completed or failed | ||
*/ | ||
default void fail(String message) { | ||
complete(null, new NoStackTraceThrowable(message)); | ||
} | ||
|
||
/** | ||
* Complete this instance | ||
* | ||
* <ul> | ||
* <li>when {@code failure} is {@code null}, a success is signaled</li> | ||
* <li>otherwise a failure is signaled</li> | ||
* </ul> | ||
* | ||
* @param result the result | ||
* @param failure the failure | ||
* @throws IllegalStateException when this instance is already completed | ||
*/ | ||
void complete(T result, Throwable failure); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.